Android listview异步加载图片

本文介绍了一种基于线程池、LruCache缓存和tag防错位机制的Listview图片异步加载方案。通过整合多种技术手段,有效地解决了图片加载过程中可能出现的错位问题,并提高了加载效率。

Listview异步加载图片网上有很多这方面的资料,对网上的一些解决方案做了整合,最终写了一个"线程池"+"LruCache缓存"+"tag防止错位"的demo,主要代码如下:

MainActivity.java

package com.syncloadbmp;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.widget.ListView;

public class MainActivity extends Activity {

	private ArrayList<PicItem> mPicList;
	private PicAdapter mPicAdapter;
	private ListView mListView;
	private Context mContext;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		mContext = MainActivity.this;
		mListView = (ListView) findViewById(R.id.listview);
		mPicList = new ArrayList<PicItem>();
		String[] imageArray = new String[] {
				"http://www.uuuu.cc/uploads/allimg/c120608/13391223A103P-DQ30.jpg",
				"http://c.hiphotos.baidu.com/image/pic/item/e1fe9925bc315c60163c29ee8fb1cb1349547701.jpg",
				"http://b.hiphotos.baidu.com/image/pic/item/1ad5ad6eddc451dac09faffbb7fd5266d01632b0.jpg",
				"http://d.hiphotos.baidu.com/image/pic/item/4afbfbedab64034f0b30770fadc379310a551d3d.jpg",
				"http://f.hiphotos.baidu.com/image/pic/item/9f2f070828381f306c7d2ba7ab014c086e06f05c.jpg",
				"http://g.hiphotos.baidu.com/image/pic/item/d0c8a786c9177f3e29b9a50072cf3bc79f3d5629.jpg",
				"http://h.hiphotos.baidu.com/image/pic/item/d4628535e5dde7116fdcfeffa5efce1b9d1661a9.jpg",
				"http://e.hiphotos.baidu.com/image/pic/item/2934349b033b5bb51d2a95f734d3d539b600bc3e.jpg",
				"http://g.hiphotos.baidu.com/image/pic/item/f636afc379310a551566b9fcb54543a982261073.jpg",
				"http://e.hiphotos.baidu.com/image/pic/item/241f95cad1c8a786b58c43036509c93d70cf501c.jpg",
				"http://d.hiphotos.baidu.com/image/pic/item/908fa0ec08fa513dbdbdcdd03f6d55fbb2fbd9ad.jpg",
				"http://e.hiphotos.baidu.com/image/pic/item/e850352ac65c1038b72980c5b0119313b07e8966.jpg",
				"http://d.hiphotos.baidu.com/image/pic/item/7acb0a46f21fbe096810b61c69600c338744ad6f.jpg",
				"http://f.hiphotos.baidu.com/image/pic/item/6159252dd42a2834d1c76f0259b5c9ea15cebf52.jpg",
				"http://f.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c0a3722bc85d6277f9e2ff8aa.jpg",
				"http://e.hiphotos.baidu.com/image/pic/item/e61190ef76c6a7ef3a500b28fffaaf51f3de664e.jpg",
				"http://b.hiphotos.baidu.com/image/pic/item/e1fe9925bc315c601dc62ee88fb1cb1349547761.jpg",
				"http://h.hiphotos.baidu.com/image/pic/item/6a63f6246b600c3340c26bcf184c510fd9f9a17a.jpg",
				"http://a.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f500a6f353da81cb39db3d61.jpg",
				"http://c.hiphotos.baidu.com/image/pic/item/29381f30e924b899336ecd9f6f061d950b7bf6c3.jpg",
				"http://a.hiphotos.baidu.com/image/pic/item/18d8bc3eb13533faa27e2b29aad3fd1f40345be8.jpg",
				"http://c.hiphotos.baidu.com/image/pic/item/3b87e950352ac65ce2e93f6ef9f2b21193138a3b.jpg",
				"http://d.hiphotos.baidu.com/image/pic/item/6c224f4a20a44623b247ee659a22720e0cf3d72c.jpg",
				"http://d.hiphotos.baidu.com/image/pic/item/023b5bb5c9ea15cedd54be0eb4003af33a87b27b.jpg",
				"http://e.hiphotos.baidu.com/image/pic/item/3b87e950352ac65cfc372174f9f2b21193138a83.jpg",
				"http://c.hiphotos.baidu.com/image/pic/item/b151f8198618367a583cc3aa2c738bd4b31ce514.jpg",
				"http://b.hiphotos.baidu.com/image/pic/item/8694a4c27d1ed21b5f1c9bf8af6eddc450da3fd1.jpg",
				"http://d.hiphotos.baidu.com/image/pic/item/3bf33a87e950352ab532fa045143fbf2b2118b6f.jpg",
				"http://b.hiphotos.baidu.com/image/pic/item/d0c8a786c9177f3e5682407a72cf3bc79f3d5616.jpg" };
		for (int i = 0; i < imageArray.length; i++) {
			PicItem item = new PicItem();
			item.mImageUrl = imageArray[i];
			mPicList.add(item);
		}
		mPicAdapter = new PicAdapter(mContext, mPicList);
		mListView.setAdapter(mPicAdapter);
	}

}
PicAdapter.java

package com.syncloadbmp;

import java.util.ArrayList;

import com.syncloadbmp.BitmapLoader.DisplayImageListener;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;

public class PicAdapter extends BaseAdapter {
	private ArrayList<PicItem> mPicList;
	private Context mContext;

	public PicAdapter(Context context, ArrayList<PicItem> list) {
		mContext = context;
		mPicList = list;
	}

	@Override
	public int getCount() {
		return mPicList.size();
	}

	@Override
	public Object getItem(int position) {
		return null;
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Holder holder = null;
		if (convertView == null) {
			Log.v("YAN-G", "new view");
			convertView = View.inflate(mContext, R.layout.pic_item, null);
			holder = new Holder();
			holder.mImageView = (ImageView) convertView
					.findViewById(R.id.image);
			convertView.setTag(holder);
		}
		// 使用holder能够避免每次都做findViewById的操作
		holder = (Holder) convertView.getTag();
		final PicItem item = mPicList.get(position);
		holder.mImageView.setTag(item.mImageUrl);
		// -- start 不加这段代码图片加载时会错位,后面会刷新恢复,但有延迟
		Bitmap bmp = BitmapLoader.getInstance().getBitmapFromMemCache(
				item.mImageUrl);
		if (bmp != null) {
			holder.mImageView.setBackgroundDrawable(new BitmapDrawable(bmp));
		} else {
			holder.mImageView.setBackgroundResource(R.drawable.ic_launcher);
		}
		// -- end
		BitmapLoader.getInstance().loadBitmap(item.mImageUrl,
				holder.mImageView, new DisplayImageListener() {

					@Override
					public void showImage(ImageView image, Bitmap bmp) {
						// 不加上这句,上下滚动过快或者网络比较慢的时候就会出现图片错位的问题
						// 因为listview会换成view,无论向上还是向下列表里引用的还是同一个view,
						// 图片加载又是异步的因此会有错位的情况,但如果每次都更新view的tag,这样就能保证
						// 引用的view所标记的tag是最新的,就不会错位
						if (item.mImageUrl.equals(image.getTag())) {
							image.setBackgroundDrawable(new BitmapDrawable(bmp));
						}
					}
				});

		return convertView;
	}

	class Holder {
		public ImageView mImageView;
	}
}

BitmapLoader.java

package com.syncloadbmp;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;

public class BitmapLoader {
	private static BitmapLoader mBitmapLoader;
	private ExecutorService mThreadService;
	private LruCache<String, Bitmap> mBmpCache;

