亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android 列表倒計(jì)時(shí)的實(shí)現(xiàn)的示例代碼(CountDownTimer)

 更新時(shí)間:2017年09月23日 10:58:08   作者:關(guān)注若l曉鋒  
本篇文章主要介紹了Android 列表倒計(jì)時(shí)的實(shí)現(xiàn)的示例代碼(CountDownTimer),具有一定的參考價(jià)值,有興趣的可以了解一下

實(shí)習(xí)一段時(shí)間了,一直想寫(xiě)點(diǎn)技術(shù)總結(jié),但一直沒(méi)找到合適的主題。剛好,最近版本中我負(fù)責(zé)的模塊遇到了個(gè)線程相關(guān)問(wèn)題(之前一直畫(huà)界面,做點(diǎn)基礎(chǔ)功能,有點(diǎn)乏味),列表項(xiàng)倒計(jì)時(shí)的實(shí)現(xiàn)。

于是乎,我的第一篇android技術(shù)文章就誕生了。

【醒目】該demo用Kotlin語(yǔ)言實(shí)現(xiàn)。


背景介紹

需要在ListView的item里實(shí)現(xiàn)倒計(jì)時(shí),初看還挺簡(jiǎn)單的,但是真正做的時(shí)候也遇到了不少坑。
網(wǎng)上有不少類似文章,有用對(duì)TextView擴(kuò)展實(shí)現(xiàn)的,也有用自帶的CountDownTimer實(shí)現(xiàn)的,本文就是用CountDownTimer,只不過(guò)多了對(duì)服務(wù)器時(shí)間的刷新控制,更貼近項(xiàng)目需求吧。

剛學(xué)了點(diǎn)kotlin,就拿這個(gè)來(lái)練練手。所以這個(gè)demo的源碼就用koltin實(shí)現(xiàn)了,想了解學(xué)習(xí)kotlin的也可以來(lái)交流下,剛學(xué),代碼里可能有些細(xì)節(jié)語(yǔ)法用的不好。

要點(diǎn)分析:

  • 倒計(jì)時(shí)需要根據(jù)請(qǐng)求所得服務(wù)器時(shí)間和結(jié)束時(shí)間確定(所以要一個(gè)線程來(lái)維持服務(wù)器時(shí)間的運(yùn)行,而且還有n個(gè)線程來(lái)維持item項(xiàng)的倒計(jì)時(shí)刷新顯示)。
  • 既然是多線程,那么線程的控制就要注意

了解CountDownTimer

在看代碼前,先來(lái)了解下android自帶的CountDownTimer類用法

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

//開(kāi)始倒計(jì)時(shí)
timer.start();

//取消倒計(jì)時(shí)(譯者:取消后,再次啟動(dòng)會(huì)重新開(kāi)始倒計(jì)時(shí))
timer.cancel();;

這里的入?yún)⒃俳忉屜耼ew CountDownTimer(30000, 1000)。

第一個(gè)參數(shù)30000代表倒計(jì)時(shí)的總時(shí)間,單位為ms,這里是30000ms,也就是30s。第二個(gè)參數(shù)1000就是刷新間隔,也就是回調(diào)onTick方法的間隔,單位也是ms,這里就是1s回調(diào)一次。

CountDownTimer相關(guān)參考文章:http://chabaoo.cn/article/119729.htm

OK,基礎(chǔ)結(jié)束,接下來(lái)直接實(shí)現(xiàn)代碼了。

代碼實(shí)現(xiàn)

先看核心,也就是CountDownAdapter類,這里就簡(jiǎn)化UI,每個(gè)item只有一個(gè)textView來(lái)顯示倒計(jì)時(shí),布局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ù)器時(shí)間線程===" + 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
    }

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

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

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

    return v
  }

  /**
   * 退出時(shí)清空所有item的計(jì)時(shí)器
   */
  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)
  }

  /**
   * 倒計(jì)時(shí)類,每間隔countDownInterval時(shí)間調(diào)用一次onTick()
   * index參數(shù)可去除,在這里只是為了打印log查看倒計(jì)時(shí)是否運(yùn)行
   */
  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為剩余時(shí)間長(zhǎng)
      Log.i("xujf", "====倒計(jì)時(shí)還活著===第 $index 項(xiàng)item======")
      //設(shè)置時(shí)間格式
      val m = millisUntilFinished / countDownInterval
      val hour = m / (60 * 60)
      val minute = (m / 60) % 60
      val s = m % 60
      tv.text = "倒計(jì)時(shí) (${hour}小時(shí)${minute}分${s}秒)"
    }

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

  /**
   * 時(shí)間工具,返回間隔時(shí)間長(zhǎng)
   */
  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)建一個(gè)線程來(lái)保持服務(wù)器時(shí)間和N個(gè)item倒計(jì)時(shí)的“走”動(dòng)。

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

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

以下是ListActivity,偽造一些時(shí)間數(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ù)時(shí),重置本地服務(wù)器時(shí)間
      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ù)器時(shí)間線程和所有item計(jì)時(shí)器,防止關(guān)閉頁(yè)面后線程失控而導(dǎo)致的內(nèi)存泄漏。但是并沒(méi)有在打開(kāi)其他頁(yè)面時(shí)清除,因?yàn)槿绻宄说脑?,那么從其他界面返回至此activity時(shí),倒計(jì)時(shí)已停止。

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

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

運(yùn)行效果

這里就看下我跑模擬機(jī)運(yùn)行demo打印的Log:


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

再來(lái)看看item里的倒計(jì)時(shí)Log:


也沒(méi)毛病,只有顯示的那幾項(xiàng)再跑,沒(méi)出現(xiàn)失控線程。

關(guān)閉ListActivity頁(yè)面后所有線程全銷毀。點(diǎn)擊item后進(jìn)入新界面,所有計(jì)時(shí)線程都在運(yùn)行,然后返回ListActivity倒計(jì)時(shí)也是再跑的(模擬機(jī)跑demo的時(shí)候由于性能問(wèn)題,長(zhǎng)時(shí)間可能會(huì)出現(xiàn)倒計(jì)時(shí)不統(tǒng)一,用真機(jī)會(huì)好很多。)

OK,最后給出源碼地址:https://github.com/xjf1128/ListCountDownDemo

小結(jié)&感想

剛接到這個(gè)需求時(shí),感覺(jué)肯定不少坑。最終做完再理一理思路,其實(shí)也還好。最初的思路正確的話,能少踩點(diǎn)坑。其實(shí)就是線程的控制和CountDownTimer的使用,難度也不大。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論