Android实现异步从网络加载图片列表 .

本文介绍了一种在Android中利用异步线程加载ListView图片的方法,通过缓存机制提高加载效率,确保良好的用户体验。

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

http://blog.youkuaiyun.com/carterjin/article/details/7995935

 

有时会有在加载ListView的时候,包含用户头像或其他需要到网络获取的图片信息,这时如果等待全部获取完成再显示会比较慢,很影响用户体验,所以这时就需要利用到异步加载图片的方法。

今天整理的方法,是用Thread来进行加载,没有利用ThreadPool的方法,后面的方法以后再慢慢学一下吧,先把学会的这个记下来。

 

具体的效果是,加入每个ListView的项只需要显示一个图片,每张图片都是本地没有的,则先把他们都显示成一张默认的图片(我用的是程序图标),然后开启后台线程去网上取图片,最后再一个一个加载出来。

下面是具体的步骤:

 

步骤一:写一个异步加载类,我的叫AsyncImageLoader

  1. package com.carter.asynchronousimage;  
  2.   
  3. import java.io.InputStream;  
  4. import java.lang.ref.SoftReference;  
  5. import java.net.URL;  
  6. import java.util.HashMap;  
  7.   
  8. import android.graphics.drawable.BitmapDrawable;  
  9. import android.graphics.drawable.Drawable;  
  10. import android.net.Uri;  
  11. import android.os.Handler;  
  12. import android.os.Message;  
  13. import android.util.Log;  
  14.   
  15.   
  16. /** 
  17.  * 异步加载图片类,内部有缓存,可以通过后台线程获取网络图片。首先生成一个实例,并调用loadDrawableByTag方法来获取一个Drawable对象 
  18.  */  
  19. public class AsyncImageLoader {  
  20.       
  21.     /** 
  22.      * 使用软引用SoftReference,可以由系统在恰当的时候更容易的回收 
  23.      */  
  24.     private HashMap<String, SoftReference<Drawable>> imageCache;  
  25.       
  26.       
  27.     public AsyncImageLoader(){  
  28.         imageCache = new HashMap<String, SoftReference<Drawable>>();  
  29.     }  
  30.       
  31.       
  32.       
  33.       
  34.     /** 
  35.      * 通过传入的TagInfo来获取一个网络上的图片 
  36.      * @param tag TagInfo对象,保存了position、url和一个待获取的Drawable对象 
  37.      * @param callback ImageCallBack对象,用于在获取到图片后供调用侧进行下一步的处理 
  38.      * @return drawable 从网络或缓存中得到的Drawable对象,可为null,调用侧需判断 
  39.      */  
  40.     public Drawable loadDrawableByTag(final TagInfo tag, final ImageCallBack callback){  
  41.         Drawable drawable;  
  42.           
  43.         /** 
  44.          * 先在缓存中找,如果通过URL地址可以找到,则直接返回该对象 
  45.          */  
  46.         if(imageCache.containsKey(tag.getUrl())){  
  47.             drawable = imageCache.get(tag.getUrl()).get();  
  48.             if(null!=drawable){  
  49.                 return drawable;  
  50.             }  
  51.         }  
  52.           
  53.           
  54.         /** 
  55.          * 用于在获取到网络图片后,保存图片到缓存,并触发调用侧的处理 
  56.          */  
  57.         final Handler handler = new Handler(){  
  58.   
  59.             @Override  
  60.             public void handleMessage(Message msg) {  
  61.   
  62.                 TagInfo info = (TagInfo)msg.obj;  
  63.                 imageCache.put(info.url, new SoftReference<Drawable>(info.drawable));  
  64.                 callback.obtainImage(info);  
  65.                   
  66.                 super.handleMessage(msg);  
  67.             }  
  68.               
  69.         };  
  70.           
  71.           
  72.         /** 
  73.          * 如果在缓存中没有找到,则开启一个线程来进行网络请求 
  74.          */  
  75.         new Thread(new Runnable() {  
  76.               
  77.             @Override  
  78.             public void run() {  
  79.                   
  80.                 TagInfo info = getDrawableIntoTag(tag);  
  81.                 Message msg = new Message();  
  82.                 msg.what = 0;  
  83.                 msg.obj = info;  
  84.                 handler.sendMessage(msg);  
  85.             }  
  86.         }).start();  
  87.           
  88.         return null;  
  89.     }  
  90.       
  91.       
  92.       
  93.     /** 
  94.      * 通过传入的TagInfo对象,利用其URL属性,到网络请求图片,获取到图片后保存在TagInfo的Drawable属性中,并返回该TagInfo 
  95.      * @param info TagInfo对象,需要利用里面的url属性 
  96.      * @return TagInfo 传入的TagInfo对象,增加了Drawable属性后返回 
  97.      */  
  98.     public TagInfo getDrawableIntoTag(TagInfo info){  
  99.         URL request;  
  100.         InputStream input;  
  101.         Drawable drawable = null;  
  102.           
  103.         try{  
  104.             request = new URL(info.getUrl());  
  105.             input = (InputStream)request.getContent();  
  106.             drawable = Drawable.createFromStream(input, "src"); // 第二个属性可为空,为DEBUG下使用,网上的说明   
  107.         }  
  108.         catch(Exception e){  
  109.             e.printStackTrace();  
  110.         }  
  111.           
  112.         info.drawable = drawable;  
  113.         return info;  
  114.     }  
  115.       
  116.       
  117.       
  118.     /** 
  119.      * 获取图片的回调接口,里面的obtainImage方法在获取到图片后进行调用 
  120.      */  
  121.     interface ImageCallBack{  
  122.         /** 
  123.          * 获取到图片后在调用侧执行具体的细节 
  124.          * @param info TagInfo对象,传入的info经过处理,增加Drawable属性,并返回给传入者 
  125.          */  
  126.         public void obtainImage(TagInfo info);  
  127.     }  
  128.       
  129.       
  130.   
  131. }  
package com.carter.asynchronousimage;

import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.HashMap;

import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.util.Log;


/**
 * 异步加载图片类,内部有缓存,可以通过后台线程获取网络图片。首先生成一个实例,并调用loadDrawableByTag方法来获取一个Drawable对象
 */
public class AsyncImageLoader {
    
    /**
     * 使用软引用SoftReference,可以由系统在恰当的时候更容易的回收
     */
    private HashMap<String, SoftReference<Drawable>> imageCache;
    
    
    public AsyncImageLoader(){
        imageCache = new HashMap<String, SoftReference<Drawable>>();
    }
    
    
    
    
    /**
     * 通过传入的TagInfo来获取一个网络上的图片
     * @param tag TagInfo对象,保存了position、url和一个待获取的Drawable对象
     * @param callback ImageCallBack对象,用于在获取到图片后供调用侧进行下一步的处理
     * @return drawable 从网络或缓存中得到的Drawable对象,可为null,调用侧需判断
     */
    public Drawable loadDrawableByTag(final TagInfo tag, final ImageCallBack callback){
        Drawable drawable;
        
        /**
         * 先在缓存中找,如果通过URL地址可以找到,则直接返回该对象
         */
        if(imageCache.containsKey(tag.getUrl())){
            drawable = imageCache.get(tag.getUrl()).get();
            if(null!=drawable){
                return drawable;
            }
        }
        
        
        /**
         * 用于在获取到网络图片后,保存图片到缓存,并触发调用侧的处理
         */
        final Handler handler = new Handler(){

            @Override
            public void handleMessage(Message msg) {

                TagInfo info = (TagInfo)msg.obj;
                imageCache.put(info.url, new SoftReference<Drawable>(info.drawable));
                callback.obtainImage(info);
                
                super.handleMessage(msg);
            }
            
        };
        
        
        /**
         * 如果在缓存中没有找到,则开启一个线程来进行网络请求
         */
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                
                TagInfo info = getDrawableIntoTag(tag);
                Message msg = new Message();
                msg.what = 0;
                msg.obj = info;
                handler.sendMessage(msg);
            }
        }).start();
        
        return null;
    }
    
    
    
    /**
     * 通过传入的TagInfo对象,利用其URL属性,到网络请求图片,获取到图片后保存在TagInfo的Drawable属性中,并返回该TagInfo
     * @param info TagInfo对象,需要利用里面的url属性
     * @return TagInfo 传入的TagInfo对象,增加了Drawable属性后返回
     */
    public TagInfo getDrawableIntoTag(TagInfo info){
        URL request;
        InputStream input;
        Drawable drawable = null;
        
        try{
            request = new URL(info.getUrl());
            input = (InputStream)request.getContent();
            drawable = Drawable.createFromStream(input, "src"); // 第二个属性可为空,为DEBUG下使用,网上的说明
        }
        catch(Exception e){
            e.printStackTrace();
        }
        
        info.drawable = drawable;
        return info;
    }
    
    
    
    /**
     * 获取图片的回调接口,里面的obtainImage方法在获取到图片后进行调用
     */
    interface ImageCallBack{
        /**
         * 获取到图片后在调用侧执行具体的细节
         * @param info TagInfo对象,传入的info经过处理,增加Drawable属性,并返回给传入者
         */
        public void obtainImage(TagInfo info);
    }
    
    

}


里面基本每个重要的地方都在我理解的情况下加了注释,应该很多人都能看懂的。

 

步骤二:写一个Activity用于展示这些内容,我的是AsynImageActivity

  1. package com.carter.asynchronousimage;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.List;  
  5. import java.util.ArrayList;  
  6.   
  7. import com.carter.asynchronousimage.AsyncImageLoader.ImageCallBack;  
  8.   
  9. import android.app.Activity;  
  10. import android.content.Context;  
  11. import android.graphics.drawable.Drawable;  
  12. import android.os.Bundle;  
  13. import android.util.Log;  
  14. import android.view.LayoutInflater;  
  15. import android.view.View;  
  16. import android.view.ViewGroup;  
  17. import android.widget.BaseAdapter;  
  18. import android.widget.ImageView;  
  19. import android.widget.ListView;  
  20.   
  21. public class AsynImageActivity extends Activity {  
  22.       
  23.     Context context;  
  24.     ListView list_lv;  
  25.     List<ImageEntry> mList;  
  26.     MyAdapter adapter;  
  27.       
  28.     // 测试数据,网上找的图片   
  29.     String[] urls = new String[]{  
  30.             "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD9.png",  
  31.             "http://pic1.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD0.png",  
  32.             "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD7.png",  
  33.             "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD6.png",  
  34.             "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD5.png",  
  35.             "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD4.png",  
  36.             "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD3.png",  
  37.             "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD7.png",  
  38.             "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD14.png",  
  39.             "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD13.png",  
  40.             "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD12.png",  
  41.             "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD11.png",  
  42.             "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD5.png",  
  43.             "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD1.png",  
  44.             "http://pic1.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD0.png"  
  45.               
  46.     };  
  47.       
  48.     /** Called when the activity is first created. */  
  49.     @Override  
  50.     public void onCreate(Bundle savedInstanceState) {  
  51.         super.onCreate(savedInstanceState);  
  52.         setContentView(R.layout.main);  
  53.           
  54.         context = this;  
  55.         list_lv = (ListView) findViewById(R.id.list_lv);  
  56.           
  57.         mList = new ArrayList<ImageEntry>();  
  58.           
  59.         for(int i=0; i<urls.length; i++){  
  60.             ImageEntry entry = new ImageEntry();  
  61.             entry.setUrl(urls[i]);  
  62.             mList.add(entry);  
  63.         }  
  64.           
  65.         adapter = new MyAdapter(context, mList);  
  66.         list_lv.setAdapter(adapter);  
  67.     }  
  68.       
  69.       
  70.       
  71.       
  72.       
  73.       
  74.     /** 
  75.      * 一个ImageEntry代表了一个带有图片地址url等其他属性的实例,为提高可扩展性,封装了一个对象。目前只包含url这一个属性。 
  76.      */  
  77.     class ImageEntry{  
  78.         String url;  
  79.           
  80.         public String getUrl(){  
  81.             return this.url;  
  82.         }  
  83.           
  84.         public void setUrl(String url){  
  85.             this.url = url;  
  86.         }  
  87.     }  
  88.       
  89.       
  90.       
  91.     /** 
  92.      * 重写的Adapter 
  93.      */  
  94.     class MyAdapter extends BaseAdapter{  
  95.           
  96.         Context context;  
  97.         List<ImageEntry> mList;  
  98.           
  99.         HashMap<String, Drawable> imgCache;     // 图片缓存   
  100.         HashMap<Integer, TagInfo> tag_map;      // TagInfo缓存   
  101.         AsyncImageLoader loader;                // 异步加载图片类   
  102.           
  103.           
  104.           
  105.         /** 
  106.          * 构造函数 
  107.          * @param context 上下文 
  108.          * @param list 包含了所有要显示的图片的ImageEntry对象的列表 
  109.          */  
  110.         public MyAdapter(Context context, List<ImageEntry> list){  
  111.             this.context = context;  
  112.             this.mList = list;  
  113.             imgCache = new HashMap<String, Drawable>();  
  114.             loader = new AsyncImageLoader();  
  115.             tag_map = new HashMap<Integer, TagInfo>();  
  116.         }  
  117.   
  118.         @Override  
  119.         public int getCount() {  
  120.             // TODO Auto-generated method stub   
  121.             return mList.size();  
  122.         }  
  123.   
  124.         @Override  
  125.         public Object getItem(int position) {  
  126.             // TODO Auto-generated method stub   
  127.             return mList.get(position);  
  128.         }  
  129.   
  130.         @Override  
  131.         public long getItemId(int position) {  
  132.             // TODO Auto-generated method stub   
  133.             return position;  
  134.         }  
  135.   
  136.         @Override  
  137.         public View getView(int position, View convertView, ViewGroup parent) {  
  138.             // TODO Auto-generated method stub   
  139.             ViewHolder holder = null;  
  140.               
  141.             if(null==convertView){  
  142.                 convertView = LayoutInflater.from(context).inflate(R.layout.adapter_item, nullfalse);  
  143.                 holder = new ViewHolder();  
  144.                 holder.img = (ImageView) convertView.findViewById(R.id.img);  
  145.                 convertView.setTag(holder);  
  146.             }else{  
  147.                 holder = (ViewHolder) convertView.getTag();  
  148.             }  
  149.               
  150.               
  151.             String imgurl = mList.get(position).getUrl();   // 得到该项所代表的url地址   
  152.             Drawable drawable = imgCache.get(imgurl);       // 先去缓存中找   
  153.               
  154.             TagInfo tag = new TagInfo();  
  155.             tag.setPosition(position);  // 保存了当前在adapter中的位置   
  156.             tag.setUrl(imgurl);         // 保存当前项所要加载的url   
  157.             holder.img.setTag(tag);     // 为ImageView设置Tag,为以后再获取图片后好能找到它   
  158.             tag_map.put(position, tag); // 把该TagInfo对应position放入Tag缓存中   
  159.               
  160.             if(null!=drawable){                         // 找到了直接设置为图像   
  161.                 holder.img.setImageDrawable(drawable);  
  162.             }else{                                      // 没找到则开启异步线程   
  163.                 drawable = loader.loadDrawableByTag(tag, new ImageCallBack() {  
  164.                       
  165.                     @Override  
  166.                     public void obtainImage(TagInfo ret_info) {  
  167.                           
  168.                         imgCache.put(ret_info.getUrl(), ret_info.getDrawable());    // 首先把获取的图片放入到缓存中   
  169.                           
  170.                         // 通过返回的TagInfo去Tag缓存中找,然后再通过找到的Tag来获取到所对应的ImageView   
  171.                         ImageView tag_view = (ImageView) list_lv.findViewWithTag(tag_map.get(ret_info.getPosition()));  
  172.                         Log.i("carter""tag_view: " + tag_view + " position: " + ret_info.getPosition());  
  173.                         if(null!=tag_view)  
  174.                             tag_view.setImageDrawable(ret_info.getDrawable());  
  175.                     }  
  176.                 });  
  177.                   
  178.                   
  179.                 if(null==drawable){ // 如果获取的图片为空,则默认显示一个图片   
  180.                     holder.img.setImageResource(R.drawable.ic_launcher);  
  181.                 }  
  182.             }  
  183.               
  184.               
  185.             return convertView;  
  186.         }  
  187.           
  188.           
  189.           
  190.         class ViewHolder{  
  191.             ImageView img;  
  192.         }  
  193.           
  194.     }  
  195. }  
