简体   繁体   English

在Android的RecyclerView上滚动时如何解决计时器?

[英]How to fix timer when scrolling on RecyclerView on Android?

In my application, I want to use multiple CountDownTimer to show the time remaining on offers in a RecyclerView . 在我的应用程序中,我想使用多个CountDownTimerRecyclerView显示商品的剩余时间。 I have written the code below in Kotlin, but while scrolling the timers keep restarting. 我已经在Kotlin中编写了以下代码,但是在滚动计时器时会不断重启。 For example, the timer starts at 4:19 and while scrolling instead of showing 4:09 after 10 seconds still shows 4:19. 例如,计时器从4:19开始,在滚动而不是在10秒后显示4:09时仍显示4:19。

Activity code: 活动代码:

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()
                            }
                        }
                    }
                }
            })
        }
    }
}

Adapter code: 适配器代码:

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 }
        }
}

How can I fix this? 我怎样才能解决这个问题?

You have CountDownTimer for each ViewHolder . 每个ViewHolder都有CountDownTimer Every time, you scroll, recyclerView creates new CountDownTimer . 每次滚动时,recyclerView都会创建新的CountDownTimer So, if I correctly got your question, you should have single CountDownTimer for the whole adapter. 因此,如果我正确地收到了您的问题,则整个适配器应该具有单个CountDownTimer Make it a local field of adapter class. 使它成为适配器类的本地字段。

When you want use Timer into your adapter, Shouldn't use this in onBindViewHolder . 当您要在适配器中使用Timer ,不应在onBindViewHolder使用它。
Because onBindViewHolder call every when you scrolling on items 因为onBindViewHolder在滚动项目时每次onBindViewHolder调用
You should use Timer into constructor of your Adapter, and every second notifyDataSetChanged your adapter. 您应该将Timer用作适配器的构造函数 ,并每隔一秒钟将notifyDataSetChanged适配器。
Don't worry, this is way not bad structure and not memory leak . 不用担心,这不是结构不好也不是内存泄漏的方式 you can check this in profile tab! 您可以在个人资料标签中查看!

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)
    }
}

I hope i can help you. 希望我能为您服务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM