ListView性能优化

ListView是一个常用到的控件,常常配合各种adapter一起用于显示列表内容。
关于列表显示:
(1)Listview:用来展示列表的view。
(2)adpater:适配器,是连接列表与待显示数据的桥梁,为listview提供数据源。

其中一个比较常用的方式是继承自BaseAdapter,灵活的定制自己的adapter。

/**
 * Created by Administrator on 2016/10/6.
 */

public class MyAdapter extends BaseAdapter{
    @Override
    public int getCount() {
        return 0;
    }

    @Override
    public Object getItem(int i) {
        return null;
    }

    @Override
    public long getItemId(int i) {
        return 0;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        return null;
    }
}

如上,对于一个listview,如何显示呢?
(1)需要知道有多少行待显示 getCount();
(2)每一行要显示的view getView();

因而,需要实现上面的四种抽象方法。
一种常见的listview性能优化的方法是covertView+ViewHolder。

public class MyAdapter extends BaseAdapter{

    String TAG = "MyAdapter";

    class ViewHolder{
        ImageView pic;
        TextView nameTV;

    }

    private List dataList = null;
    private Context context;

    public MyAdapter(Context context, List dataList) {
        this.context = context;
        this.dataList = dataList;
    }

    public MyAdapter(List dataList) {
        this.dataList = dataList;
    }

    @Override
    public int getCount() {
        Log.d(TAG,"getCount()");
        return dataList.size();
    }

    @Override
    public Object getItem(int i) {
        Log.d(TAG,"getItem()");
        return dataList.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {


        ViewHolder holder;

        if (view == null){

            holder = new ViewHolder();

            view = LayoutInflater.from(context).inflate(R.layout.listview_item,null);
            holder.pic = (ImageView) view.findViewById(R.id.list_item_img);
            holder.nameTV = (TextView)view.findViewById(R.id.list_tv);
            //关联View和ViewHolder
            view.setTag(holder);

            Log.d(TAG,"position = "+i+"  getView():创建新的view"+"  "+view.hashCode());
        }else {

            holder = (ViewHolder) view.getTag();

            Log.d(TAG,"position = "+i+"  getView():复用view"+"  "+view.hashCode());
        }

        MyListItem myListItem = (MyListItem) dataList.get(i);

        holder.pic.setImageDrawable(ContextCompat.getDrawable(context,myListItem.picId));
        holder.nameTV.setText(myListItem.name);

        return view;
    }
}

如上,是实现adapter的一种优化方法,重点在于getView方法。一般,对于初学者常常有两个疑问:
(1)getView方法中参数View是什么?
(2)为何要用ViewHolder?

对于第一个问题:
首先,我们知道getView方法用来为每一个item行提供view对象,可以试想,如果我们有成千上万个item待显示,如果我们对每一个item都使用new来新建一个view对象,必定会占用很大的资源,很容易OOM崩溃。因而Android中提供了一个叫做反复循环器的构件(Recycler),用于对滑出显示界面的view实现复用,从而不用new出那么多的view对象。关于Recycler原理可参见http://blog.csdn.net/bill_ming/article/details/8817172
而getView方法的view参数就是Recycler中滑出显示界面外的view,最其内的子view重新赋值可以实现复用。可以这样理解:假设屏幕最多能显示10个item,则对于position = 0-9的item进行绘制调用getView时,getView方法的view参数为null,这时需要inflate来new一个view。而当listview上滑,显示position = 10、11、…时,getView的view参数不等于null,此时是复用的滑出屏幕外的view,关于这一点之后可以用代码验证。

对于第二个问题:
我们很容易想到,实现view复用,不用viewHolder也是可以实现的,大概如下:

 @Override
    public View getView(int i, View view, ViewGroup viewGroup) {


        if (view == null){

            view = LayoutInflater.from(context).inflate(R.layout.listview_item,null);
        }
        //为对应子view填充内容
        ((TextView)view.findViewById(R.id.list_tv)).setText("text");

        return view;
    }

这样做似乎也可以,但可以发现,不管view是否复用,为子view填充内容的时候都要进行findViewById操作,而这一操作是极为耗性能的操作。因而,就有了ViewHolder,使用ViewHolder模式来避免没有必要的调用findViewById(),关于ViewHolder的作用,官方给的说明是:ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById()。

综上:
(1)covertView(就是getCount参数中的view)可以实现view复用。
(2)ViewHolder可以减少findViewById的次数,提高性能。
此外,关于ViewHolder是否定义为static还待讨论:
若为static,容易引起内存泄漏问题。优点则是ViewHolder的使用不依赖于外部类的创建。

这里写图片描述
这里写图片描述
代码验证:
如上,我的listview最多能显示11项,当position=0-10,都是创建新的view。滑动listview,显示position 为11、12…都是复用view,注意观察复用view后面打印的对应view的hasCode,即可知道复用的是滑出屏幕外的view。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值