ListView工作原理以及优化

ListView的工作原理

ListView 针对每个item,要求 adapter “返回一个视图” (getView),也就是说ListView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后根据这个长度,调用getView()一行一行的绘制ListView的每一项。如果你的getCount()返回几则显示几行。如果我们有几千几万甚至更多的item要显示怎么办?为每个Item创建一个新的View?不可能!!!其实Android中有个叫做Recycler的构件已经缓存了这些视图,大家可以看下下面这个截图来理解下。


下面简单说下上图的原理:

  • 1、如果你有几千几万甚至更多的选项(item)时,其中只有可见的项目存在内存(内存内存哦,说的优化就是说在内存中的优化!!!)中,其他的在Recycler中
  • 2、ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的
  • 3、当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图

Listview 异步加载图片错位问题

Listview 异步加载图片之所以错位的根本原因是:重用了convertView有异步操作如果不重用 convertView不会出现错位现象,重用convertView但没有异步操作也不会有问题。 我简单分析一下:

接着上图分析:最初一屏显示 7 条记录,getView 被调用 7 次,创建了 7 个 convertView.当 Item1 划出屏幕,Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例,Item8 复用的是Item1 的 view 如果没有异步不会有任何问题,虽然 Item8 和 Item1 指向的是同一个 view,但滑到Item8 时刷上了 Item8 的数据,这时 Item1 的数据和 Item8 是一样的,因为它们指向的是同一块内存,但 Item1 已滚出了屏幕你看不见。当 Item1 再次可见时这块 view 又涮上了 Item1 的数据。

但当有异步下载时就有问题了,假设 Item1 的图片下载的比较慢,Item8 的图片下载的比较快,你滚上去使 Item8 可见,这时 Item8 先显示它自己下载的图片没错,但等到 Item1 的图片也下载完时你发现Item8 的图片也变成了 Item1 的图片,因为它们复用的是同一个 view。 如果 Item1 的图片下载的比Item8 的图片快, Item1 先刷上自己下载的图片,这时你滑下去,Item8 的图片还没下载完, Item8会先显示 Item1 的图片,因为它们是同一快内存,当 Item8 自己的图片下载完后 Item8 的图片又刷成了自己的,你再滑上去使 Item1 可见, Item1 的图片也会和 Item8 的图片是一样的,因为它们指向的是同一块内存。

最简单的解决方法就是网上说的,给 ImageView 设置一个 tag, 并预设一个图片。

  • 在getView()中:

            // 给 ImageView 设置一个 tag
    	holder.img.setTag(imgUrl);
    	// 预设一个图片
    	holder.img.setImageResource(R.drawable.ic_launcher);

  • 在异步操作中:

            //通过 tag 来防止图片错位
    	if (imageView.getTag() != null && imageView.getTag().equals(oldImageUrl)) {
    	    imageView.setImageBitmap(result);
    	}

前面说了,由于ListView中的View(这里View中的控件暂定为:ImageView)都是重用的,移出屏幕的view控件很快会被进入屏幕的图片重新利用起来,那么getView()方法就会再次得到执行,而在getView()方法中会为这个View的ImageView控件设置新的Tag,这样老的Tag就会被覆盖掉,于是这时再调用imageView.getTag()方法,就只能得到新的tag了,而我们判断只有imageView.getTag() != null && imageView.getTag().equals(oldImageUrl)的时候才会设置图片,这样图片乱序的问题也就不存在了。

也就是说当 Item1 比 Item8 图片下载的快时, 你滚下去使 Item8 可见,这时 ImageView 的 tag 被设成了Item8 的 URL, 当 Item1 下载完时,由于 Item1 不可见现在的 tag 是 Item8 的 URL,所以不满足条件,虽然下载下来了但不会设置到 ImageView 上, tag 标识的永远是可见 view 中图片的 URL。

Listview优化

  • 1、优化一:复用convertView
  • 2、优化二:缓存item条目的引用——ViewHolder

    在创建view对象的时候,减少布局文件转化成view对象的次数;即在创建view对象的时候,把所有孩子全部找到,并把孩子的引用给存起来

    findViewById()这个方法是比较耗性能的操作,因为这个方法要找到指定的布局文件,进行不断地解析每个节点:从最顶端的节点进行一层一层的解析查询,找到后在一层一层的返回,如果在左边没找到,就会接着解析右边,并进行相应的查询,直到找到位置。因此可以对findViewById进行优化处理,需要注意的是: 特点:xml文件被解析的时候,只要被创建出来了,其孩子的id就不会改变了。根据这个特点,可以将孩子id存入到指定的集合中,每次就可以直接取出集合中对应的元素就可以了。

    • ①定义存储控件引用的类ViewHolder

      class ViewHolder{ 
      //定义item中相应的控件 
      } 

      这里的ViewHolder类需要不需要定义成static,根据实际情况而定,如果item不是很多的话,可以使用,这样在初始化的时候,只加载一次,可以稍微得到一些优化 不过,如果item过多的话,建议不要使用。因为static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(比如Context的情况最多),这时就要尽量避免使用了。

    • ②创建自定义的类:ViewHolder holder = null

    • ③将子view添加到holder中;

      在创建新的listView的时候,创建新的ViewHolder,把所有孩子全部找到,并把孩子的引用给存起来 通过view.setTag(holder)将引用设置到view中 通过holder,将孩子view设置到此holder中,从而减少以后查询的次数。

    • ④在复用listView中的条目的时候,通过view.getTag(),将view对象转化为holder,即转化成相应的引用,方便在下次使用的时候存入集合。 通过view.getTag(holder)获取引用(需要强转)

      class ViewHolder{
             ImageView img;
             TextView price;
         }
        public View getView(int position, View convertView, ViewGroup parent) {
             ViewHolder holder = new ViewHolder();
             if(convertView==null){
                 convertView = inflater.inflate(R.layout.good_list_item, null, false);
                 holder.img = (ImageView) convertView.findViewById(R.id.img);
                 holder.price = (TextView) convertView.findViewById(R.id.price);
                 convertView.setTag(holder);  
            }else{
                holder = (ViewHolder) convertView.getTag();
            }
            //设置holder
            holder.img.setImageResource(R.drawable.ic_launcher);
            holder.price.setText("$"+list.get(position).price);
      
            return convertView;
        }



内容摘自:
http://www.cnblogs.com/lesliefang/p/3619223.html
http://blog.csdn.net/guolin_blog/article/details/45586553
http://www.xuanyusong.com/archives/1252
http://blog.csdn.net/fenghai22/article/details/44173057
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值