package com.carter.asynchronousimage;

import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

import com.carter.asynchronousimage.AsyncImageLoader.ImageCallBack;

import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;

public class AsynImageActivity extends Activity {
    
    Context context;
    ListView list_lv;
    List<ImageEntry> mList;
    MyAdapter adapter;
    
    // 测试数据,网上找的图片
    String[] urls = new String[]{
            "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD9.png",
            "http://pic1.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD0.png",
            "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD7.png",
            "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD6.png",
            "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD5.png",
            "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD4.png",
            "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD3.png",
            "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD7.png",
            "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD14.png",
            "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD13.png",
            "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD12.png",
            "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD11.png",
            "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD5.png",
            "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD1.png",
            "http://pic1.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD0.png"
            
    };
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        context = this;
        list_lv = (ListView) findViewById(R.id.list_lv);
        
        mList = new ArrayList<ImageEntry>();
        
        for(int i=0; i<urls.length; i++){
            ImageEntry entry = new ImageEntry();
            entry.setUrl(urls[i]);
            mList.add(entry);
        }
        
        adapter = new MyAdapter(context, mList);
        list_lv.setAdapter(adapter);
    }
    
    
    
    
    
    
    /**
     * 一个ImageEntry代表了一个带有图片地址url等其他属性的实例,为提高可扩展性,封装了一个对象。目前只包含url这一个属性。
     */
    class ImageEntry{
        String url;
        
        public String getUrl(){
            return this.url;
        }
        
        public void setUrl(String url){
            this.url = url;
        }
    }
    
    
    
    /**
     * 重写的Adapter
     */
    class MyAdapter extends BaseAdapter{
        
        Context context;
        List<ImageEntry> mList;
        
        HashMap<String, Drawable> imgCache;     // 图片缓存
        HashMap<Integer, TagInfo> tag_map;      // TagInfo缓存
        AsyncImageLoader loader;                // 异步加载图片类
        
        
        
        /**
         * 构造函数
         * @param context 上下文
         * @param list 包含了所有要显示的图片的ImageEntry对象的列表
         */
        public MyAdapter(Context context, List<ImageEntry> list){
            this.context = context;
            this.mList = list;
            imgCache = new HashMap<String, Drawable>();
            loader = new AsyncImageLoader();
            tag_map = new HashMap<Integer, TagInfo>();
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return mList.size();
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return mList.get(position);
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            ViewHolder holder = null;
            
            if(null==convertView){
                convertView = LayoutInflater.from(context).inflate(R.layout.adapter_item, null, false);
                holder = new ViewHolder();
                holder.img = (ImageView) convertView.findViewById(R.id.img);
                convertView.setTag(holder);
            }else{
                holder = (ViewHolder) convertView.getTag();
            }
            
            
            String imgurl = mList.get(position).getUrl();   // 得到该项所代表的url地址
            Drawable drawable = imgCache.get(imgurl);       // 先去缓存中找
            
            TagInfo tag = new TagInfo();
            tag.setPosition(position);  // 保存了当前在adapter中的位置
            tag.setUrl(imgurl);         // 保存当前项所要加载的url
            holder.img.setTag(tag);     // 为ImageView设置Tag,为以后再获取图片后好能找到它
            tag_map.put(position, tag); // 把该TagInfo对应position放入Tag缓存中
            
            if(null!=drawable){                         // 找到了直接设置为图像
                holder.img.setImageDrawable(drawable);
            }else{                                      // 没找到则开启异步线程
                drawable = loader.loadDrawableByTag(tag, new ImageCallBack() {
                    
                    @Override
                    public void obtainImage(TagInfo ret_info) {
                        
                        imgCache.put(ret_info.getUrl(), ret_info.getDrawable());    // 首先把获取的图片放入到缓存中
                        
                        // 通过返回的TagInfo去Tag缓存中找,然后再通过找到的Tag来获取到所对应的ImageView
                        ImageView tag_view = (ImageView) list_lv.findViewWithTag(tag_map.get(ret_info.getPosition()));
                        Log.i("carter", "tag_view: " + tag_view + " position: " + ret_info.getPosition());
                        if(null!=tag_view)
                            tag_view.setImageDrawable(ret_info.getDrawable());
                    }
                });
                
                
                if(null==drawable){ // 如果获取的图片为空,则默认显示一个图片
                    holder.img.setImageResource(R.drawable.ic_launcher);
                }
            }
            
            
            return convertView;
        }
        
        
        
        class ViewHolder{
            ImageView img;
        }
        
    }
}

 

