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