Android ListView和GridView异步加载图片

Android ListView和GridView异步加载图片

一般大家在加载图片的时候都是从网络或者SD卡硬盘上,这种操作是耗时操作,是不能在UI线程进行的所以需要异步加载图片,这样我们就会用到Android的异步任务AsyncTask类。不会使用这个的可以去网上百度。

但是当我们使用ListView的时候,加载Item里面的图片就会出问题了。因为ListView的item是随着我们手指滑动,item的在屏幕上的显示和消失而回收利用。举个例子:加入现在我们的屏幕最多可以显示8个listview,当我们向上滑动的时候,第一个Item会消失,这个item不会被垃圾回收器回收,而是被用来显示第九个Item(这样我们就可以减少创建Item对象的开销),当第一个图片出现的时候会打开一个异步任务我们暂且就叫他TASK1,在TASK1还未加载完成的时候我们将ListView向下滑这个时候Item1消失被用来显示Item9,这个时候又启动了一个任务TASK9,这两个Item使用的是同一个View对象那么就会同时有两个TASK在操纵这个对象。这样就有可能在Item9上显示Item1的图片,在Item1上显示Item9的图片。也就是我们开发中可能遇到的ListView图片错乱。

针对这个问题,官方文档给了一种解决办法大致就是下面这样:

Created with Raphaël 2.1.0 Item显示 将Item的ImageView和Task绑定,并开始进行异步加载 用户手指滑动使得旧的Item划出屏幕新的Item进入屏幕 检查新的Item所使用的View是否已经开启了一个Task如果已经开启,关闭旧的Task,开启新的Task并和Item的Imageview绑定 End

这样就不会出现ListView图片乱序的问题了。
下面我们看一下官方文档给出的代码(删去了一些关系不大的代码)。

class BitmapWorkerTask extends AsyncTask<URL, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private URL data = null;

public BitmapWorkerTask(ImageView imageView) {

    imageViewReference = new WeakReference<ImageView>(imageView);
}


@Override
protected Bitmap doInBackground(URL... params) {
    data = params[0];
    return ImageUtils.getBitmap(data);
}


@Override
protected void onPostExecute(Bitmap bitmap) {
    if (imageViewReference != null && bitmap != null) {
        final ImageView imageView = imageViewReference.get();
        if (imageView != null) {
            imageView.setImageBitmap(bitmap);
        }
    }
}}

这段代码是一个异步加载图片的任务。

static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

public AsyncDrawable(Resources res, Bitmap bitmap,
        BitmapWorkerTask bitmapWorkerTask) {
    super(res, bitmap);
    bitmapWorkerTaskReference =
        new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}

public BitmapWorkerTask getBitmapWorkerTask() {
    return bitmapWorkerTaskReference.get();
}}

这里复写了一个Drawable类AsyncDrawable,我们可以在它的构造函数里面传入异步加载任务bitmapWorkerTask。

public void loadBitmap(int resId, ImageView imageView) {
if (cancelPotentialWork(resId, imageView)) {
    final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
    final AsyncDrawable asyncDrawable =
            new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
    imageView.setImageDrawable(asyncDrawable);
    task.execute(resId);
}}

这里new出来一个BitmapWorkerTask,然后把BitmapWorkerTask放到上面的AsyncDrawable对象里面然后调用imageView.setImageDrawable(asyncDrawable);这样ImageView就和BitmapWorkerTask绑定到一起了,然后开始执行Task也就是开始加载图片。

public static boolean cancelPotentialWork(int data, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

if (bitmapWorkerTask != null) {
    final URL bitmapData = bitmapWorkerTask.data;
     if (bitmapData == 0 || bitmapData != data) {

        bitmapWorkerTask.cancel(true);
    } else {

        return false;
    }
}

return true;}

这段代码是上面那段代码的一函数,如果现在已经有了一个Task并且和我们将要执行的那个Task不一样的话,我们就取消原来的Task。

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...

@Override
protected void onPostExecute(Bitmap bitmap) {
    if (isCancelled()) {
        bitmap = null;
    }

    if (imageViewReference != null && bitmap != null) {
        final ImageView imageView = imageViewReference.get();
        final BitmapWorkerTask bitmapWorkerTask =
                getBitmapWorkerTask(imageView);
        if (this == bitmapWorkerTask && imageView != null) {
            imageView.setImageBitmap(bitmap);
        }
    }
}
}

最后在Task执行完毕之后,在onPostExecute里面得到Bitmap并且设置到imageView里面就好了。如果看不懂的话就再看几遍上面的流程图,流程图就是全部过程

最后总结一下:把View比作一个人的话,Item1就是他白天的职业——软件工程师,Item9就是他晚上的职业——出租车司机。白天的时候只敲代码(Task1),晚上的时候他就是出租车司机了,脑子里不去想代码(关闭Task1)开始集中精力开车(开启Task9)。这样他就不会感到职业混乱啦~~你是不是也明白了呢 ^_^。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用 ListView 异步加载图片时,可以通过以下步骤实现: 1. 创建一个自定义的适配器(Adapter)类,继承自 BaseAdapter。这个适配器将负责管理数据和视图的绑定。 2. 在适配器中,创建一个内部类 ViewHolder,用于保存列表项中的视图引用。这个类将包含一个 ImageView 用于显示图片。 3. 在适配器的 getView 方法中,获取当前列表项的数据,并更新 ViewHolder 中的 ImageView。 4. 在更新 ImageView 时,可以使用异步加载图片的第三方库,如 Glide 或 Picasso。这些库提供了简单的接口来加载网络图片,并且处理了图片的缓存和压缩等问题。 下面是一个简单的示例代码: ```java public class CustomAdapter extends BaseAdapter { private List<String> imageUrlList; private Context context; public CustomAdapter(Context context, List<String> imageUrlList) { this.context = context; this.imageUrlList = imageUrlList; } @Override public int getCount() { return imageUrlList.size(); } @Override public Object getItem(int position) { return imageUrlList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false); viewHolder = new ViewHolder(); viewHolder.imageView = convertView.findViewById(R.id.image_view); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } String imageUrl = imageUrlList.get(position); // 使用 Glide 异步加载图片 Glide.with(context) .load(imageUrl) .placeholder(R.drawable.placeholder_image) // 加载中显示的占位图 .error(R.drawable.error_image) // 加载失败显示的错误图 .into(viewHolder.imageView); return convertView; } static class ViewHolder { ImageView imageView; } } ``` 在上述示例代码中,CustomAdapter 是自定义的适配器类,其中的 getView 方法中使用了 Glide 来异步加载图片。你可以将 imageUrlList 替换为你自己的图片地址列表,并根据需要修改加载中和加载失败时显示的图片资源。同时,记得在布局文件中定义好 ImageView 的 id。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值