RecyclerView中添加多不同剩余时长倒计时出现闪烁,如何解决?
解决RecyclerView多倒计时组件闪烁问题
你的代码出现闪烁的核心原因是:ViewHolder复用时,旧的CountDownTimer没有被停止,多个Timer同时更新同一个TextView,导致UI频繁跳变;且Timer的状态没有和列表数据绑定,复用后时间计算混乱。以下是具体解决方案:
1. 给ViewHolder添加Timer引用,绑定前先停止旧Timer
每次绑定新数据时,先取消ViewHolder中已存在的Timer,避免多个Timer同时操作同一个View。同时在ViewHolder销毁时取消Timer,防止内存泄漏。
2. 让倒计时数据和列表项绑定
把剩余秒数存在数据模型里,Timer触发时更新数据源的数值,再同步到UI。这样ViewHolder复用时,能直接拿到最新的剩余时间,避免数值跳变。
3. 优化Timer创建逻辑
只在需要时创建新Timer,减少不必要的对象创建,同时确保Timer启动后能正确关联当前列表项的数据。
修改后的完整代码示例
首先定义数据模型
data class CountDownItem(var secondsRemaining: Long)
优化后的Adapter代码
class MyAdapter(private val itemList: MutableList<CountDownItem>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val binding = MyItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) return MyViewHolder(binding) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val item = itemList[position] // 先显示当前最新的剩余秒数 holder.binding.tvcountdown.text = item.secondsRemaining.toString() // 停止旧Timer,避免复用冲突 holder.timer?.cancel() // 创建并启动新Timer,绑定当前数据 holder.timer = object : CountDownTimer(item.secondsRemaining * 1000, 1000) { override fun onTick(millisUntilFinished: Long) { val secLeft = millisUntilFinished / 1000 // 更新数据源的剩余秒数 item.secondsRemaining = secLeft holder.binding.tvcountdown.text = secLeft.toString() } override fun onFinish() { // 倒计时结束后的自定义逻辑,比如重置时间 item.secondsRemaining = 60 this.start() } }.start() } override fun getItemCount() = itemList.size class MyViewHolder(val binding: MyItemBinding) : RecyclerView.ViewHolder(binding.root) { // 保存Timer引用,用于后续取消 var timer: CountDownTimer? = null // ViewHolder从窗口移除时,取消Timer override fun onDetachedFromWindow() { super.onDetachedFromWindow() timer?.cancel() } } }
额外优化:统一管理所有倒计时(适合大量列表项)
如果列表项数量很多,每个Item一个Timer会消耗过多资源。可以用协程实现全局定时器,统一更新所有倒计时数据,再刷新对应Item:
class MyAdapter(private val itemList: MutableList<CountDownItem>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() { private val scope = CoroutineScope(Dispatchers.Main) private var timerJob: Job? = null init { // 启动全局定时任务,每秒更新一次 timerJob = scope.launch { while (isActive) { delay(1000) itemList.forEachIndexed { index, item -> if (item.secondsRemaining > 0) { item.secondsRemaining -= 1 notifyItemChanged(index) } else { item.secondsRemaining = 60 } } } } } // Adapter销毁时取消协程 fun destroy() { timerJob?.cancel() scope.cancel() } // 其余onCreateViewHolder、onBindViewHolder等代码同上 }
这种方式所有倒计时共用一个定时器,彻底避免多Timer冲突,同时降低资源消耗。
内容的提问来源于stack exchange,提问作者Alon Rech
相关产品推荐
相关产品推荐

