功能:
1.包含计时器的 RecyclerView 列表
2.支持刷新和加载更多(分页加载)
效果图:
实现思路:
内部维护一个 map, key 为 adapter 绑定数据模型的唯一 id, value 为绑定的数据模型,
map 保存需要更新倒计时的 model,
启动一个定时器, 每个间隔周期遍历一次 map, 取出 map 中数据, 更新 model 的倒计时属性, 最后局部更新 item.
主要代码 adapter :
package com.example.recyclertimerdemo
import android.util.ArrayMap
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.util.*
import java.util.concurrent.TimeUnit
private const val COUNT_DOWN_TIME = 30
/**
* @Author: qqyang
* @Date: 2022/6/8
* @Description:
* <p>
* 内部维护一个 map, key 为 adapter 绑定数据模型的唯一 id, value 为绑定的数据模型,
* map 保存需要更新倒计时的 model,
* 启动一个定时器, 每个间隔周期遍历一次 map, 取出 map 中数据, 更新 model 的倒计时属性, 最后局部更新 item.
* 注意: 不要忘记界面销毁时调用 [release] 释放资源.
* </p>
*/
class UserAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
/**
* rxjava 实现倒计时.
*/
private var mTimerSubscribe: Disposable? = null
private val mList: MutableList<User> by lazy { LinkedList() }
/**
* 用来存储当前正在进行倒计时的 user.
*/
private val mUserMap by lazy { ArrayMap<Int, User>() }
fun clearData() {
mList.clear()
notifyDataSetChanged()
}
fun addDataList(dataList: List<User>) {
mList.addAll(dataList)
notifyDataSetChanged()
}
fun release() {
mTimerSubscribe?.dispose()
mTimerSubscribe = null
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return UserViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.adapter_user, parent, false)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val user = mList[position]
user.itemPosition = position // 设置 item position.
val vh = holder as UserViewHolder
vh.tvName.text = user.name
if (user.isRunning()) {
disableCountDown(vh.btnCountDown)
holder.btnCountDown.text = "倒计时: ${user.countDownTime}S"
startTimer(user)
} else {
enableCountDown(vh.btnCountDown)
}
vh.btnCountDown.setOnClickListener {
user.countDownTime = COUNT_DOWN_TIME
holder.btnCountDown.text = "倒计时: ${user.countDownTime}S"
startTimer(user)
}
}
/**
* 局部更新倒计时 view.
*/
override fun onBindViewHolder(
holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any>
) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
val user = payloads[0]
if (user is User && holder is UserViewHolder) {
if (user.isRunning()) {
// 如果正在进行倒计时, 禁止重复点击.
disableCountDown(holder.btnCountDown)
holder.btnCountDown.text = "倒计时: ${user.countDownTime}S"
} else {
enableCountDown(holder.btnCountDown)
}
}
}
}
override fun getItemCount() = mList.size
private fun enableCountDown(btn: Button) {
btn.isClickable = true
btn.text = "开始倒计时"
}
private fun disableCountDown(btn: Button) {
btn.isClickable = false
}
/**
* view holder.
*/
internal class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvName: TextView by lazy { itemView.findViewById(R.id.tvName) }
val btnCountDown: Button by lazy { itemView.findViewById(R.id.btnCountDown) }
}
/**
* 启动计时器.
*/
private fun startTimer(user: User) {
mUserMap[user.id] = user
if (null == mTimerSubscribe) {
// 间隔 1s.
mTimerSubscribe = Observable.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
val iterator = mUserMap.entries.iterator()
while (iterator.hasNext()) {
val bean = iterator.next().value
bean.countDownTime -= 1
if (bean.countDownTime <= 0) { // 倒计时结束, 从列表移除数据.
iterator.remove()
}
considerNotifyItemChanged(bean)
}
}
}
}
/**
* 考虑是否更新列表.
* 如果需要更新的数据已经不在列表中(ex: 进行了刷新操作, 刷新后, 接口返回的新数据列表中没有当前需要进行刷新倒计时的数据), 不进行更新.
*/
private fun considerNotifyItemChanged(user: User) {
mList.firstOrNull { it.id == user.id }?.let {
notifyItemChanged(user.itemPosition, user)
}
}
}
全部代码地址: