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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 04:45:33