實習一段時間了,一直想寫點技術總結,但一直沒找到合適的主題。剛好,最近版本中我負責的模塊遇到了個線程相關問題(之前一直畫界面,做點基礎功能,有點乏味),列表項倒計時的實現。
於是乎,我的第一篇android技術文章就誕生了。
【醒目】該demo用Kotlin語言實現。
背景介紹
需要在ListView的item裏實現倒計時,初看還挺簡單的,但是真正做的時候也遇到了不少坑。
網上有不少類似文章,有用對TextView擴展實現的,也有用自帶的CountDownTimer實現的,本文就是用CountDownTimer,只不過多了對服務器時間的刷新控制,更貼近項目需求吧。
剛學了點kotlin,就拿這個來練練手。所以這個demo的源碼就用koltin實現了,想了解學習kotlin的也可以來交流下,剛學,代碼裏可能有些細節語法用的不好。
要點分析:
-
倒計時需要根據請求所得服務器時間和結束時間確定(所以要一個線程來維持服務器時間的運行,而且還有n個線程來維持item項的倒計時刷新顯示)。
既然是多線程,那麽線程的控制就要註意
了解CountDownTimer
在看代碼前,先來了解下android自帶的CountDownTimer類用法
private CountDownTimer timer = new CountDownTimer(30000, 1000) { //根據間隔時間來不斷回調此方法,這裏是每隔1000ms調用一次 @Override public void onTick(long millisUntilFinished) { //todo millisUntilFinished為剩余時間,也就是30000 - n*1000 } //結束倒計時調用 @Override public void onFinish() { //todo } }; //開始倒計時 timer.start(); //取消倒計時(譯者:取消後,再次啟動會重新開始倒計時) timer.cancel();;
這裏的入參再解釋下new CountDownTimer(30000, 1000)。
第一個參數30000代表倒計時的總時間,單位為ms,這裏是30000ms,也就是30s。第二個參數1000就是刷新間隔,也就是回調onTick方法的間隔,單位也是ms,這裏就是1s回調一次。
CountDownTimer相關參考文章:http://www.jb51.net/article/119729.htm
OK,基礎結束,接下來直接實現代碼了。
代碼實現
先看核心,也就是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", "服務器時間線程===" + systemDate + "==for==" + this) handler.postDelayed(this, 1000) } } } init { handler.postDelayed(runnable, 1000) } override fun getView(position: Int, convertView: View"xujf", "====倒計時還活著===第 $index 項item======") //設置時間格式 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 = "倒計時結束" //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 } }
這裏主要的創建一個線程來保持服務器時間和N個item倒計時的“走”動。
保持服務器時間沒什麽好說的,就是Handler配合Runnable的循環調用,註意的是,當activity銷毀時,別忘了調用CountDownAdapter的removeTimer()方法來取消handler的回調,防止內存泄漏。
重點就是item裏的倒計時的線程控制,這裏參照網上的一個比較好的方法,就是用HashMap<TextView, MyCountDownTimer>()來讓MyCountDownTimer和item裏的TextView關聯起來,也就是每個item對應一個CountDownTimer,當關閉頁面時或者刷新list時,可利用cancelAllTimers()方法來清除所有關聯,避免內存泄漏。
以下是ListActivity,偽造一些時間數據
class ListActivity : AppCompatActivity() { private val list: ArrayList<Date> = ArrayList() private var countDownAdapter: CountDownAdapter"jb51code">countDownAdapter"text-align: center">
嗯,本地的服務器時間每秒一次再跑著,沒毛病。
再來看看item裏的倒計時Log:
也沒毛病,只有顯示的那幾項再跑,沒出現失控線程。
關閉ListActivity頁面後所有線程全銷毀。點擊item後進入新界面,所有計時線程都在運行,然後返回ListActivity倒計時也是再跑的(模擬機跑demo的時候由於性能問題,長時間可能會出現倒計時不統一,用真機會好很多。)
OK,最後給出源碼地址:https://github.com/xjf1128/ListCountDownDemo
小結&感想
剛接到這個需求時,感覺肯定不少坑。最終做完再理一理思路,其實也還好。最初的思路正確的話,能少踩點坑。其實就是線程的控制和CountDownTimer的使用,難度也不大。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持電腦玩物。
Tags: CountDownTimer 倒計時 實現 線程 時間 一直文章來源: