ListView之Recycler机制

源地址


ListView之Recycler机制

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public View getView(int position, View convertView, ViewGroup parent)  

这个就是ListView中的getView()方法,参数convertView就是我们的头号嫌疑犯。假如我们有100条数据,但是页面上只能显示10条。Android还不会白痴到全部先创建出来。在初始创建的时候,显示多少,convertView就创建了多少,当你滑动的时候,比如向上滑动一个Item,那么Item1就会隐藏,Item11会从下面出来,那么这时候,如果Android再去创建一个convertView,那它就要被人骂死了,因为它首先要回收Item1,再去创建Item11,有意思吗?
为了解决这样一个问题,Android在ListView中提供了Recycler机制,说白了就是创建了一个Item的缓冲池,当Item1消失后,它并没有被回收,而是进入了缓冲池,成了二手货,当Item11显示的时候,它会从缓冲池中取出Item1,把Item1的数据擦擦干净继续用,所以这个时候,Item11和Item1显示的内容会是相同的,因为它们在内存中的地址是一样的,本质上是一个Item,但这个时候,Item1已经看不见了,所以它显示成什么,跟Item11没一点关系。当它再显示的时候,重新再写上正确的数据就好了。
这里在网上盗了张图,帮助大家来理解这样一个捡二手货的概念:

当然,如果不是异步操作,屁事都没有,因为Android UI是单线程的,或者说你重用了convertView但每次都new一个,那也没事,当然你也不会这样做,太Low了。但是异步就不一样了,就拿我们这里下载图片来说,每个图片都有自己的脾气,大家下载的速度都是不一样的,当初始显示ListView时,Item1的图片下载太慢,当你滑上去,显示Item11了,Item11的图显示的飞起,马上就下好了,OK,Item11的图显示好了,可马上,Item1下的慢的图也下好了,它去找Item1,但是它不知道这个时候的Item1已经变成Item11了,它还去给人家设置图像,这不就乱了吗?
OK,知道了原因,我们怎么解决呢?现在的问题就是,下载的图像就好像一个冬眠的人,它睡了20年,起来发现,他大爷已经不是当年的那个大爷了,但它的记忆还停在20年前。所以,我们需要建立一个标志,来让冬眠的朋友回来看看清楚,现在的这个大爷是不是你以前的那个大爷。
在Android中,我们可以非常方便的使用tag来对View进行标识,Item1显示的时候,tag被设置为url1,然后它就去下载url1了,当滑动后,显示Item11了,这时候tag被修改为url11,下载url11的图了。而当url1的图片下载好了之后,回来只要看看tag是不是url1就知道大爷还是不是那个大爷了,如果不是,就不显示了,这样就可以避免错位的问题了。
当然,前面的例子中还有一个坑,我就不埋大家了,下载完毕后,通过handler将图片通知UI线程修改ImageView,但是这个时候,参数中的ImageView与这时候传递过来的图像可能并不是一一对应的关系,显示肯定不正确了,所以我们还需要让url和对应的ImageView进行下配对,最简单的方法就是使用一个对象来进行保存。

多线程异步下载正解

在进行初次尝试并分析了失败原因后,我们将上面两个坑填起来,修改后的方法如下:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Using Thread 
  3.  * @param imageView 
  4.  * @param url 
  5.  */  
  6. public void showImageByThread(final ImageView imageView, final String url) {  
  7.   
  8.     mHandler = new Handler() {  
  9.         @Override  
  10.         public void handleMessage(Message msg) {  
  11.             ImgHolder holder = (ImgHolder) msg.obj;  
  12.             if (holder.imageView.getTag().equals(holder.url)) {  
  13.                 holder.imageView.setImageBitmap(holder.bitmap);  
  14.             }  
  15.         }  
  16.     };  
  17.     new Thread() {  
  18.         @Override  
  19.         public void run() {  
  20.             Bitmap bitmap = getBitmapFromUrl(url);  
  21.             Message message = Message.obtain();  
  22.             message.obj = new ImgHolder(imageView, bitmap, url);  
  23.             mHandler.sendMessage(message);  
  24.         }  
  25.     }.start();  
  26. }  

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. private class ImgHolder {  
  2.     public Bitmap bitmap;  
  3.     public ImageView imageView;  
  4.     public String url;  
  5.   
  6.     public ImgHolder(ImageView iv, Bitmap bm,String url) {  
  7.         this.imageView = iv;  
  8.         this.bitmap = bm;  
  9.         this.url = url;  
  10.     }  
  11. }  

修改后的方法,依然是使用Handler,这个没有别的选择,首先我们下载图像,然后将图像和ImageView通过一个对象来进行匹配保存。在Handler拿到对象后,通过判断当前的tag与url是否对应来决定是否加载。这里就利用到了我们在 Android异步加载全解析之开篇瞎扯淡里面所提到的:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. viewHolder.imageView.setTag(url);  

这里就派上用场了。

最后,在Adapter中修改下代码:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public View getView(int position, View convertView, ViewGroup parent) {  
  3.     String url = mData.get(position);  
  4.     ViewHolder viewHolder = null;  
  5.     if (convertView == null) {  
  6.         viewHolder = new ViewHolder();  
  7.         convertView = mInflater.inflate(R.layout.listview_item, null);  
  8.         viewHolder.imageView = (ImageView) convertView.findViewById(R.id.iv_lv_item);  
  9.         convertView.setTag(viewHolder);  
  10.     } else {  
  11.         viewHolder = (ViewHolder) convertView.getTag();  
  12.     }  
  13.     viewHolder.imageView.setTag(url);  
  14.     viewHolder.imageView.setImageResource(R.drawable.ic_launcher);  
  15.     mImageLoader.showImageByThread(viewHolder.imageView, url);  
  16.     return convertView;  
  17. }  

在getView的时候,异步加载图片。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值