溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么在Android中實現(xiàn)列表倒計時

發(fā)布時間:2021-04-15 17:35:12 來源:億速云 閱讀:136 作者:Leah 欄目:移動開發(fā)

怎么在Android中實現(xiàn)列表倒計時?相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

CountDownTimer類用法

  private CountDownTimer timer = new CountDownTimer(30000, 1000) { 
    //根據(jù)間隔時間來不斷回調(diào)此方法,這里是每隔1000ms調(diào)用一次
    @Override 
    public void onTick(long millisUntilFinished) { 
      //todo millisUntilFinished為剩余時間,也就是30000 - n*1000
    } 
    
    //結(jié)束倒計時調(diào)用  
    @Override 
    public void onFinish() { 
      //todo
    } 
}; 

//開始倒計時
timer.start();

//取消倒計時(譯者:取消后,再次啟動會重新開始倒計時)
timer.cancel();;

代碼實現(xiàn)

先看核心,也就是CountDownAdapter類,這里就簡化UI,每個item只有一個textView來顯示倒計時,布局XML就不放了,直接放代碼

class CountDownAdapter(private var activity: ListActivity, private var data: ArrayList<Date>, private var systemDate: Date) : BaseAdapter() {

  private val timeMap = HashMap<TextView, MyCountDownTimer>()
  private val handler = Handler()
  private val runnable = object : Runnable {
    override fun run() {
      if (systemDate != null) {
        systemDate.time = systemDate.time + 1000
        Log.i("xujf", "服務(wù)器時間線程===" + systemDate + "==for==" + this)
        handler.postDelayed(this, 1000)
      }
    }
  }

  init {
    handler.postDelayed(runnable, 1000)
  }

  override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
    var v: View
    var tag: ViewHolder
    var vo = data[position]
    if (null == convertView) {
      v = activity.layoutInflater.inflate(R.layout.item_count_down, null)
      tag = ViewHolder(v)

      v.tag = tag
    } else {
      v = convertView
      tag = v.tag as ViewHolder
    }

    //獲取控件對應(yīng)的倒計時控件是否存在, 存在就取消, 解決時間重疊問題
    var tc: MyCountDownTimer? = timeMap[tag.tvTime]
    if (tc != null) {
      tc.cancel()
      tc = null
    }

    //計算時間差
    val time = getDistanceTimeLong(systemDate, vo)
    //創(chuàng)建倒計時,與控件綁定
    val cdu = MyCountDownTimer(position, time, 1000, tag.tvTime)
    cdu.start()

    //[醒目]此處需要map集合將控件和倒計時類關(guān)聯(lián)起來
    timeMap.put(tag.tvTime, cdu)

    return v
  }

  /**
   * 退出時清空所有item的計時器
   */
  fun cancelAllTimers() {
    var s: Set<MutableMap.MutableEntry<TextView, MyCountDownTimer>>? = timeMap.entries
    var it: Iterator<*>? = s!!.iterator()
    while (it!!.hasNext()) {
      try {
        val pairs = it.next() as MutableMap.MutableEntry<*, *>
        var cdt: MyCountDownTimer? = pairs.value as MyCountDownTimer
        cdt!!.cancel()
        cdt = null
      } catch (e: Exception) {
      }

    }
    it = null
    s = null
    timeMap.clear()
  }

  fun removeTimer(){
    handler?.removeCallbacks(runnable)
  }

  fun reSetTimer(date: Date) {
    removeTimer()
    systemDate = date
    handler.postDelayed(runnable, 1000)
  }

  override fun getItem(position: Int): Any = data[position]

  override fun getItemId(position: Int): Long = 0L

  override fun getCount(): Int = data.size

  internal inner class ViewHolder(view: View) {
    var tvTime = view.findViewById<TextView>(R.id.tv_time)
  }

  /**
   * 倒計時類,每間隔countDownInterval時間調(diào)用一次onTick()
   * index參數(shù)可去除,在這里只是為了打印log查看倒計時是否運行
   */
  private inner class MyCountDownTimer(internal var index: Int, millisInFuture: Long,
                     internal var countDownInterval: Long, internal var tv: TextView
  ) : CountDownTimer(millisInFuture, countDownInterval) {

    override fun onTick(millisUntilFinished: Long) {
      //millisUntilFinished為剩余時間長
      Log.i("xujf", "====倒計時還活著===第 $index 項item======")
      //設(shè)置時間格式
      val m = millisUntilFinished / countDownInterval
      val hour = m / (60 * 60)
      val minute = (m / 60) % 60
      val s = m % 60
      tv.text = "倒計時 (${hour}小時${minute}分${s}秒)"
    }

    override fun onFinish() {
      tv.text = "倒計時結(jié)束"
      //todo 可以做一些刷新動作
    }
  }

  /**
   * 時間工具,返回間隔時間長
   */
  fun getDistanceTimeLong(one: Date, two: Date): Long {
    var diff = 0L
    try {
      val time1 = one.time
      val time2 = two.time
      if (time1 < time2) {
        diff = time2 - time1
      } else {
        diff = time1 - time2
      }
    } catch (e: Exception) {
      e.printStackTrace()
    }

    return diff
  }
}

