You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

正确下载图片并更新RecyclerView ViewHolder的方法咨询

正确实现FirestoreRecyclerAdapter加载Firebase Storage图片的方案

问题背景

我通过FirestoreRecyclerOptions向FirestoreRecyclerAdapter传递数据,在RecyclerView中展示内容,但所需图片存储在Firebase Storage中(与Firestore文档相互独立)。目前已实现图片下载并在ViewHolder中设置,但认为当前实现方式存在问题,希望得到正确方案指导。

当前onBindViewHolder实现代码:

@Override
protected void onBindViewHolder(@NonNull MyListHolder holder, int position, @NonNull TodoItem model) {
    // Get the 'to do' image from storage using the stored image reference from document.
    StorageReference imageReference = storageRef.child(model.getImageReference());
    ImageTransfer.imageDownload(this, holder, imageReference);

    holder.description.setText(model.getDescription());

    if (model.getMarkedDone().equals("true")) {
        holder.markedDone.setImageResource(R.drawable.mark_done_icon);
    }
    else {
        holder.markedDone.setImageResource(R.drawable.mark_not_done_icon);
    }
}

当前图片下载工具方法:

static public void imageDownload(FirestoreRecyclerAdapter<?, ?> firestoreRecyclerAdapter, RecyclerView.ViewHolder holder, StorageReference imageStorageRef) {
    final long ONE_MEGABYTE = 1024 * 1024;
    imageStorageRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
        @Override
        public void onSuccess(byte[] bytes) {
            Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
            ((DownloadCompleteCallback) firestoreRecyclerAdapter).downloadComplete(bitmap, holder);
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception exception) {

        }
    });
}

当前实现的核心问题

  • 代码耦合度高:将FirestoreRecyclerAdapter和ViewHolder传递到外部工具类,工具类依赖Adapter的回调接口,扩展性差;
  • 未处理ViewHolder复用问题:RecyclerView滑动时ViewHolder会被复用,旧的下载任务完成后会覆盖新位置的图片,导致显示错误;
  • 无缓存机制:每次滑动都会重新下载图片,浪费流量与性能。

优化方案

方案一:使用第三方图片加载库(推荐)

直接用Glide、Fresco这类成熟的图片加载库,它们自带缓存管理、线程调度、ViewHolder复用处理,能大幅简化代码并提升性能。以Glide为例:

  1. 添加Glide依赖(如果未添加)
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
  1. 改造onBindViewHolder
@Override
protected void onBindViewHolder(@NonNull MyListHolder holder, int position, @NonNull TodoItem model) {
    // 设置文本与完成状态图标
    holder.description.setText(model.getDescription());
    holder.markedDone.setImageResource(model.getMarkedDone().equals("true") 
        ? R.drawable.mark_done_icon 
        : R.drawable.mark_not_done_icon);

    // 加载Storage图片
    StorageReference imageReference = storageRef.child(model.getImageReference());
    Glide.with(this)
         .load(imageReference)
         .placeholder(R.drawable.placeholder_image) // 加载中占位图
         .error(R.drawable.error_image) // 加载失败占位图
         .into(holder.imageView); // 替换为你的ImageView实例
}

方案二:手动实现下载逻辑(不推荐,仅作学习参考)

如果必须自己实现下载逻辑,需解决ViewHolder复用与耦合问题:

  1. 改造ViewHolder,添加任务管理逻辑
public class MyListHolder extends RecyclerView.ViewHolder {
    public ImageView imageView;
    public TextView description;
    public ImageView markedDone;
    private Task<byte[]> currentDownloadTask; // 记录当前下载任务

    public MyListHolder(@NonNull View itemView) {
        super(itemView);
        imageView = itemView.findViewById(R.id.item_image);
        description = itemView.findViewById(R.id.item_description);
        markedDone = itemView.findViewById(R.id.item_mark_done);
    }

    // 取消当前未完成的下载任务,避免复用错误
    public void cancelCurrentDownload() {
        if (currentDownloadTask != null && !currentDownloadTask.isComplete()) {
            currentDownloadTask.cancel();
        }
    }
}
  1. 修改onBindViewHolder实现
@Override
protected void onBindViewHolder(@NonNull MyListHolder holder, int position, @NonNull TodoItem model) {
    // 先取消旧下载任务,防止滑动时旧回调覆盖新数据
    holder.cancelCurrentDownload();

    // 设置文本与状态图标
    holder.description.setText(model.getDescription());
    holder.markedDone.setImageResource(model.getMarkedDone().equals("true") 
        ? R.drawable.mark_done_icon 
        : R.drawable.mark_not_done_icon);

    // 执行图片下载
    StorageReference imageReference = storageRef.child(model.getImageReference());
    final long ONE_MEGABYTE = 1024 * 1024;
    holder.currentDownloadTask = imageReference.getBytes(ONE_MEGABYTE)
        .addOnSuccessListener(bytes -> {
            // 检查当前ViewHolder位置是否匹配模型,避免复用错误
            if (getItem(holder.getAdapterPosition()).equals(model)) {
                Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                holder.imageView.setImageBitmap(bitmap);
            }
        })
        .addOnFailureListener(e -> {
            // 下载失败时设置错误图
            holder.imageView.setImageResource(R.drawable.error_image);
        });
}

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 07:47:37