Android ListView行内ProgressBar并行下载进度更新错位问题
ListView下载进度更新错位修复方案
问题本质
ListView的position是数据项在当前数据源列表里的动态位置,会随着列表项增删、重排实时变化;同时ListView的View回收机制会导致你持有的行View、ProgressBar引用随时被复用到其他位置的列表项上,这就是你直接传position、传View引用、用HashMap存位置都失效的根本原因——你绑定的标识都是会动态变化的临时值,不是列表项的稳定唯一标识。
可直接落地的修复步骤
- 首先修改你的
UpdateItem数据模型:
给每个UpdateItem加一个全局唯一、不会随列表变化的标识字段,比如用下载链接的哈希值、应用包名当stableId;再加一个整型字段progress存当前下载进度,初始值设为0。 - 改造AsyncTask的传参逻辑:
点击下载启动任务时,不要给AsyncTask传position、不要传行View/ProgressBar引用,只传当前点击项对应的stableId和下载地址。 - 改造进度更新、下载完成回调逻辑:
- 收到进度回调时,遍历适配器的数据源
ArrayList<UpdateItem>,找到stableId和当前任务匹配的项,更新它的progress字段为最新进度值 - 如果要优化性能,可以先判断这个项当前是否在可视区域内:如果可见,就找到对应的可见行View更新ProgressBar进度;如果不可见,只更新数据字段即可,不需要额外操作
- 下载完成时,同样通过
stableId匹配到对应的数据项,从数据源列表中移除该项,调用notifyDataSetChanged()刷新列表
- 收到进度回调时,遍历适配器的数据源
- 改造
getView绑定逻辑:
每次绑定列表项时,直接从当前position对应的UpdateItem实例中读取progress值,设置给ViewHolder里的ProgressBar,不要从其他外部引用拿进度值。
之前方案失效的原因
- 直接传ProgressBar/行View引用失效:ListView的回收机制会在滚动、增删项时把已滑出屏幕、被删除项的View复用到其他位置,你持有的View实例最终对应的已经不是原来的下载项,更新自然错位
- HashMap存position失效:position本身是动态值,只要列表前面的项被删除,后续所有项的position都会往前移,你存在Map里的旧position和实际位置无法对应,本质还是用动态值当唯一标识。
内容的提问来源于stack exchange,提问作者itapi
相关产品推荐
相关产品推荐

