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

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

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

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

下面是具体的步骤:

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

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(){@Overridepublic 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() {@Overridepublic 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

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[]{"%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","%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","%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","%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","%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","%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","%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","%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","%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","%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","%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","%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","%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","%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","%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. */@Overridepublic 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>();}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn mList.size();}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn mList.get(position);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubViewHolder 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);// 保存当前项所要加载的urlholder.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() {@Overridepublic void obtainImage(TagInfo ret_info) {imgCache.put(ret_info.getUrl(), ret_info.getDrawable()); // 首先把获取的图片放入到缓存中// 通过返回的TagInfo去Tag缓存中找,然后再通过找到的Tag来获取到所对应的ImageViewImageView 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;}}}

朋友,为了幸福,请你保持一副热爱生活的心肠,

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

相关文章:

你感兴趣的文章:

标签云: