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

如何阻止RecyclerView回收特定视图?解决进度条重置问题

解决RecyclerView中ProgressBar滚动重置的问题

这个问题我之前帮不少开发者处理过,核心原因就是RecyclerView的ViewHolder复用机制,加上你没给每个下载任务单独维护进度状态——滚动时旧的ViewHolder被复用给新列表项,进度自然就被重置了。结合ThinDownloadManager的特性,咱们一步步搞定:

核心原因拆解

RecyclerView为了性能会复用ViewHolder,当你滑动屏幕时,显示某条视频下载进度的ViewHolder会被分配给新的列表项。如果没有为每个下载任务单独存储进度,并且在绑定ViewHolder时正确恢复,就会出现进度被清零的情况。

具体解决方案

1. 维护全局的下载进度状态

首先得搞个容器存每个下载任务的实时进度,推荐用HashMap:键用ThinDownloadManager返回的downloadId(每个下载任务的唯一标识),值存当前进度(0-100的整数):

private HashMap<Long, Integer> downloadProgressMap = new HashMap<>();
// 如果你习惯用视频URL当标识,也可以换成HashMap<String, Integer>

2. 绑定下载回调,同步更新进度并通知刷新

启动下载时,给每个任务设置DownloadListener,在进度变化、完成或失败时更新进度Map,然后通知Adapter刷新对应列表项:

// 构建下载请求
DownloadRequest downloadRequest = new DownloadRequest(Uri.parse(videoUrl))
        .setDestinationURI(Uri.parse(savePath))
        .setPriority(DownloadRequest.Priority.HIGH)
        .setDownloadListener(new DownloadListener() {
            @Override
            public void onDownloadComplete(int id) {
                // 下载完成,进度设为100%
                downloadProgressMap.put((long) id, 100);
                // 找到对应列表位置并刷新
                int targetPosition = getPositionByDownloadId(id);
                if (targetPosition != -1) {
                    yourVideoAdapter.notifyItemChanged(targetPosition);
                }
            }

            @Override
            public void onDownloadFailed(int id, int errorCode, String errorMessage) {
                // 下载失败可以保持当前进度,也可以按需处理
                int currentProgress = downloadProgressMap.getOrDefault((long) id, 0);
                downloadProgressMap.put((long) id, currentProgress);
                int targetPosition = getPositionByDownloadId(id);
                if (targetPosition != -1) {
                    yourVideoAdapter.notifyItemChanged(targetPosition);
                }
            }

            @Override
            public void onProgress(int id, long totalBytes, long downloadedBytes) {
                // 计算当前进度百分比
                int progress = (int) ((downloadedBytes * 100) / totalBytes);
                downloadProgressMap.put((long) id, progress);
                // 刷新对应列表项
                int targetPosition = getPositionByDownloadId(id);
                if (targetPosition != -1) {
                    yourVideoAdapter.notifyItemChanged(targetPosition);
                }
            }
        });

// 添加任务到下载管理器,同时保存任务ID和列表位置的映射
long downloadId = thinDownloadManager.add(downloadRequest);
saveDownloadIdPositionPair(downloadId, currentListItemPosition); // 自己实现一个映射表来存ID和位置的对应关系

3. 在Adapter绑定ViewHolder时恢复进度

每次RecyclerView绑定ViewHolder(不管是首次加载还是复用),都从进度Map里取出当前视频的进度,设置给ProgressBar:

@Override
public void onBindViewHolder(@NonNull VideoViewHolder holder, int position) {
    VideoItem video = videoList.get(position);
    // 从进度Map里拿当前任务的进度,默认0
    int currentProgress = downloadProgressMap.getOrDefault(video.getDownloadId(), 0);
    // 设置ProgressBar进度
    holder.downloadProgressBar.setProgress(currentProgress);
    
    // 其他列表项绑定逻辑...
}

4. 额外注意事项

  • 要维护一个HashMap<Long, Integer>来存downloadId和列表位置的对应关系,这样才能在回调里准确找到要刷新的列表项。
  • 如果列表有新增、删除操作,记得同步更新这个ID-位置映射表,避免刷新时找错位置。
  • 别直接持有ViewHolder的引用,容易引发内存泄漏,用notifyItemChanged让RecyclerView重新绑定才是安全的方式。

进阶优化小技巧

  • 可以用LiveData包裹进度Map,让Adapter自动观察数据变化并刷新,代码更简洁还符合MVVM架构。
  • 页面销毁时,记得取消所有未完成的下载任务,清空进度Map和ID-位置映射表,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:24:38