這里主要的創(chuàng)建一個線程來保持服務(wù)器時間和N個item倒計時的“走”動。

保持服務(wù)器時間沒什么好說的,就是Handler配合Runnable的循環(huán)調(diào)用,注意的是,當(dāng)activity銷毀時,別忘了調(diào)用CountDownAdapter的removeTimer()方法來取消handler的回調(diào),防止內(nèi)存泄漏。

重點就是item里的倒計時的線程控制,這里參照網(wǎng)上的一個比較好的方法,就是用HashMap<TextView, MyCountDownTimer>()來讓MyCountDownTimer和item里的TextView關(guān)聯(lián)起來,也就是每個item對應(yīng)一個CountDownTimer,當(dāng)關(guān)閉頁面時或者刷新list時,可利用cancelAllTimers()方法來清除所有關(guān)聯(lián),避免內(nèi)存泄漏。

以下是ListActivity,偽造一些時間數(shù)據(jù)

class ListActivity : AppCompatActivity() {

  private val list: ArrayList<Date> = ArrayList()
  private var countDownAdapter: CountDownAdapter? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_list)
    getDate()
    setDate()
  }

  private fun setDate() {
    if (countDownAdapter == null) {
      countDownAdapter = CountDownAdapter(this, list, Date())
      lv_count_down.adapter = countDownAdapter
      lv_count_down.onItemClickListener = AdapterView.OnItemClickListener { adapterView, view, i, l ->
        val intent = Intent(ListActivity@this, Main2Activity::class.java)
        startActivity(intent)
      }
    } else {
      //刷新數(shù)據(jù)時,重置本地服務(wù)器時間
      countDownAdapter!!.reSetTimer(Date())
      countDownAdapter!!.notifyDataSetChanged()
    }
  }

  private fun getDate() {
    for (i in 1..20) {
      var date = Date(Date().time + i * 1000 * 60 * 30)
      list.add(date)
    }

  }

  override fun onDestroy() {
    countDownAdapter?.cancelAllTimers()
    countDownAdapter?.removeTimer()
    super.onDestroy()
  }
}

這里在銷毀activity前,清除了服務(wù)器時間線程和所有item計時器,防止關(guān)閉頁面后線程失控而導(dǎo)致的內(nèi)存泄漏。但是并沒有在打開其他頁面時清除,因為如果清除了的話,那么從其他界面返回至此activity時,倒計時已停止。

當(dāng)然如果你的需求允許返回界面時重新請求加載數(shù)據(jù)的,可以在onStop()中,只不過這樣體驗不好

countDownAdapter?.cancelAllTimers()
countDownAdapter?.removeTimer()

運行效果

這里就看下我跑模擬機運行demo打印的Log:

怎么在Android中實現(xiàn)列表倒計時

嗯,本地的服務(wù)器時間每秒一次再跑著,沒毛病。

再來看看item里的倒計時Log:

怎么在Android中實現(xiàn)列表倒計時

看完上述內(nèi)容,你們掌握怎么在Android中實現(xiàn)列表倒計時的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI