一、前言

原生的倒计时功能比较简单,没有暂停和重新开始计时功能,所以仿照原生的CountDownTimer做一个更好用的倒计时功能。

二、自定义类

  1. public class LCountDownTimer {
  2. /**
  3. * 时间,即开始的时间,通俗来说就是倒计时总时间
  4. */
  5. private long mMillisInFuture;
  6. /**
  7. * 布尔值,表示计时器是否被取消
  8. * 只有调用cancel时才被设置为true
  9. */
  10. private boolean mCancelled = false;
  11. /**
  12. * 用户接收回调的时间间隔,一般是1秒
  13. */
  14. private long mCountdownInterval;
  15. /**
  16. * 记录暂停时候的时间
  17. */
  18. private long mStopTimeInFuture;
  19. /**
  20. * mas.what值
  21. */
  22. private static final int MSG = 520;
  23. /**
  24. * 暂停时,当时剩余时间
  25. */
  26. private long mCurrentMillisLeft;
  27. /**
  28. * 是否暂停
  29. * 只有当调用pause时,才设置为true
  30. */
  31. private boolean mPause = false;
  32. /**
  33. * 监听listener
  34. */
  35. private TimerListener mCountDownListener;
  36. /**
  37. * 是否创建开始
  38. */
  39. private boolean isStart;
  40. public LCountDownTimer(){
  41. isStart = true;
  42. }
  43. public LCountDownTimer(long millisInFuture, long countdownInterval) {
  44. long total = millisInFuture + 20;
  45. this.mMillisInFuture = total;
  46. //this.mMillisInFuture = millisInFuture;
  47. this.mCountdownInterval = countdownInterval;
  48. isStart = true;
  49. }
  50. /**
  51. * 开始倒计时,每次点击,都会重新开始
  52. */
  53. public synchronized final void start() {
  54. if (mMillisInFuture <= 0 && mCountdownInterval <= 0) {
  55. throw new RuntimeException("you must set the millisInFuture > 0 or countdownInterval >0");
  56. }
  57. mCancelled = false;
  58. long elapsedRealtime = SystemClock.elapsedRealtime();
  59. mStopTimeInFuture = elapsedRealtime + mMillisInFuture;
  60. mPause = false;
  61. mHandler.sendMessage(mHandler.obtainMessage(MSG));
  62. if (mCountDownListener!=null){
  63. mCountDownListener.onStart();
  64. }
  65. }
  66. /**
  67. * 取消计时器
  68. */
  69. public synchronized final void cancel() {
  70. if (mHandler != null) {
  71. //暂停
  72. mPause = false;
  73. mHandler.removeMessages(MSG);
  74. //取消
  75. mCancelled = true;
  76. }
  77. }
  78. /**
  79. * 按一下暂停,再按一下继续倒计时
  80. */
  81. public synchronized final void pause() {
  82. if (mHandler != null) {
  83. if (mCancelled) {
  84. return;
  85. }
  86. if (mCurrentMillisLeft < mCountdownInterval) {
  87. return;
  88. }
  89. if (!mPause) {
  90. mHandler.removeMessages(MSG);
  91. mPause = true;
  92. }
  93. }
  94. }
  95. /**
  96. * 恢复暂停,开始
  97. */
  98. public synchronized final void resume() {
  99. if (mMillisInFuture <= 0 && mCountdownInterval <= 0) {
  100. throw new RuntimeException("you must set the millisInFuture > 0 or countdownInterval >0");
  101. }
  102. if (mCancelled) {
  103. return;
  104. }
  105. //剩余时长少于
  106. if (mCurrentMillisLeft < mCountdownInterval || !mPause) {
  107. return;
  108. }
  109. mStopTimeInFuture = SystemClock.elapsedRealtime() + mCurrentMillisLeft;
  110. mHandler.sendMessage(mHandler.obtainMessage(MSG));
  111. mPause = false;
  112. }
  113. @SuppressLint("HandlerLeak")
  114. private Handler mHandler = new Handler() {
  115. @Override
  116. public void handleMessage(@NonNull Message msg) {
  117. synchronized (LCountDownTimer.this) {
  118. if (mCancelled) {
  119. return;
  120. }
  121. //剩余毫秒数
  122. final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
  123. if (millisLeft <= 0) {
  124. mCurrentMillisLeft = 0;
  125. if (mCountDownListener != null) {
  126. mCountDownListener.onFinish();
  127. }
  128. } else if (millisLeft < mCountdownInterval) {
  129. mCurrentMillisLeft = 0;
  130. // 剩余时间小于一次时间间隔的时候,不再通知,只是延迟一下
  131. sendMessageDelayed(obtainMessage(MSG), millisLeft);
  132. } else {
  133. //有多余的时间
  134. long lastTickStart = SystemClock.elapsedRealtime();
  135. if (mCountDownListener != null) {
  136. mCountDownListener.onTick(millisLeft);
  137. }
  138. mCurrentMillisLeft = millisLeft;
  139. // 考虑用户的onTick需要花费时间,处理用户onTick执行的时间
  140. // 打印这个delay时间,大概是997毫秒
  141. long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
  142. // 特殊情况:用户的onTick方法花费的时间比interval长,那么直接跳转到下一次interval
  143. // 注意,在onTick回调的方法中,不要做些耗时的操作
  144. boolean isWhile = false;
  145. while (delay < 0){
  146. delay += mCountdownInterval;
  147. isWhile = true;
  148. }
  149. if (isWhile){
  150. }
  151. sendMessageDelayed(obtainMessage(MSG), delay);
  152. }
  153. }
  154. }
  155. };
  156. /**
  157. * 设置倒计时总时间
  158. * @param millisInFuture 毫秒值
  159. */
  160. public void setMillisInFuture(long millisInFuture) {
  161. long total = millisInFuture + 20;
  162. this.mMillisInFuture = total;
  163. }
  164. /**
  165. * 设置倒计时间隔值
  166. * @param countdownInterval 间隔,一般设置为1000毫秒
  167. */
  168. public void setCountdownInterval(long countdownInterval) {
  169. this.mCountdownInterval = countdownInterval;
  170. }
  171. /**
  172. * 设置倒计时监听
  173. * @param countDownListener listener
  174. */
  175. public void setCountDownListener(TimerListener countDownListener) {
  176. this.mCountDownListener = countDownListener;
  177. }
  178. }

最后的几个方法可以设置总时长,倒计时间隔,倒计时监听。

接口:

  1. public interface TimerListener {
  2. /**
  3. * 当倒计时开始
  4. */
  5. void onStart();
  6. /**
  7. * 当倒计时结束
  8. */
  9. void onFinish();
  10. /**
  11. * @param millisUntilFinished 剩余时间
  12. */
  13. void onTick(long millisUntilFinished);
  14. }

使用示例:

  1. var mCountTime: LCountDownTimer? = null
  2. override fun initInterface() {
  3. //倒计时相关
  4. mCountTime = LCountDownTimer(60*1000 + 100, 1000)
  5. btn_click_all_start.setOnClickListener {
  6. mCountTime?.start()
  7. }
  8. btn_click_all_restart.setOnClickListener {
  9. mCountTime?.resume()
  10. }
  11. btn_click_all_pause.setOnClickListener {
  12. mCountTime?.pause()
  13. }
  14. btn_click_all_stop.setOnClickListener {
  15. mCountTime?.cancel()
  16. }
  17. mCountTime?.setCountDownListener(object : TimerListener{
  18. override fun onFinish() {
  19. LogUtils.e("onFinish")
  20. }
  21. override fun onTick(millisUntilFinished: Long) {
  22. tv_click_all_num.text = "${millisUntilFinished / 1000}, 倒计时"
  23. LogUtils.e("onTick--${millisUntilFinished/1000}")
  24. }
  25. override fun onStart() {
  26. LogUtils.e("onStart")
  27. }
  28. })
  29. }
  30. override fun onDestroy() {
  31. super.onDestroy()
  32. mCountTime?.run {
  33. cancel()
  34. }
  35. mCountTime = null
  36. }

参考

Android 倒计时的五种实现方式