В моем приложении я хочу использовать несколько CountDownTimer
для отображения времени, оставшегося на предложениях в RecyclerView
. Я написал код ниже в Kotlin, но при прокрутке таймеры продолжают перезапускаться. Например, таймер запускается в 4:19 и при прокрутке вместо 4:09 через 10 секунд все равно показывает 4:19.
Код деятельности:
class MainActivity : AppCompatActivity() {
private lateinit var apisList: ApisList
private lateinit var retrofit: Retrofit
private lateinit var todayAdapter: AuctionsTodayAdapter
private val todayModel: MutableList<Today> = mutableListOf()
private lateinit var layoutManager: RecyclerView.LayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Initialize
retrofit = ApiClient.instance
apisList = retrofit.create(ApisList::class.java)
todayAdapter = AuctionsTodayAdapter(themedContext, todayModel)
layoutManager = LinearLayoutManager(themedContext)
//RecyclerView
main_list.setHasFixedSize(true)
main_list.layoutManager = layoutManager
main_list.adapter = todayAdapter
if (isNetworkAvailable()) getData(1, 10)
}
private fun getData(page: Int, limit: Int) {
main_loader.visibility = View.VISIBLE
val call = apisList.getAuctionsToday(page, limit)
call.let {
it.enqueue(object : Callback<AuctionsTodayResponse> {
override fun onFailure(call: Call<AuctionsTodayResponse>, t: Throwable) {
main_loader.visibility = View.GONE
Log.e("auctionsTodayList", t.message)
}
override fun onResponse(call: Call<AuctionsTodayResponse>, response: Response<AuctionsTodayResponse>) {
if (response.isSuccessful) {
response.body()?.let { itBody ->
main_loader.visibility = View.GONE
if (itBody.toString().isNotEmpty()) {
todayModel.clear()
todayModel.addAll(itBody.res.today)
todayAdapter.notifyDataSetChanged()
}
}
}
}
})
}
}
}
Код адаптера:
class AuctionsTodayAdapter(val context: Context, val model: MutableList<Today>) :
RecyclerView.Adapter<AuctionsTodayAdapter.MyHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val view = LayoutInflater.from(context).inflate(R.layout.row_main_list, parent, false)
val holder = MyHolder(view)
//holder.setIsRecyclable(false)
return holder
}
override fun getItemCount(): Int {
return model.size
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
val modelUse = model[position]
holder.setData(modelUse)
if (holder.newCountDownTimer != null) {
holder.newCountDownTimer!!.cancel()
}
var timer = modelUse.calculateEnd
timer = timer * 1000
holder.newCountDownTimer = object : CountDownTimer(timer, 1000) {
override fun onTick(millisUntilFinished: Long) {
var seconds = (millisUntilFinished / 1000).toInt()
val hours = seconds / (60 * 60)
val tempMint = seconds - hours * 60 * 60
val minutes = tempMint / 60
seconds = tempMint - minutes * 60
holder.rowMain_timer.rowMain_timer.text =
String.format("%02d", hours) + ":" + String.format(
"%02d",
minutes
) + ":" + String.format("%02d", seconds)
}
override fun onFinish() {
holder.rowMain_timer.text = "00:00:00"
}
}.start()
}
inner class MyHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var newCountDownTimer: CountDownTimer? = null
lateinit var rowMain_timer: TextView
init {
rowMain_timer = itemView.findViewById(R.id.rowMain_timer)
}
fun setData(model: Today) {
model.image.let {
Glide.with(context)
.load(Constants.MAIN_BASE_URL + it)
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.RESOURCE))
.into(itemView.rowMain_img)
}
model.title.let { itemView.rowMain_title.text = it }
}
}
Как я могу это исправить?
Если вы хотите использовать Timer
в вашем адаптере, не следует использовать это в onBindViewHolder
.
Потому что onBindViewHolder
вызывать каждый раз при прокрутке элементов
Вы должны использовать Timer в конструктор вашего адаптера, и каждую секунду notifyDataSetChanged
ваш адаптер.
Не волнуйтесь, это неплохая структура и не утечка памяти. Вы можете проверить это на вкладке профиля !
class AuctionsTodayAdapter(val context: Context, val model: MutableList<Today>) :
RecyclerView.Adapter<AuctionsTodayAdapter.MyHolder>() {
private var newData: Long = 0
init {
for (items in model) {
items.end.let {
newData = items.end.toLong()
}
}
//set the timer which will refresh the data every 1 second.
object : CountDownTimer(newData, 1000) {
override fun onFinish() {
notifyDataSetChanged()
}
override fun onTick(p0: Long) {
var i = 0
val dataLength = model.size
while (i < dataLength) {
val item = model[i]
item.end -= 1000
i++
}
notifyDataSetChanged()
}
}.start()
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
var modelUse = model[position]
//Img
modelUse.image.let {
Glide.with(context)
.load(Constants.MAIN_BASE_URL + it)
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.RESOURCE))
.into(holder.rowMAIN_img)
}
//Timer
modelUse.end.let {
if (modelUse.calculateEnd > 0) {
holder.rowMAIN_timer.text = getDurationBreakdown(modelUse.end.toLong())
} else {
holder.rowMain_timer.text = "Finished"
}
}
}
private fun millToMins(milliSec: Long): String {
var seconds = (milliSec / 1000).toInt()
val hours = seconds / (60 * 60)
val tempMint = seconds - hours * 60 * 60
val minutes = tempMint / 60
seconds = tempMint - minutes * 60
return String.format("%02d", hours) + ":" + String.format(
"%02d",
minutes
) + ":" + String.format("%02d", seconds)
}
}
Я надеюсь, что смогу помочь тебе.
У вас есть CountDownTimer
для каждого ViewHolder
. Каждый раз, когда вы прокручиваете, recyclerView создает новый CountDownTimer
. Итак, если я правильно понял ваш вопрос, у вас должен быть один CountDownTimer
для всего адаптера. Сделайте это локальным полем класса адаптера.