Android高效显示图片详解(二)

转载 2013年12月02日 15:10:12

http://blog.csdn.net/zhiying201039/article/details/8665598

 上节课我们介绍了如何加载和显示大图,这节课我们就要把这个技巧与实际开发联系起来,在实际的开发过程中,最常见的场景就是用ListView,GridView等集合显示控件

来呈现图片,这节课,我们就要用这些控件来高效的显示图片。

       实际的使用环境中,如果图片来源是SD卡或者网络,那那么加载图片的过程一定不要放在UI线程中,这样会严重的阻塞UI线程,出现ANR,程序就废了。因此我们首先要实现异步加载。

第一步:利用AsyncTask实现图片的异步加载

将decodeSampledBitmapFromResource方法放入Task的doInBackground中后台执行。不熟悉AsyncTask的同学可以学习AsyncTask的相关知识,这里不再过多介绍。

代码:

[html] view plaincopy
  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     private final WeakReference<ImageView> imageViewReference;  
  3.     private int data = 0;  
  4.   
  5.   
  6.     public BitmapWorkerTask(ImageView imageView) {  
  7.         // Use a WeakReference to ensure the ImageView can be garbage collected  
  8.         imageViewReference = new WeakReference<ImageView>(imageView);  
  9.     }  
  10.   
  11.   
  12.     // Decode image in background.  
  13.     @Override  
  14.     protected Bitmap doInBackground(Integer... params) {  
  15.         data = params[0];  
  16.         return decodeSampledBitmapFromResource(getResources(), data, 100, 100));  
  17.     }  
  18.   
  19.   
  20.     // Once complete, see if ImageView is still around and set bitmap.  
  21.     @Override  
  22.     protected void onPostExecute(Bitmap bitmap) {  
  23.         if (imageViewReference != null && bitmap != null) {  
  24.             final ImageView imageView = imageViewReference.get();  
  25.             if (imageView != null) {  
  26.                 imageView.setImageBitmap(bitmap);  
  27.             }  
  28.         }  
  29.     }  
  30. }  

注意,这里对ImageView使用 WeakReference弱引用的目的是确保 AsyncTask不会妨碍系统对ImageView必要时候的垃圾回收。否则可能会出现内存泄露,同时,我们一定要在Task执行完毕后对ImageView的存在性进行判断,因为不能保证Task执行完毕后,ImageView还会存在。

下来我们按照下面的代码就可以使用这个Task了:
[html] view plaincopy
  1. public void loadBitmap(int resId, ImageView imageView) {  
  2.     BitmapWorkerTask task = new BitmapWorkerTask(imageView);  
  3.     task.execute(resId);  
  4. }  

第二步:处理并发情况


ListView与GridView这种多子视图的控件会出现两个问题,

第一,一个ListView会有众多的ChildView,为了更高效的利用内存,控件会自动回收掉被用户滑动过去,不在当前有显示的ChildView,如果每一个ChildView都开启一个Task去加载图片,这样就不能保证开启Task的ChildView在Task执行完毕后没有被回收掉(很有可能用户滑动到其他地方去了)。


第二,因为每张图片的处理时间是不同的,因此同样不能保证加载完成的次序与开始的次序一致。


下来我们开始着手解决这些问题,我们要让ImageView与Task形成一种绑定的关系。

我们先来创建一个特殊的Drawable,这个Drawable有两个功能,一个是与Task形成一种绑定的关系,另外也充当了ImageView的临时占位图像,该Drawable的代码如下:

[html] view plaincopy
  1. static class AsyncDrawable extends BitmapDrawable {  
  2.     private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;  
  3.   
  4.     public AsyncDrawable(Resources res, Bitmap bitmap,  
  5.             BitmapWorkerTask bitmapWorkerTask) {  
  6.         super(res, bitmap);  
  7.         bitmapWorkerTaskReference =  
  8.             new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);  
  9.     }  
  10.   
  11.     public BitmapWorkerTask getBitmapWorkerTask() {  
  12.         return bitmapWorkerTaskReference.get();  
  13.     }  
  14. }  

在该Drawable中通过弱引用能与对应的Task形成一种一一对应的捆绑关系。

我们可以这样使用它,在执行Task之前,先创建一个对应的Drawable,并把它当成将要呈现实际图片的ImageView占位图片,同时也与ImageView形成了绑定关系。

[html] view plaincopy
  1. public void loadBitmap(int resId, ImageView imageView) {  
  2.     if (cancelPotentialWork(resId, imageView)) {  
  3.         final BitmapWorkerTask task = new BitmapWorkerTask(imageView);  
  4.         final AsyncDrawable asyncDrawable =  
  5.                 new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);  
  6.         imageView.setImageDrawable(asyncDrawable);  
  7.         task.execute(resId);  
  8.     }  
  9. }  

当然,我们需要判断下ImageView之前是否已经绑定了,如果之前绑定过但与本次的图片不同,那我们就要按最新的需要从新绑定下,如果之前与现在的一致,则保持原状,不再从新绑定,代码中的cancelPotentialWork就是做这个工作的,其代码如下:

