ListView异步加载图片是常见的Android开发需求之一。在加载大量图片时,如果不使用异步加载,会严重影响应用性能和用户体验。本篇文章主要讲解如何使用ListView实现异步加载图片,并对其进行优化。
实现思路
- 创建一个ViewHolder类并在其中声明ImageView控件。
- 在ListView中加入标记每一个ImageView的Tag。
- 利用LruCache存储图片,防止因重复下载浪费流量和内存。
- 利用异步线程方法如AsyncTask或者线程池加载图片。
- 在视图重绘时检查标记,阻止重复加载,加快加载速度。
代码示例
下面给出使用异步加载图片优化ListView的代码示例。
步骤一:创建ViewHolder类
public class ViewHolder {
public ImageView imageView;
}
步骤二:设置ImageView的Tag
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = inflater.inflate(R.layout.list_item, parent, false);
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.ivImage);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.imageView.setTag(position);
步骤三:使用LruCache存储图片
private LruCache<Integer, Bitmap> mMemoryCache;
private void init_lru() {
//获取最大可用内存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
//使用最大可用内存值的1/4作为缓存容量
final int cacheSize = maxMemory / 4;
mMemoryCache = new LruCache<Integer, Bitmap>(cacheSize) {
@Override
protected int sizeOf(Integer key, Bitmap bitmap) {
// 重写此方法来衡量每张图片的大小,默认返回图片数量。
return bitmap.getByteCount() / 1024;
}
};
}
public void addBitmapToMemoryCache(int key, Bitmap bitmap) {
if (getBitmapFromMemoryCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemoryCache(int key) {
return mMemoryCache.get(key);
}
步骤四:使用异步方式加载图片
private void loadImageAsyncTask(int position, ImageView imageView) {
// 加载前先尝试从缓存中寻找
final Bitmap bitmap = getBitmapFromMemoryCache(position);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
imageView.setImageResource(R.drawable.placeholder); // 显示占位图
new AsyncTask<Integer, Void, Bitmap>() {
private int mPosition;
@Override
protected Bitmap doInBackground(Integer... params) {
mPosition = params[0];
return decodeSampledBitmapFromResource(mResources, mImageIds[mPosition], mReqWidth, mReqHeight);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
addBitmapToMemoryCache(mPosition, bitmap);
ImageView imageView = (ImageView) mListView.findViewWithTag(mPosition);
if (imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
}.execute(position);
}
步骤五:阻止重复加载
在适配器getView()方法中,加上对ImageView的重复判断:
if (imageView.getTag() != null && imageView.getTag().equals(position)) {
loadImageAsyncTask(position, viewHolder.imageView);
}
在计算图片样式的时候加上:
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// 源图片的高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 取比率中的最小值作为inSampleSize的值,这样可以保证最终图片的大小一定会大于这个目标大小或者与其相等。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
// 当inJustDecodeBounds设置为true时,不会加载图片只会获取图片宽高信息。
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 计算inSampleSize的值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 将inJustDecodeBounds设置为false,真正开始加载图片。
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
总结
使用上述代码族可以实现网速较慢的情况下,ListView中图片的延迟加载和优化。如果想要更高的性能优化,可以考虑使用RecyclerView等其他优化方法进一步优化。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:ListView异步加载图片实现思路(优化篇) - Python技术站