	public static BitmapLoader getInstance() {
		if (mBitmapLoader == null) {
			mBitmapLoader = new BitmapLoader();
		}
		return mBitmapLoader;
	}

	public BitmapLoader() {
		mThreadService = Executors.newFixedThreadPool(6);
		int maxMemory = (int) Runtime.getRuntime().maxMemory();
		int cacheSize = maxMemory / 4;
		// 给LruCache分配1/8 4M
		mBmpCache = new LruCache<String, Bitmap>(cacheSize) {
			@Override
			protected int sizeOf(String key, Bitmap value) {
				// TODO Auto-generated method stub
				return value.getRowBytes() * value.getHeight();
			}
		};
	}

	public void addBitmapToCache(String key, Bitmap bmp) {
		if (getBitmapFromMemCache(key) == null && bmp != null) {
			mBmpCache.put(key, bmp);
		}
	}

	public Bitmap getBitmapFromMemCache(String key) {
		return mBmpCache.get(key);
	}

	public void loadBitmap(final String url, final ImageView image,
			final DisplayImageListener listener) {
		final Handler handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				// TODO Auto-generated method stub
				super.handleMessage(msg);
				listener.showImage(image, (Bitmap) msg.obj);
			}
		};
		Bitmap bmp = getBitmapFromMemCache(url);
		if (bmp != null) {
			Log.v("YAN-G", "not null");
			listener.showImage(image, bmp);
			return;
		}
		mThreadService.execute(new Runnable() {

			@Override
			public void run() {
				Log.v("YAN-G", "load bmp from net");
				Bitmap bitmap = getBitmapFormUrl(url, true, 100, 80);
				Message msg = Message.obtain();
				msg.obj = bitmap;
				handler.sendMessage(msg);
				addBitmapToCache(url, bitmap);
			}
		});
	}

	public void destroy() {
		mThreadService.shutdown();
	}

	public Bitmap getBitmapFormUrl(String url, boolean isCompress, float f,
			float g) {
		URL myFileUrl = null;
		Bitmap bitmap = null;
		if (null == url || url.equals("") || url.endsWith(".com")) {
			return null;
		}
		try {
			myFileUrl = new URL(url);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		try {
			HttpURLConnection conn = (HttpURLConnection) myFileUrl
					.openConnection();
			conn.setDoInput(true);
			conn.connect();
			InputStream is = conn.getInputStream();
			if (null != is && is.available() >= 0) {
				if (isCompress) {
					BitmapFactory.Options options = new BitmapFactory.Options();
					options.inJustDecodeBounds = true;
					// 获取这个图片的宽和高
					bitmap = BitmapFactory.decodeStream(is, null, options);
					options.outWidth = (int) f;
					options.outHeight = (int) g;
					options.inSampleSize = (int) (options.outWidth / f);
					options.inPreferredConfig = Bitmap.Config.RGB_565;
					options.inJustDecodeBounds = false;
					options.inPurgeable = true;// 设置图片可以被回收
					conn.disconnect();
					is.close();

					conn = (HttpURLConnection) myFileUrl.openConnection();
					conn.setDoInput(true);
					conn.connect();
					is = conn.getInputStream();
					bitmap = BitmapFactory.decodeStream(is, null, options);
				} else {
					bitmap = BitmapFactory.decodeStream(is);
				}
				conn.disconnect();
				is.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return bitmap;

	}

	public interface DisplayImageListener {
		public void showImage(ImageView image, Bitmap bmp);
	}
}
PicItem.java

package com.syncloadbmp;

public class PicItem {
	public String mImageUrl;
	public String mTitle;

}
main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </ListView>

</LinearLayout>

pic_item.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/image"
        android:layout_width="100dp"
        android:layout_height="60dp"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:layout_margin="5dp"
        android:layout_marginLeft="10dp" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dp" />

</RelativeLayout>


源码下载: SyncLoadBitmap

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值