[html] view plaincopy
  1. public static boolean cancelPotentialWork(int data, ImageView imageView) {  
  2.     final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);  
  3.   
  4.     if (bitmapWorkerTask != null) {  
  5.         final int bitmapData = bitmapWorkerTask.data;  
  6.         if (bitmapData != data) {  
  7.             // Cancel previous task  
  8.             bitmapWorkerTask.cancel(true);  
  9.         } else {  
  10.             // The same work is already in progress  
  11.             return false;  
  12.         }  
  13.     }  
  14.     // No task associated with the ImageView, or an existing task was cancelled  
  15.     return true;  
  16. }  

[html] view plaincopy
  1. private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {  
  2.    if (imageView != null) {  
  3.        final Drawable drawable = imageView.getDrawable();  
  4.        if (drawable instanceof AsyncDrawable) {  
  5.            final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;  
  6.            return asyncDrawable.getBitmapWorkerTask();  
  7.        }  
  8.     }  
  9.     return null;  
  10. }  

最后,我们在Task的onPostExecute函数中,把加载的图片更新到视图中去,在更新前我们需要检查下Task是否被取消,并且当前的Task是否是那个与ImageView关联的Task,一致则我们把图片更新到ImageView上去,代码如下:

[html] view plaincopy
  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     ...  
  3.   
  4.     @Override  
  5.     protected void onPostExecute(Bitmap bitmap) {  
  6.         if (isCancelled()) {  
  7.             bitmap = null;  
  8.         }  
  9.   
  10.         if (imageViewReference != null && bitmap != null) {  
  11.             final ImageView imageView = imageViewReference.get();  
  12.             final BitmapWorkerTask bitmapWorkerTask =  
  13.                     getBitmapWorkerTask(imageView);  
  14.             if (this == bitmapWorkerTask && imageView != null) {  
  15.                 imageView.setImageBitmap(bitmap);  
  16.             }  
  17.         }  
  18.     }  
  19. }  

最后,实际的使用也相当简单,只需要在你的ListView适配器的getView函数中调用上面的loadBitmap函数就OK了~


下一节我们来说说缓存,加入缓存让这个机制更加强大。。


感谢收看! 多多好评,在此谢过!

Android高效显示图片详解(一)

说明:       本讲义分为三部分,较为详细的介绍了Android平台下图片显示,加载等操作的处理原则与办法,以供大家共同学习,转载请注明出处 “From 移动微技”。 前提与解释: ...
  • ahaochina
  • ahaochina
  • 2014年08月25日 10:51
  • 98

Android高效显示图片详解(三)

http://blog.csdn.net/zhiying201039/article/details/8682419 用户在使用ListView或GridView时,控件会自动把用户滑过的已不在当前...
  • dajian790626
  • dajian790626
  • 2013年12月02日 15:09
  • 1499

Android高效显示图片详解(一) .

http://blog.csdn.net/zhiying201039/article/details/8653786 说明:       本讲义分为三部分,较为详细的介绍了Android平台下图片...
  • dajian790626
  • dajian790626
  • 2013年11月30日 19:15
  • 654

高效显示图片(一,二)

原文链接:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html 有效的加载大图 图像存在各种形...
  • yangxi_001
  • yangxi_001
  • 2013年05月09日 11:36
  • 502

android中高效显示图片

1.高效加载大量Bitmap 系统为我们提供了BitmapFactory的一些静态方法来解析图片转为Bitmap,但是调用这些函数就会尝试请求构建bitmap的内存,这样就很容易内存溢出;但是我们可...
  • baidu_26994091
  • baidu_26994091
  • 2016年06月14日 21:37
  • 330

Android高效显示图片漫谈

本讲义分为三部分,较为详细的介绍了Android平台下图片显示,加载等操作的处理原则与办法,以供大家共同学习tan...
  • mokee8023
  • mokee8023
  • 2014年09月10日 08:21
  • 267

cmake 手册详解

http://www.cnblogs.com/coderfenghc/archive/2012/06/16/CMake_ch_01.html 我对于在电脑上阅读大篇幅的文章不是很感兴趣,只能一章一章...
  • u014221266
  • u014221266
  • 2016年10月16日 15:06
  • 309

高效显示图片

原文链接:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html 有效的加载大图 图像存在各种形状和大小...
  • yangxi_001
  • yangxi_001
  • 2013年11月28日 09:27
  • 471

《TCP/IP详解卷2:实现》笔记--TCP的输入

当收到的数据报的协议字段指明这是一个TCP报文段时,ipintr(通过协议协议转换表中的pr_input函数)会调用tcp_input 进行处理,tcp_inut在软件中断一级执行。 函数非常长,我们...
  • TODD911
  • TODD911
  • 2015年01月06日 22:45
  • 1767

《TCP/IP详解卷2:实现》笔记--IP选项处理

IP输入函数(ipintr)将在验证分组格式(检验和,长度等)之后,确定分组是否到达目的地之前,对选项进行处理。这表明分组所 遇到的每个路由器以及最终的目的主机都对要分组的选项进行处理。 IP分组内可...
  • TODD911
  • TODD911
  • 2014年07月22日 14:11
  • 2475
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android高效显示图片详解(二)
举报原因:
原因补充:

(最多只允许输入30个字)