ListView和其convertView回收机制

ListView和其convertView回收机制


前言

ListView是在Android应用比较广泛的一种控件,允许用户通过触屏上下滑动的方式将屏幕外的数据移动到屏幕内,同时将屏幕上原有的数据移动到屏幕外,我们其实经常用到这个控件,例如查看QQ、微信消息。
ListView的简单结果如下图所示。

ListView与Adapter的关系

ListView中的每一个Item子项都可以看成一个个小视图,视图是由布局和布局中的内容构成的。举个例子,对于QQ的消息列表,每一个消息就是一个视图,视图中包含两个部分,一个是头像,一个是消息摘要,一般头像都是在每条消息的最左边,为什么他是在最左边而不是最右边呢?这是由于视图中动态加载的布局决定的,布局决定了内容填充的位置。
对于ListView来说,他的布局和对应数据加载都是由Adapter完成的,Adapter就是我们所说的适配器,适配器的种类有很多种,以下都是基于ArrayAdapter适配器举例,来看一段代码

ArrayAdapter<String> ap = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,data);

ArrayAdapter适配器有三个参数,分别是上下文、需要加载的布局、需要加载的数据,当我们传入对应的参数,剩下的事就由适配器自己完成,适配器是如何完成的呢,请继续往下看。

Adapter中的getView()

对于ListView来说,每当一个Item子项被滚动到屏幕内时,都会调用Adapter中的getView()函数创建新的视图,getView()函数完成两个核心工作,一个是加载布局,一个是往布局上丢需要显示的数据,我们来看看getView()的函数原型

public View getView(int position, View convertView,  ViewGroup parent) {

}

可以很直观看到传入了三个参数,我们这里主要谈的有两个,一个是position,用于记录当前Item的位置,另一个是convertView,指向当前看不到的已经被缓存的视图。
简单的应用getView()来创建视图的代码如下

public View getView(int position, View convertView,  ViewGroup parent) {
    Data data = getItem(position);
    View view = LayoutInflater.from(getContext()).inflate(resourceID,parent,false);
    TextView textView = (TextView) view.findViewById(R.id.textview);
    textView.setText(data.getData());
}

通过getItem()函数获取当前position位置需要显示的数据,同时通过inflate()函数创建当前Item的view,最后在textView控件(可以是用于显示数据的任意控件,注意下findViewById()函数前需要指明当前的view)中放入需要显示的数据,getView()的使命结束了,此时创建的Item在屏幕中展示出来了。

convertView和Recycler构件

首先提下Android中的Recycler构件(反复循环器),以下是他的工作原理

从原理图中可以看出,假设当前屏幕只能容纳7个Item,ListView在初始化的时候,只会创建7个view,这些view也就是我们所说的convertView,此时Recycler控件是没有东西的。
当随着屏幕滑动,Item1滚动到屏幕外的时候,此时Item2占据着屏幕头部位置,空出来的一个位置由新创建的Item8的来补充,那么Item1暂时不用了,会被销毁吗?显然Android设计者进行了精巧的构思,暂时用不到的Item不是被销毁了,而是被回收了(包括布局和数据),存放在Recycler中,在内存中只留下可见的Item。那么这样做的好处是什么?
假设有100W个Item需要浏览,如果没有这种回收机制,那么每次滑到到新的Item,就会触发getView()函数进行创建Item,那么从头到尾浏览一次列表会触发100W次创建任务,再从尾到头又会触发100W次创建,我们浏览一圈总共触发了200W次创建任务,然而创建任务消耗的内存和计算资源是很大的,几次可能体现不出来,当次数大后这种效率问题就暴露的很明显。
引入了回收机制后,当Item1被划出屏幕,暂时用不到的时候,会进入到Recycler中,此时的convertView不再为空,而是指向了Item1,我们只需要对getView()中的逻辑代码进行一些修改,就可以充分利用convertView来减少创建次数,请看如下代码

public View getView(int position, View convertView,  ViewGroup parent) {
    View view;
    Data data = getItem(position);
    if(convertView == null) {
        view = LayoutInflater.from(getContext()).inflate(resourceID,parent,false);
    }
    else {
        view = convertView;
    }
    TextView textView = (TextView) view.findViewById(R.id.textview);
    textView.setText(data.getData());
}

当convertView为空的时候,表示此时没有被回收的Item,换句话说就是屏幕还没有被Item覆盖满,可以进行正常的创建任务(屏幕一次性能显示的Item不多),当convertView不为空的时候,说明已经有被回收的Item了,以上诉的原理图为例,当划动到Item8的时候,Item1被回收到Recycler中,当继续滑到Item9进入屏幕的时候,此时并不会重新创建一个新的view,而是将原来Item1的view空间给Item9使用,并且Item2被回收到Recycler中,然后通过对数据的更新显示,达到一种创建新的view效果。
通过上诉分析大家大致也可以知道,充分利用convertView对于Item量大的时候,效率可以提高好几个数量级,假设当前屏幕需要可以显示的Item最大为n,显然利用了convertView最多只会创建n+1个view,在本例中如果需要浏览100W个Item,最多也就创建了8个view,相比之下节约的成本大家心目了然。

对于以上的验证代码如下

public View getView(int position, View convertView,  ViewGroup parent) {
    View view;
    TEST test = new TEST();
    if(convertView == null) {
        view = LayoutInflater.from(getContext()).inflate(resourceID,parent,false); 
        test.position = position;
        view.setTag(test); 

        Log.d("str","converView is  null " + test.position);
    }
    else {
        view = convertView; 
        test = (TEST) view.getTag();

        Log.d("str", "converView is not null " + test.position);
    }


    return  view;
}

class TEST {
    int position;
}

调试结果如下

刚开始浏览的时候

往下滑

再往上滑

参考链接:
http://blog.csdn.net/harvic880925/article/details/25335957
http://www.cnblogs.com/xiaowenji/archive/2010/12/08/1900579.html
http://blog.csdn.net/u011494050/article/details/25483329
http://blog.csdn.net/xsf50717/article/details/49206385

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值