Android ListView图片异步加载显示

本文详细介绍了Android应用中图片加载与显示的优化策略,包括异步下载、本地缓存等方法,以提高应用性能并减少用户等待时间。重点阐述了ListView、GridView、WebView等组件中图片加载的实现细节,以及如何利用多线程异步加载图片并更新视图。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

模拟android中的消息机制实现图片的异步加载和动态显示
001package cn.jd3g.utils;
002 
003import java.lang.ref.SoftReference;
004import java.util.HashMap;
005import java.util.LinkedHashMap;
006import java.util.Map.Entry;
007 
008import android.graphics.Bitmap;
009import android.os.Handler;
010import android.util.Log;
011import android.widget.ImageView;
012 
013/**
014 * 利用多线程异步加载图片并更新视图
015 *
016 * @author xfzhang
017 *
018 */
019public final class AsynImageLoader {
020 
021    private LoaderThread thread;// 加载图片并发消息通知更新界面的线程
022    private HashMap<String, SoftReference<Bitmap>> imageCache;// 图片对象缓存,key:图片的url
023    private Handler handler;// 界面Activity的Handler对象
024 
025    public AsynImageLoader(Handler handler) {
026        imageCache = new HashMap<String, SoftReference<Bitmap>>();
027        this.handler = handler;
028    }
029 
030    /**
031     * 加载图片前显示到指定的ImageView中,图片的url保存在视图对象的Tag中
032     *
033     * @param imageView
034     *            要显示图片的视图
035     * @param defaultBitmap
036     *            加载需要显示的提示正在加载的默认图片对象
037     */
038    public void loadBitmap(ImageView imageView, Bitmap defaultBitmap) {
039        // 图片所对应的url,这个值在加载图片过程中很可能会被改变
040        String url = (String) imageView.getTag();
041        if (imageCache.containsKey(url)) {// 判断缓存中是否有
042            SoftReference<Bitmap> softReference = imageCache.get(url);
043            Bitmap bitmap = softReference.get();
044            if (bitmap != null) {// 如果图片对象不为空,则可挂接更新视图,并返回
045                imageView.setImageBitmap(bitmap);
046                return;
047            } else {// 如果为空,需要将其从缓存中删除(其bitmap对象已被回收释放,需要重新加载)
048                Log.e("TAG", "cache bitmap is null");
049                imageCache.remove(url);
050            }
051        }
052        imageView.setImageBitmap(defaultBitmap);// 先显示一个提示正在加载的图片
053        if (thread == null) {// 加载线程不存在,线程还未启动,需要新建线程并启动
054            thread = new LoaderThread(imageView, url);
055            thread.start();
056        } else {// 如果存在,就调用线程对象去加载
057            thread.load(imageView, url);
058        }
059 
060    }
061 
062    /**
063     * 释放缓存中所有的Bitmap对象,并将缓存清空
064     */
065    public void releaseBitmapCache() {
066        if (imageCache != null) {
067            for (Entry<String, SoftReference<Bitmap>> entry : imageCache.entrySet()) {
068                Bitmap bitmap = entry.getValue().get();
069                if (bitmap != null) {
070                    bitmap.recycle();// 释放bitmap对象
071                }
072            }
073            imageCache.clear();
074        }
075    }
076 
077    /**
078     * 加载图片并显示的线程
079     */
080    private class LoaderThread extends Thread {
081 
082        LinkedHashMap<String, ImageView> mTaskMap;// 需要加载图片并显示的图片视图对象任务链
083        private boolean mIsWait;// 标识是线程是否处于等待状态
084 
085        public LoaderThread(ImageView imageView, String url) {
086            mTaskMap = new LinkedHashMap<String, ImageView>();
087            mTaskMap.put(url, imageView);
088        }
089 
090        /**
091         * 处理某个视图的更新显示
092         *
093         * @param imageView
094         */
095        public void load(ImageView imageView, String url) {
096            mTaskMap.remove(imageView);// 任务链中可能有,得先删除
097            mTaskMap.put(url, imageView);// 将其添加到任务中
098            if (mIsWait) {// 如果线程此时处于等待得唤醒线程去处理任务队列中待处理的任务
099                synchronized (this) {// 调用对象的notify()时必须同步
100                    this.notify();
101                }
102            }
103        }
104 
105        @Override
106        public void run() {
107            while (mTaskMap.size() > 0) {// 当队列中有数据时线程就要一直运行,一旦进入就要保证其不会跳出循环
108                mIsWait = false;
109                final String url  = mTaskMap.keySet().iterator().next();
110                final ImageView imageView = mTaskMap.remove(url);
111                if (imageView.getTag() == url) {// 判断视图有没有复用(一旦ImageView被复用,其tag值就会修改变)
112                    final Bitmap bitmap = MyConnection.getBitmapByUrl(url);// 此方法应该是从网络或sd卡中加载
113                    try {
114                        Thread.sleep(1000);// 模拟网络加载数据时间
115                    } catch (InterruptedException e1) {
116                        e1.printStackTrace();
117                    }
118                    // 将加载的图片放入缓存map中
119                    imageCache.put(url, new SoftReference<Bitmap>(bitmap));
120                    if (url == imageView.getTag()) {// 再次判断视图有没有复用
121                        handler.post(new Runnable() {// 通过消息机制在主线程中更新UI
122                            @Override
123                            public void run() {
124                                imageView.setImageBitmap(bitmap);
125                            }
126                        });
127                    }
128                }
129                if (mTaskMap.isEmpty()) {// 当任务队列中没有待处理的任务时,线程进入等待状态
130                    try {
131                        mIsWait = true;// 标识线程的状态,必须在wait()方法之前
132                        synchronized (this) {
133                            this.wait();// 保用线程进入等待状态,直到有新的任务被加入时通知唤醒
134                        }
135                    } catch (InterruptedException e) {
136                        e.printStackTrace();
137                    }
138                }
139            }
140        }
141    }
142}
01private class ProductListAdapter extends BaseAdapter {
02 
03        private AsynImageLoader mImageAsynLoader;
04 
05        public ProductListAdapter() {
06            mImageAsynLoader = new AsynImageLoader(mHandler);
07        }
08 
09        @Override
10        public int getCount() {
11            int size = Math.min(mLastItemViewIndex + 1, mDataList.size());
12            mLastItemViewIndex = size - 1;
13            return size;
14        }
15 
16        @Override
17        public Object getItem(int position) {
18            return mDataList.get(position);
19        }
20 
21        @Override
22        public long getItemId(int position) {
23            return position;
24        }
25 
26        @Override
27        public View getView(int position, View convertView, ViewGroup parent) {
28            if (convertView == null) {
29                convertView = getLayoutInflater().inflate(R.layout.product_list_item,
30                        null);
31            }
32            ImageView imageView = (ImageView) convertView
33                    .findViewById(R.id.iv_item_product_image);
34            Map<String, String> map = mDataList.get(position);
35//存放图片所对应的url
36            imageView.setTag(map.get("product_pic_address"));
37            mImageAsynLoader.loadBitmap(imageView, mDefautBitmap);
38            return convertView;
39        }
40    }

****************************************************************************************************************************************************************************

Android应用中,从远程获取图片算是经常要用的一个功能,而图片资源往往会消耗比较大的流量,而且如果加载在图片比较多的View中,如ListView GridView WebView中,加载速度会比较缓慢。Android图片加载速度很慢的话,程序很容易就挂掉,等待的用户用户同样会等到崩溃。

我们一般的处理方法是:

异步下载

本地缓存

-----------------------------------------------------------

先说异步下载



 

如一个ListView的异步下载

Java代码  收藏代码
  1. public class AsyncActivity extends Activity  {  
  2.     List<data> insList;  
  3.     private ListView mListView;  
  4.     private ArrayList<data> mListItems;  
  5.     private ItemAdapter mAdapter;  
  6.     private ImageView mLfteView;  
  7.     private ImageView mRightView;  
  8.       
  9.       
  10.   
  11.     private static final int REFRESH_LIST = 1;  
  12.     Handler mHandler = new Handler() {  
  13.         public void handleMessage(android.os.Message msg) {  
  14.             int type = msg.what;  
  15.             switch (type) {  
  16.             case REFRESH_LIST: {  
  17.               
  18.                 if (mListItems.size() > 0) {  
  19.                     mListView.setVisibility(View.VISIBLE);  
  20.                     if (mAdapter== null) {  
  21.                         mAdapter = new Adapter(AsyncActivity.this,  
  22.                                 mListItems);  
  23.                         mListView.setAdapter(mAdapter);  
  24.                     }  
  25.                         mAdapter.notifyDataSetChanged();  
  26.   
  27.                 }  
  28.                 mListView.requestFocus();  
  29.                   
  30.                 unShowDialogLoading();  
  31.                 break;  
  32.             }  
  33.   
  34.           
  35.             default:  
  36.                 break;  
  37.             }  
  38.         };  
  39.     };  
  40.   
  41.       
  42.     public void onCreate(Bundle savedInstanceState) {   
  43.         super.onCreate(savedInstanceState);   
  44.         setContentView(R.layout.insurance_list);   
  45.         initViews();  
  46.         getList();  
  47.     }   
  48.     
  49.     /** 
  50.      * 初始化view 
  51.      */  
  52.     private void initViews(){  
  53.           
  54.         mListView = (ListView)findViewById(R.id.list);  
  55.         mListView.setAdapter(mAdapter);  
  56.         mListView.setOnItemClickListener(new OnItemClickListener(){  
  57.             public void onItemClick(AdapterView<?> arg0, View v, int id,  
  58.                     long pos) {  
  59.                 // 获得被单击的项       
  60.                 //跳转  
  61.               
  62.             }  
  63.         });  
  64.      
  65.           
  66.         mListItems = new ArrayList<data>();  
  67.   
  68.     }  
  69.   
  70.       
  71.     
  72.     private void getList() {  
  73.         showDialogLoading();  
  74.          //得到保险列表  
  75.         insuranceData insurance = new insuranceData();  
  76.         insList = insurance.getList();  
  77.         mListItems.clear();  
  78.         mListItems.addAll(insList);  
  79.         mHandler.sendEmptyMessage(REFRESH_LIST);  
  80.           
  81.           
  82.     }  
  83.       
  84.   
  85.     private ProgressDialog mLoadingDialog;  
  86.   
  87.     private void showDialogLoading() {  
  88.         mLoadingDialog = new ProgressDialog(this);                   
  89.         mLoadingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);  
  90.          //spinner  自旋体 像螺旋桨那样                               
  91.         mLoadingDialog.setMessage("载入中,请稍候...");                   
  92.         mLoadingDialog.setIndeterminate(false);//设置进度条是否为不明确                  
  93.         mLoadingDialog.setCancelable(true);//设置进度条是否可以按退回健取消                                 
  94.         mLoadingDialog.show();   
  95.     }  
  96.   
  97.     private void unShowDialogLoading() {  
  98.         if (mLoadingDialog == null)  
  99.             return;  
  100.         else  
  101.             mLoadingDialog.dismiss();  
  102.     }  
  103.       
  104. }  

 

它的Adapter是

 

Java代码  收藏代码
  1. public class InsuranceItemAdapter extends BaseAdapter{  
  2.     private ArrayList<data> mList;  
  3.     private Context mContext;  
  4.     // 异步加载图片的线程  
  5.     private AsyncImageLoader imageLoader = new AsyncImageLoader();  
  6.     //当前的缓存  
  7.     private Map<Integer, View> viewMap = new HashMap<Integer, View>();  
  8.   
  9.     public InsuranceItemAdapter(Context context, ArrayList<data> ins) {  
  10.         mContext = context;  
  11.         mList= ins;  
  12.     }  
  13.   
  14.     public InsuranceItemAdapter(Context context, ArrayList<data> ins,  
  15.             Handler handler) {  
  16.         mContext = context;  
  17.         mList= ins;  
  18.     }  
  19.   
  20.     public void setInsurance(ArrayList<data> ins) {  
  21.         mList= ins;  
  22.     }  
  23.       
  24.       
  25.       
  26.     public int getCount() {  
  27.         return mList.size();  
  28.     }  
  29.   
  30.     public Object getItem(int position) {  
  31.         try {  
  32.             return mList.get(position);  
  33.         } catch (Exception ex) {  
  34.             return null;  
  35.         }  
  36.     }  
  37.   
  38.     public long getItemId(int position) {  
  39.         return position;  
  40.     }  
  41.   
  42.       
  43.     private View newView() {  
  44.         ItemView view = new ItemView(mContext);  
  45.         return view;  
  46.     }  
  47.       
  48.       
  49.       
  50.     public View getView(int position, View convertView, ViewGroup parent) {  
  51.         //先从缓存里面读取  
  52.         ViewHolder holder = null;    
  53.         View view;    
  54.           
  55.          if (viewMap.get(position) == null) {    
  56.             view = newView();  
  57.             holder = new ViewHolder();    
  58.             holder.mTitle = (TextView)view.findViewById(R.id.ins_title);  
  59.             holder.mTime = (TextView)view.findViewById(R.id.ins_time);  
  60.             holder.mType = (TextView)view.findViewById(R.id.ins_from);  
  61.             holder.mPic = (ImageView)view.findViewById(R.id.ins_small_pic);  
  62.             final int p = position;    
  63.             viewMap.put(position, view);    
  64.             view.setTag(holder);    
  65.          }else{  
  66.              Log.e("MainActivity","position2 = "+position);    
  67.              view = viewMap.get(position);    
  68.              holder = (ViewHolder)view.getTag();    
  69.          }  
  70.           
  71.          data ins = mList.get(position);  
  72.          holder.mTitle.setText(ins.getTitle());  
  73.   
  74.           
  75.         holder.mPic.setVisibility(View.VISIBLE);  
  76.           
  77.         // 异步加载图片  
  78.         Drawable cachedImage = imageLoader.loadDrawable(ins.getPic(), holder.mPic,  
  79.                 new ImageCallback() {  
  80.                     public void imageLoaded(Drawable imageDrawable,  
  81.                             ImageView imageView, String imageUrl) {  
  82.                         imageView.setImageDrawable(imageDrawable);  
  83.                     }  
  84.                 });  
  85.         if (cachedImage != null) {  
  86.             holder.mPic.setImageDrawable(cachedImage);  
  87.         }  
  88.            
  89.          return view;    
  90.   
  91.     }  
  92.       
  93.       
  94.     static class ViewHolder{    
  95.         ImageView mPic;  
  96.         TextView mTitle;  
  97.         TextView mTime;  
  98.         TextView mType;  
  99.     }  
  100. }  

 

 

原理简单,不罗嗦了

 

本地缓存

  

   就是先读取本地的数据,如果本地没有再从网络上获取

 

WebView中很简单,

Java代码  收藏代码
  1. //优先缓存  
  2.         mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);  

 就能实现了。

 

 

其他地方我们就得自己写一个异步图片下载了,

Java代码  收藏代码
  1. public class AsyncImageLoader {  
  2.   
  3.      //SoftReference是软引用,是为了更好的为了系统回收变量  
  4.     private static HashMap<String, SoftReference<Drawable>> imageCache;  
  5.       
  6.     static {  
  7.         imageCache = new HashMap<String, SoftReference<Drawable>>();  
  8.     }  
  9.       
  10.       
  11.     public AsyncImageLoader() {  
  12.           
  13.     }  
  14.     public Drawable loadDrawable(final String imageUrl,final ImageView imageView, final ImageCallback imageCallback){  
  15.         if (imageCache.containsKey(imageUrl)) {  
  16.             //从缓存中获取  
  17.             SoftReference<Drawable> softReference = imageCache.get(imageUrl);  
  18.             Drawable drawable = softReference.get();  
  19.             if (drawable != null) {  
  20.                 return drawable;  
  21.             }  
  22.         }  
  23.         final Handler handler = new Handler() {  
  24.             public void handleMessage(Message message) {  
  25.                 imageCallback.imageLoaded((Drawable) message.obj, imageView,imageUrl);  
  26.             }  
  27.         };  
  28.         //建立新一个新的线程下载图片  
  29.         new Thread() {  
  30.             @Override  
  31.             public void run() {  
  32.                 Drawable drawable = null;  
  33.                 try {  
  34.                     drawable = ImageUtil.geRoundDrawableFromUrl(imageUrl, 20);  
  35.                 } catch (Exception e) {  
  36.                     e.printStackTrace();  
  37.                 }  
  38.                 imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));  
  39.                 Message message = handler.obtainMessage(0, drawable);  
  40.                 handler.sendMessage(message);  
  41.             }  
  42.         }.start();  
  43.         return null;  
  44.     }  
  45.     //回调接口  
  46.     public interface ImageCallback {  
  47.         public void imageLoaded(Drawable imageDrawable,ImageView imageView, String imageUrl);  
  48.     }  
  49. }  

 

 

当然我们也可以存数据库中,从数据库中优先获取,当没有网络的时候达到离线阅读的功能。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值