在Activity中需要用到一个类叫做TagInfo,这个是作为一个Tag,保存一些例如位置、地址和图像等的信息

  1. package com.carter.asynchronousimage;  
  2.   
  3. import android.graphics.drawable.Drawable;  
  4.   
  5. public class TagInfo {  
  6.     String url;  
  7.     int position;  
  8.     Drawable drawable;  
  9.       
  10.     public String getUrl() {  
  11.         return url;  
  12.     }  
  13.     public void setUrl(String url) {  
  14.         this.url = url;  
  15.     }  
  16.     public int getPosition() {  
  17.         return position;  
  18.     }  
  19.     public void setPosition(int position) {  
  20.         this.position = position;  
  21.     }  
  22.     public Drawable getDrawable() {  
  23.         return drawable;  
  24.     }  
  25.     public void setDrawable(Drawable drawable) {  
  26.         this.drawable = drawable;  
  27.     }  
  28.   
  29. }  
package com.carter.asynchronousimage;

import android.graphics.drawable.Drawable;

public class TagInfo {
    String url;
    int position;
    Drawable drawable;
    
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public int getPosition() {
        return position;
    }
    public void setPosition(int position) {
        this.position = position;
    }
    public Drawable getDrawable() {
        return drawable;
    }
    public void setDrawable(Drawable drawable) {
        this.drawable = drawable;
    }

}


 

步骤三:只要在Adapter的getView方法中调用loadDrawableByTag方法就可以了。
 

 

工程需要两个布局文件,一个是主界面main.xml,一个是每项所显示的布局文件adapter_item.xml

main.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <ListView  
  8.         android:id="@+id/list_lv"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="fill_parent"  
  11.         />  
  12.   
  13. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/list_lv"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</LinearLayout>


adapter_item.xml

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <ImageView  
  8.         android:id="@+id/img"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         />  
  12.   
  13. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</LinearLayout>


布局很简单,不难懂。

 

其他问题:

在adapter中使用了一个HashMap叫做tag_map,它的作用是保存每个position位置的TagInfo。其实这样做在显示很少项的时候还好,但是如果有几千几万个项需要显示,则会造成大量的内存浪费。

不过使用这个方法实在是逼不得已,原来的版本,是在obtainImage方法中利用url的值来找到ImageView的,例如:

  1. holder.img.setTag(imgurl);  
  2.   
  3. public void obtainImage(){  
  4.     // ...   
  5.     ImageView tag_view = (ImageView) list_lv.findViewWithTag(imgurl);  
  6.     // ...   
  7. }  
            holder.img.setTag(imgurl);
            
            public void obtainImage(){
                // ...
                ImageView tag_view = (ImageView) list_lv.findViewWithTag(imgurl);
                // ...
            }

这样做有一个问题,例如我有两个项利用的是一个url,则在findViewWithTag(imgurl)时只会找到一个ImageView,则另外一个就没有处理了,所以会造成只有一个显示了正确的图片,另外一个显示默认的。

 

所以我进行了另外一个尝试,利用ListView的getChildAt方法得到那个位置的View,再通过getTag方法得到ViewHolder,然后再设置图片。但有时通过getChildAt()方法得到的View为null,如果不处理就崩了,就算处理了肯定也得不到预先想要的结果。查了一下网上的解释,getChildAt()方法返回的只是当前显示出来的View,对于已经隐藏掉的也就只能返回null了。所以这个方法也废了。

 

所以我又进行了另外一个尝试,还是利用Tag,但是利用position这个固定的值。后来还是报了NullPointerException,原因是setTag方法传入的参数是一个Object类型,把position这个int类型传入,会自动生成Integer对象。在findViewWithTag的时候,由于convertView的复用机制,有时会使原来设置该Tag的项,动态的设置成了一个新的Tag,这样就找不到了,所以返回的View也是空的。

没办法了,我只能先这样进行处理,以后再进一步解决这个问题。如果有朋友有方法,请告知,不胜感激。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值