Android列表倒计时的实现的示例代码(CountDownTimer)

Android列表倒计时的实现的⽰例代码
(CountDownTimer)
实习⼀段时间了,⼀直想写点技术总结,但⼀直没到合适的主题。刚好,最近版本中我负责的模块遇到了个线程相关问题(之前⼀直画界⾯,做点基础功能,有点乏味),列表项倒计时的实现。
于是乎,我的第⼀篇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回调⼀次。
碱性水机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?, 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
}
//获取控件对应的倒计时控件是否存在, 存在就取消, 解决时间重叠问题
var tc: MyCountDownTimer? = timeMap[tag.tvTime]
if (tc != null) {
tc.cancel()
tc = null
}
dmx512协议
/
/计算时间差
val time = getDistanceTimeLong(systemDate, vo)
//创建倒计时,与控件绑定
val cdu = MyCountDownTimer(position, time, 1000, tag.tvTime)
cdu.start()
//[醒⽬]此处需要map集合将控件和倒计时类关联起来
timeMap.put(tag.tvTime, cdu)
return v
}
/**
* 退出时清空所有item的计时器
*/
fun cancelAllTimers() {
var s: Set<MutableMap.MutableEntry<TextView, MyCountDownTimer>>? = ies    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时间调⽤⼀次onTick()
* index参数可去除,在这⾥只是为了打印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======")
//设置时间格式
val m = millisUntilFinished / countDownInterval
val hour = m / (60 * 60)
val minute = (m / 60) % 60
val s = m % 60
< = "倒计时 (${hour}⼩时${minute}分${s}秒)"
}
override fun onFinish() {
< = "倒计时结束"
//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? = null
金属修复override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_list)
getDate()
setDate()
}
private fun setDate() {
if (countDownAdapter == null) {
countDownAdapter = CountDownAdapter(this, list, Date())
lv_count_down.adapter = countDownAdapter
lv_ItemClickListener = AdapterView.OnItemClickListener { adapterView, view, i, l ->
val intent = Intent(ListActivity@this, Main2Activity::class.java)
startActivity(intent)
}
} else {
//刷新数据时,重置本地服务器时间
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()
}
}
这⾥在销毁activity前,清除了服务器时间线程和所有item计时器,防⽌关闭页⾯后线程失控⽽导致的内存泄漏。但是并没有在打开其他页⾯时清除,因为如果清除了的话,那么从其他界⾯返回⾄此activity时,倒计时已停⽌。
当然如果你的需求允许返回界⾯时重新请求加载数据的,可以在onStop()中,只不过这样体验不好
countDownAdapter?.cancelAllTimers()
countDownAdapter?.removeTimer()
运⾏效果
这⾥就看下我跑模拟机运⾏demo打印的Log:
嗯,本地的服务器时间每秒⼀次再跑着,没⽑病。
再来看看item⾥的倒计时Log:
也没⽑病,只有显⽰的那⼏项再跑,没出现失控线程。
关闭ListActivity页⾯后所有线程全销毁。点击item后进⼊新界⾯,所有计时线程都在运⾏,然后返回ListActivity倒计时也是再跑的(模拟机跑demo的时候由于性能问题,长时间可能会出现倒计时不统⼀,⽤真机会好很多。)
⼩结&感想
刚接到这个需求时,感觉肯定不少坑。最终做完再理⼀理思路,其实也还好。最初的思路正确的话,能少踩点坑。其实就是线程的控制和CountDownTimer的使⽤,难度也不⼤。
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

本文发布于:2024-09-23 04:27:45,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/3/277478.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:倒计时   时间   线程   服务器   控件   实现   刷新
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议