Kotlin如何停止RecyclerView含隐藏项的所有Chronometer及修复滚动误停问题
问题根因
你遇到的滚动后计时器停止的核心问题是 计时逻辑与Chronometer视图强绑定:
- Android原生Chronometer本身依赖视图Attach到Window的状态,一旦子项滚动出屏幕被回收/Detach,Chronometer会自动停止计时更新
- 目前代码里把计时状态的更新完全依赖UI组件,无法覆盖不在屏幕内的隐藏子项
解决方案
第一步:抽离计时逻辑到数据层,完全解耦视图
不要用Chronometer本身做计时,改用全局定时任务统一更新所有激活任务的计时数据:
- 给
Task实体类加startTimeStamp字段,记录计时器启动时的SystemClock.elapsedRealtime()值,激活状态下每次计算已经过时长 = 当前时间 - startTimeStamp - 之前的pauseOffset - 在Fragment中启动一个每秒触发一次的
Coroutine/Handler定时任务,遍历所有active=true的Task,实时计算更新它的pauseOffset和显示用的timer字符串,然后通知适配器刷新可见的对应子项即可。这样就算子项不在屏幕内,计时数据也会正常更新。
第二步:修改适配器绑定逻辑,仅做数据渲染
去掉在onBindViewHolder里启动/停止Chronometer的逻辑,只需要把Task里已经计算好的timer字符串赋值给控件即可,不需要调用Chronometer的start()/stop()方法,也可以删掉holder.setIsRecyclable(false)这行,恢复RecyclerView的复用能力提升性能。
第三步:实现全局停止所有计时器的功能
直接遍历所有Task列表,把所有active=true的Task做如下处理:
- 标记
active = false - 计算最终的
timer字符串和pauseOffset - 批量更新数据库,然后调用适配器的
update()方法刷新列表即可,不需要操作任何视图组件,自然覆盖所有隐藏项。
第四步:调整点击交互逻辑
点击子项的时候只需要修改对应Task的active状态、startTimeStamp值,更新数据库即可,计时的更新由全局的定时任务统一处理,不要在点击事件里操作Chronometer。
优化后的适配器核心代码示例
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { val task = tasks[position] holder.itemView.apply { tvTitleInHome.text = task.task // 直接渲染预计算好的计时文本,不需要操作Chronometer的启停 chronometer.text = task.timer tvTitleInHome.setOnClickListener { // 仅修改数据层状态,不操作视图计时 if(!task.active){ viewFragment.stopAllOtherTimers(task.id) task.active = true task.startTimeStamp = SystemClock.elapsedRealtime() }else{ task.active = false task.pauseOffset = SystemClock.elapsedRealtime() - task.startTimeStamp + task.pauseOffset task.timer = formatDuration(task.pauseOffset) } taskViewModel.updateTask(task) viewFragment.updateMainTitle(task.task, task.timer) } } }
内容的提问来源于stack exchange,提问作者Hassan Amer
相关产品推荐
相关产品推荐

