正确下载图片并更新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为例:
- 添加Glide依赖(如果未添加)
implementation 'com.github.bumptech.glide:glide:4.16.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
- 改造
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复用与耦合问题:
- 改造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(); } } }
- 修改
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
相关产品推荐
相关产品推荐

