如何阻止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
相关产品推荐
相关产品推荐

