码迷,mamicode.com
首页 > 其他好文 > 详细

ListView优化 思路及原理

时间:2016-03-15 16:05:43      阅读:354      评论:0      收藏:0      [点我收藏+]

标签:


最佳实践

1、将【内部类】用 static修饰一下。public static class ViewHolder { }

2、将实例mViewHolder作为外部类的成员变量。private ViewHolder mViewHolder;


注解:
  • ViewHolder是否用static修饰,结果都是一样的,效率也没什么提升,不过大家都习惯用 static修饰一下。
  • 一种情况下可以将mViewHolder作为局部变量,那就是想【临时】操作屏幕上item中的控件,这时屏幕上的mViewHolder和convertView是一一对应的,可以方便的通过mViewHolder获取你所操作的convertView中的所有控件,进而进行各种操作。但是一定要谨记,这种操作是临时的,下次进入界面时又会恢复原来的状态。要实现永久存储,只有一个办法,那就是将你所操作的结果保存在listview的adapter中的集合中,既然是操作集合,那肯定是通过point获取、操作控件了。
  • 所以,根据需求,若是想临时修改控件属性,就将mViewHolder作为局部变量,若是想持久修改控件属性,就采取将状态保存在集合中的方式。


View重用的原理探讨

1. View的重用的原理

  • View的每次创建是比较耗时的,因此对于 getview 方法传入的 convertView 应充分利用 != null 的判断。
  • 如果你有N个 item,其中只有可见的 item +1 个(即 item1 - item8 )存在内存中,其他的在 Recycler(循环器)中。
  • ListView先一个一个的调用 getView 创建所有可见的 item 的视图,此时getView中的参数 convertView 是空 null 的。
  • 当 item1 滚出屏幕,并且一个新的 item8 从屏幕底端滚上来时(只要有一点点露头就算滚上来了),ListView再调用 getView 请求一个 item9 的视图时,convertView 不是空值了,它的值就是刚刚滚出去的 item1。
  • 所以这时你只需将 item9 的【数据】赋给 convertView 然后返回 convertView 即可得到 item9 的视图,而不必重新 new 一个视图。
2、ViewHolder的应用
  • View的 findViewById 方法也是比较耗时的,因此需要考虑只调用一次,之后就用 View.getTag() 方法来获得ViewHolder对象。我们知道,每个item都有同样的【视图结构】,item之间仅仅是所填充的数据不同,若每次都为得到一个相同结构的视图都要重新 inflate 布局文件,然后 findViewById 布局中的控件,这是很浪费资源的,为了不重复 findViewById,我们可以定义一个 ViewHolder 用于封装所有需要更新数据的控件,那么以后就可以直接赋值。
  • ViewHolder只是将需要缓存的那些view封装好,convertView的setTag才是将这些缓存起来供下次调用。View中的 setTag 方法表示给View添加一个额外的数据,以后可以用getTag()将这个数据取出来。
技术分享


代码
现在大家都知道用ViewHolder来实现listview的优化了,但是,ViewHolder到底要用什么来修饰,ViewHolder的实例到底应该放在哪呢?为了弄清楚这个问题,我做了以下测试。
我简单说一下代码是什么意思,ViewHolder有一个成员变量id,用来区分不同的ViewHolder,在构造函数中,对id进行赋值,itemId是一个静态变量,每初始化一次就+1,我们可以根据构造函数的打印次数,来计算ViewHolder的实例化次数,根据toString()可以来判断到底是使用了哪一个ViewHolder。getVIew中的写法是固定的,下面是测试结果:

public class MainActivity extends ListActivity {

    //ViewHolder mViewHolder;
    //作为成员变量,首先调用10次 getView(),并且每调用一次,就对【此实例】重新初始化一次(new)
    //虽然new了10次,但至始自终都是指向的是同一个地址,并且这个ViewHolder 始终只代表【最后出现】的那个条目
    public static int itemId = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getListView().setAdapter(new MyAdapter());
    }
    private class MyAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return 100;
        }
        @Override
        public Object getItem(int position) {
            return position;
        }
        @Override
        public long getItemId(int position) {
            return position;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final ViewHolder mViewHolder; //或者ViewHolder mViewHolder;
            //若作为局部变量,初始化时会先调用10次 getView(),每调用一次,就 会new一个新的 ViewHolder
            //所以共有10个 ViewHolder 实例,每个 ViewHolder 实例代表一个【当前屏幕】上的一个条目
            //注意:final修饰引用类型变量时,只是保证这个引用的地址不变,即一直引用同一对象,但这个对象自身是可以改变的
            if (convertView == null) {
                convertView = getLayoutInflater().inflate(R.layout.item, parent, false);
                mViewHolder = new ViewHolder();
                mViewHolder.tv = (TextView) convertView.findViewById(R.id.tv);
                convertView.setTag(mViewHolder);//标记
            } else mViewHolder = (ViewHolder) convertView.getTag();
            mViewHolder.tv.setText("listview条目的序号=" + position + ",mViewHolder的id=" + mViewHolder.id);
            //不管采用的是哪种方法,在次操作item中的控件都没有任何问题!千万别忧虑!
            Log.d("bqt""position=" + position + "    " + mViewHolder.id + "    " + mViewHolder.hashCode());
            return convertView;
        }
    }
    private class ViewHolder {//ViewHolder用private、private static、private final或private static final修饰时完全一样!
        //结论:如果界面内可见的item是9个,那么不管哪种方式,自始至终ViewHolder一共初始化了9+1次
        //之后都是复用之前初始化的ViewHolder,但并不能保证哪个item复用的是哪个ViewHolder的实例。
        private int id;//内部类的任何成员都是完全暴露给外部类的
        private TextView tv;
        private ViewHolder() {
            id = itemId;
            itemId++;
        }
    }
}


item的布局
item.xml

<?xml version="1.0" encoding="utf-8"?>

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/tv"
          android:layout_width="match_parent"
          android:layout_height="62dp"
          android:gravity="center_vertical"
          android:text="listview的每个条目" />


实验一,如何修饰内部类ViewHolder
结论1:如果界面内可见的item数量是9个,那么自始至终ViewHolder一共初始化了9+1次,之后都是复用ViewHolder,但并不保证哪个item复用的是哪个ViewHolder的实例

结论2:内部类 ViewHolder 不管是用 private 修饰,还是用 private static 修饰,还是用 private final 修饰,还是用 private static final class ViewHolder修饰,完全一样!

static修饰类,在这里是静态内部类,并不是说只存在一个实例,而是可以访问外部类的静态变量,final修饰类则是不让该类继承,我们这里使用final毫无根据,所以,以后写ViewHolder的时候,可以不纠结了,加什么加啊,什么都不用加!

 技术分享   技术分享   技术分享  技术分享  技术分享


实验二,ViewHolder的实例放在哪里
结论:

        mViewHolder无论是作为activity的成员变量,还是作为getView()的局部变量,还是作为getView()的 final 局部变量,当然mViewHolder不能作为final的成员变量,上面的结论依然成立。

个人理解的区别:

  • 1、作为成员变量,首先调用10次 getView(),那么每调用一次,就初始化一次,虽然 new 了10次,但至始自终只有一个 ViewHolder 实例,并且这个ViewHolder 始终只代表最后出现的那个条目
  • 2、作为局部变量,首先调用10次 getView(),很明显,每调用一次,就 new了一个新的 ViewHolder,所以共有10个 ViewHolder 实例,每个 ViewHolder 实例代表一个当前屏幕上的一个条目
  • 3、所以,主要区别在于,作为成员变量,只有一个对象,始终代表最后出现的item;作为局部变量,共有10个对象,代表屏幕上的每个item。






ListView优化 思路及原理

标签:

原文地址:http://www.cnblogs.com/baiqiantao/p/5279832.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!