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

如何使用DiffUtil更新RecyclerView中Firebase文件上传的进度等状态数据

修改实现步骤

1. 完善FileUpdate模型类

确保该类包含所有需要存储的字段,以及用于DiffUtil匹配的唯一标识,建议用枚举定义上传状态避免硬编码常量:

public class FileUpdate {
    // 唯一标识,建议用文件本地绝对路径,用于区分不同条目
    private String fileId;
    private String fileName;
    // 上传进度范围 0-100
    private int uploadProgress;
    private UploadStatus uploadStatus;
    // 可选:存储文件本地路径,后续上传逻辑需要用到
    private String localFilePath;

    public enum UploadStatus {
        PENDING, // 待上传
        UPLOADING, // 上传中
        SUCCESS, // 上传成功
        FAILED // 上传失败
    }

    // 补充全参构造方法、所有字段的getter、setter方法
    // Java请重写equals和hashCode方法,Kotlin可直接用data class
}

2. 补全适配器的DiffUtil配置

你的AdminUploadFileAdapter建议继承ListAdapter,该类内置DiffUtil处理逻辑,无需手动维护列表引用:

public class AdminUploadFileAdapter extends ListAdapter<FileUpdate, AdminUploadFileAdapter.ViewHolder> {
    // DiffUtil匹配规则
    public static final DiffUtil.ItemCallback<FileUpdate> DIFF_CALLBACK = new DiffUtil.ItemCallback<FileUpdate>() {
        @Override
        public boolean areItemsTheSame(@NonNull FileUpdate oldItem, @NonNull FileUpdate newItem) {
            // 用唯一ID判断是否为同一个文件条目
            return oldItem.getFileId().equals(newItem.getFileId());
        }

        @Override
        public boolean areContentsTheSame(@NonNull FileUpdate oldItem, @NonNull FileUpdate newItem) {
            // 所有展示字段相同才不刷新条目
            return oldItem.getFileName().equals(newItem.getFileName())
                    && oldItem.getUploadProgress() == newItem.getUploadProgress()
                    && oldItem.getUploadStatus() == newItem.getUploadStatus();
        }

        // 可选:实现局部刷新,仅更新变化的控件,提升性能
        @Nullable
        @Override
        public Object getChangePayload(@NonNull FileUpdate oldItem, @NonNull FileUpdate newItem) {
            Bundle payload = new Bundle();
            if (!oldItem.getFileName().equals(newItem.getFileName())) {
                payload.putString("KEY_FILE_NAME", newItem.getFileName());
            }
            if (oldItem.getUploadProgress() != newItem.getUploadProgress()) {
                payload.putInt("KEY_PROGRESS", newItem.getUploadProgress());
            }
            if (oldItem.getUploadStatus() != newItem.getUploadStatus()) {
                payload.putSerializable("KEY_STATUS", newItem.getUploadStatus());
            }
            return payload.isEmpty() ? null : payload;
        }
    };

    public AdminUploadFileAdapter() {
        super(DIFF_CALLBACK);
    }

    // 全量绑定条目逻辑
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        FileUpdate item = getItem(position);
        holder.fileNameTv.setText(item.getFileName());
        holder.progressBar.setProgress(item.getUploadProgress());
        switch (item.getUploadStatus()) {
            case PENDING:
                holder.statusTv.setText("待上传");
                break;
            case UPLOADING:
                holder.statusTv.setText("上传中");
                break;
            case SUCCESS:
                holder.statusTv.setText("上传成功");
                break;
            case FAILED:
                holder.statusTv.setText("上传失败");
                break;
        }
    }

    // 局部刷新逻辑
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
        if (payloads.isEmpty()) {
            super.onBindViewHolder(holder, position, payloads);
            return;
        }
        Bundle changePayload = (Bundle) payloads.get(0);
        for (String key : changePayload.keySet()) {
            switch (key) {
                case "KEY_FILE_NAME":
                    holder.fileNameTv.setText(changePayload.getString(key));
                    break;
                case "KEY_PROGRESS":
                    holder.progressBar.setProgress(changePayload.getInt(key));
                    break;
                case "KEY_STATUS":
                    FileUpdate.UploadStatus status = (FileUpdate.UploadStatus) changePayload.getSerializable(key);
                    // 按状态更新状态文本即可
                    break;
            }
        }
    }

    // 原有ViewHolder、onCreateViewHolder逻辑无需大幅修改
    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView fileNameTv, statusTv;
        ProgressBar progressBar;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            // 控件绑定逻辑保持原有即可
        }
    }
}

3. 修改UploadActivity逻辑

不再维护三个独立列表,仅需维护一个List<FileUpdate>类型的数据源即可:

新增文件逻辑(选完文件后调用)

// 全局维护的数据源
private List<FileUpdate> fileList = new ArrayList<>();
private AdminUploadFileAdapter adapter;

// 选中单个文件后执行
File selectedFile = // 你选中的文件实例
FileUpdate newFile = new FileUpdate(
    selectedFile.getAbsolutePath(), // fileId用本地路径保证唯一
    selectedFile.getName(),
    0,
    FileUpdate.UploadStatus.PENDING,
    selectedFile.getAbsolutePath()
);
fileList.add(newFile);
// 必须传入新的列表实例,DiffUtil才能检测到变化
adapter.submitList(new ArrayList<>(fileList));

上传进度更新逻辑

// 你已有的Firebase上传进度监听
String currentFileId = // 当前上传的文件路径,用于匹配对应条目
int newProgress = // 回调返回的最新进度
for (int i = 0; i < fileList.size(); i++) {
    if (fileList.get(i).getFileId().equals(currentFileId)) {
        FileUpdate updateFile = fileList.get(i);
        updateFile.setUploadProgress(newProgress);
        updateFile.setUploadStatus(FileUpdate.UploadStatus.UPLOADING);
        fileList.set(i, updateFile);
        break;
    }
}
// 切到主线程提交新列表
runOnUiThread(() -> adapter.submitList(new ArrayList<>(fileList)));

上传结果更新逻辑

// 上传成功/失败回调
String currentFileId = // 当前上传的文件路径
boolean isSuccess = // 上传结果
for (int i = 0; i < fileList.size(); i++) {
    if (fileList.get(i).getFileId().equals(currentFileId)) {
        FileUpdate updateFile = fileList.get(i);
        updateFile.setUploadStatus(isSuccess ? FileUpdate.UploadStatus.SUCCESS : FileUpdate.UploadStatus.FAILED);
        if (isSuccess) updateFile.setUploadProgress(100);
        fileList.set(i, updateFile);
        break;
    }
}
runOnUiThread(() -> adapter.submitList(new ArrayList<>(fileList)));

注意事项

  • 所有submitList操作必须在主线程执行,Firebase的异步回调默认在子线程,需手动切线程
  • 每次提交列表必须传入新的列表实例,不能直接传入原有fileList引用,否则DiffUtil会认为新旧列表相同,不会触发刷新
  • 每个文件的进度、结果回调必须对应正确的fileId,避免更新错条目

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 20:06:04