温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么在Android中实现列表倒计时

发布时间:2021-04-15 17:35:12 来源:亿速云 阅读:148 作者:Leah 栏目:移动开发

怎么在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();;

代码实现

先看核心,也就是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
    }

    //计算时间差
    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>>? = 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时间调用一次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
      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? = 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 {
      //刷新数据时,重置本地服务器时间
      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前,清除了服务器时间线程和所有item计时器,防止关闭页面后线程失控而导致的内存泄漏。但是并没有在打开其他页面时清除,因为如果清除了的话,那么从其他界面返回至此activity时,倒计时已停止。

当然如果你的需求允许返回界面时重新请求加载数据的,可以在onStop()中,只不过这样体验不好

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

运行效果

这里就看下我跑模拟机运行demo打印的Log:

怎么在Android中实现列表倒计时

嗯,本地的服务器时间每秒一次再跑着,没毛病。

再来看看item里的倒计时Log:

怎么在Android中实现列表倒计时

看完上述内容,你们掌握怎么在Android中实现列表倒计时的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI