在 recyclerview 中管理多个 timers/handlers

Managing multiple timers/handlers in recyclerview

我有一个回收站视图,我在其中列出了不同的作业,每个作业都有一个进度条,因为您可以一次执行多个作业。我目前正在使用一个处理程序来减少我的进度条,但它一次只适用于一项工作。我希望它被绑定到一个位置而不是完整的回收器视图,所以我可以一次有多个处理程序 运行。 我如何更新我的适配器,以便处理程序(或另一个计时器,如果它能更好地工作)特定于 [position] 而不是完整的回收器视图?

class JobAdapter(
    private val context: Context,
    private val dataset: List<JobList>
) : RecyclerView.Adapter<JobAdapter.ItemViewHolder>() {

    class ItemViewHolder(private val view: View): RecyclerView.ViewHolder(view){
        val tvJobName = view.findViewById<TextView>(R.id.tvJobName)
        val tvJobTime = view.findViewById<TextView>(R.id.tvJobTime)
        val tvJobReward = view.findViewById<TextView>(R.id.tvJobReward)
        val pbJobTime = view.findViewById<ProgressBar>(R.id.pbJobTime)
        val btnStartJob = view.findViewById<Button>(R.id.btnStartJob)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val jobLayout = LayoutInflater.from(parent.context)
            .inflate(R.layout.job_layout, parent, false)
        return ItemViewHolder(jobLayout)
    }

    var currentProgress: Int = 0
    var counter: Int=0
    var isTriggered: Boolean = false

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {

        val item = dataset[position]
        holder.tvJobName.text = item.dcJobName
        holder.tvJobTime.text = item.dcJobTime.toString()+"s"
        holder.tvJobReward.text = "$"+item.dcJobReward.toString()

        val maxAllowable=25
        val mills = (1000/maxAllowable)*item.dcJobTime

        holder.btnStartJob.setOnClickListener{

            val jobHandler = Handler()
            val jobRunnable: Runnable = object : Runnable {
                override fun run() {
                    counter++
                    if (counter<=maxAllowable){
                        currentProgress-=100/maxAllowable
                        holder.pbJobTime.progress = currentProgress
                        jobHandler.postDelayed(this, mills.toLong())
                    } else {
                        counter = 0
                        isTriggered=false
                    }
                }
            }
            if (!isTriggered) {
                currentProgress=100
                holder.pbJobTime.progress = currentProgress
                isTriggered=true
                jobHandler.post(jobRunnable)
            }

        }
    }

    override fun getItemCount() = dataset.size }

RecyclerView 的要点是您有少量 ViewHolders 可以重复使用(回收)以显示列表中的所有项目。因此在任何给定时间,一个特定的 VH 可以代表任意项目。但是您的可运行对象(绑定到 specific 项目)正在更新 specific ViewHolder - 所以即使它实际上没有显示该项目,它也会更新该项目的进度。

所以您正在寻找解决该问题的方法,对吗?您可以尝试找出特定 VH 是否正在显示该特定项目,如果是则更新它 - 但这非常混乱,可能会变得非常复杂(您不会取消现有的可运行对象,对吧?)并且您可能会遇到一些尴尬的错误.


我个人建议将其分解为几个独立的部分:

  • 您的数据和基本的回收者视图
  • 包含 运行 个项目及其当前进度状态的东西
  • 像您的 Runnable 一样经常“滴答”,更新进度状态,并告诉 RecyclerView 更新和显示当前进度

“当前进度状态”可能只是项目 -> 进度的映射。当你开始一项工作时,将该项目及其初始进度添加到地图中(这将清除所有当前进度,如果它不是你想要的,你可以解决它)。启动处理程序(如果尚未启动)运行

Handler 的 runnable 将遍历地图,更新每个项目的进度,如果过期则将其从地图中删除,并在 Adapter 上调用 notifyItemChanged(因此它会被重绘目前的进展)。理想情况下,如果地图为空(即没有要继续更新的工作),处理程序将停止。

您在 onBindViewHolder 中的显示代码可以只检查该地图,查看它显示的项目是否有进度值,如果有则显示,否则显示“无 运行 作业”状态(请记住,您需要更新所有内容,因为 ViewHolder 之前可能一直在显示其他状态)


我希望这是有道理的 - 让一个 Runnable 任务经常滴答作响并更新当前状态比多个 Runnable 做自己的事情更容易管理。拥有关于您的作业及其状态的单一数据源可以更轻松地更新它们(并执行诸如保持该状态之类的事情)。并在必要时让 RecyclerView 更新自己的显示,通过引用数据源,可以更轻松地管理显示的数据,而不用担心内部细节,比如哪些 ViewHolder 对象是可见的和他们目前代表哪个项目