Android 倒计时总结
文章目录
- Android 倒计时总结
- Handler方案
- CountDownTimer方案
- Timer方案
- Flow方案
- 总结
- 源码下载
Android 倒计时总结
Handler方案
class MyHandler(private val intervalTime: Long, // 间隔private val totalTime: Long, // 总时长onTick: (Long) -> Unit, // 每秒回调onFinish: () -> Unit // 结束回调
) {private var runType = RunType.INITprivate var handler: Handler? = Handler(Looper.getMainLooper())private val weekOnTick = WeakReference(onTick)private val weekOnFinish = WeakReference(onFinish)private var currentTime = 0Lprivate val runnable = object : Runnable {override fun run() {if (currentTime > 0) {// 进行中currentTime -= intervalTimeweekOnTick.get()?.invoke(currentTime)handler?.postDelayed(this, intervalTime)} else {// 结束weekOnFinish.get()?.invoke()runType = RunType.STOP}}}fun start() {if (runType == RunType.RUNNING) returnrunType = RunType.RUNNINGif (currentTime == 0L) {currentTime = totalTime}handler?.post(runnable)}fun pause() {if (runType != RunType.RUNNING) returnrunType = RunType.PAUSEhandler?.removeCallbacksAndMessages(null)}fun stop() {runType = RunType.STOPhandler?.removeCallbacksAndMessages(null)currentTime = 0weekOnFinish.get()?.invoke()}fun release() {runType = RunType.INIThandler?.removeCallbacksAndMessages(null)handler = nullweekOnTick.clear()weekOnFinish.clear()}enum class RunType {INIT, RUNNING, PAUSE, STOP}
}
使用:
class HandlerFragment : BaseFragment() {private lateinit var tvCountDown: TextViewprivate lateinit var btnStart: Buttonprivate lateinit var btnPause: Buttonprivate lateinit var btnStop: Buttoncompanion object {@JvmStaticfun newInstance() = HandlerFragment()}private lateinit var myHandler: MyHandleroverride fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View {return inflater.inflate(R.layout.fragment_handler, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)tvCountDown = view.findViewById(R.id.tv_count_down)btnStart = view.findViewById(R.id.btn_start)btnPause = view.findViewById(R.id.btn_pause)btnStop = view.findViewById(R.id.btn_stop)myHandler = MyHandler(1000L, 10_000L, { time ->tvCountDown.text = "剩余时间:${time / 1000}s"}, {tvCountDown.text = "倒计时结束!"})btnStart.setOnClickListener {myHandler.start()}btnPause.setOnClickListener {myHandler.pause()}btnStop.setOnClickListener {myHandler.stop()}}override fun onDestroyView() {super.onDestroyView()myHandler.release()}}
CountDownTimer方案
class MyCountDownTimer(private val intervalTime: Long,private val totalTime: Long,onTick: (Long) -> Unit,onFinish: () -> Unit
) {private var runType = RunType.INITprivate val weekOnTick = WeakReference(onTick)private val weekOnFinish = WeakReference(onFinish)private var countDownTimer: CountDownTimer? = nullfun start() {if (runType == RunType.RUNNING) returnrunType = RunType.RUNNINGcountDownTimer = object : CountDownTimer(totalTime, intervalTime) {override fun onTick(p0: Long) {weekOnTick.get()?.invoke(p0)}override fun onFinish() {weekOnFinish.get()?.invoke()runType = RunType.STOP}}countDownTimer!!.start()}fun stop() {runType = RunType.STOPcountDownTimer?.cancel()weekOnFinish.get()?.invoke()}fun release() {countDownTimer?.cancel()countDownTimer = nullweekOnTick.clear()weekOnFinish.clear()}enum class RunType {INIT, RUNNING, STOP}
}
使用:
class CountDownTimerFragment : BaseFragment() {private lateinit var tvCountDown: TextViewprivate lateinit var btnStart: Buttonprivate lateinit var btnStop: Buttonprivate lateinit var countDownTimer: MyCountDownTimercompanion object {@JvmStaticfun newInstance() = CountDownTimerFragment()}override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View {return inflater.inflate(R.layout.fragment_countdown_timer, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)tvCountDown = view.findViewById(R.id.tv_count_down)btnStart = view.findViewById(R.id.btn_start)btnStop = view.findViewById(R.id.btn_stop)countDownTimer = MyCountDownTimer(1000L, 10_000L,{ time ->tvCountDown.text = "剩余时间:${time / 1000}s"},{tvCountDown.text = "倒计时结束!"})btnStart.setOnClickListener {countDownTimer.start()}btnStop.setOnClickListener {countDownTimer.stop()}}override fun onDestroyView() {super.onDestroyView()countDownTimer.release()}
}
Timer方案
class MyTimer(private val intervalTime: Long,private val totalTime: Long,onTick: (Long) -> Unit,onFinish: () -> Unit
) {private val mainHandler = Handler(Looper.getMainLooper())private var runType = RunType.INITprivate val weekOnTick = WeakReference(onTick)private val weekOnFinish = WeakReference(onFinish)private var timer: Timer? = nullprivate var currentTime = 0Lfun start() {if (runType == RunType.RUNNING) returnrunType = RunType.RUNNINGcurrentTime = totalTimetimer = Timer()timer!!.schedule(object : TimerTask() {override fun run() {if (currentTime <= 0) {mainHandler.post {weekOnFinish.get()?.invoke()}cancel()runType = RunType.STOP} else {currentTime -= intervalTimemainHandler.post {weekOnTick.get()?.invoke(currentTime)}}}}, 0, intervalTime)}fun stop() {if (runType != RunType.RUNNING) returnrunType = RunType.STOPtimer?.cancel()weekOnFinish.get()?.invoke()}fun release() {timer?.cancel()timer = nullweekOnTick.clear()weekOnFinish.clear()}enum class RunType {INIT, RUNNING, STOP}
}
使用:
class TimerFragment : BaseFragment() {private lateinit var tvCountDown: TextViewprivate lateinit var btnStart: Buttonprivate lateinit var btnStop: Buttonprivate lateinit var myTimer: MyTimercompanion object {@JvmStaticfun newInstance() = TimerFragment()}override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View {return inflater.inflate(R.layout.fragment_timer, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)tvCountDown = view.findViewById(R.id.tv_count_down)btnStart = view.findViewById(R.id.btn_start)btnStop = view.findViewById(R.id.btn_stop)myTimer = MyTimer(1000L, 10_000L,{ time ->tvCountDown.text = "剩余时间:${time / 1000}s"},{tvCountDown.text = "倒计时结束!"})btnStart.setOnClickListener {myTimer.start()}btnStop.setOnClickListener {myTimer.stop()}}override fun onDestroyView() {super.onDestroyView()myTimer.release()}
}
Flow方案
class MyFlow(private val intervalTime: Long,private val totalTime: Long,private val onTick: (Long) -> Unit,private val onFinish: () -> Unit,private val scope: CoroutineScope
) {private var runType = RunType.INITprivate val weekOnTick = WeakReference(onTick)private val weekOnFinish = WeakReference(onFinish)private var job: Job? = nullprivate var currentTime = 0Lfun start() {if (runType == RunType.RUNNING) returnrunType = RunType.RUNNINGjob = scope.launch {flow {currentTime = totalTimewhile (currentTime >= 0) {emit(currentTime)delay(intervalTime)currentTime -= 1000}}.collect {weekOnTick.get()?.invoke(it)if (it <= 0) {weekOnFinish.get()?.invoke()runType = RunType.STOP}}}}fun stop() {if (runType != RunType.RUNNING) returnrunType = RunType.STOPjob?.cancel()weekOnFinish.get()?.invoke()}fun release() {job?.cancel()job = nullweekOnTick.clear()weekOnFinish.clear()}enum class RunType {INIT, RUNNING, STOP}
}
使用:
class FlowFragment : BaseFragment() {private lateinit var tvCountDown: TextViewprivate lateinit var btnStart: Buttonprivate lateinit var btnStop: Buttonprivate lateinit var myFlow: MyFlowcompanion object {@JvmStaticfun newInstance() = FlowFragment()}override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View {return inflater.inflate(R.layout.fragment_flow, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)tvCountDown = view.findViewById(R.id.tv_count_down)btnStart = view.findViewById(R.id.btn_start)btnStop = view.findViewById(R.id.btn_stop)myFlow = MyFlow(1000L, 10_000L,{ time ->tvCountDown.text = "剩余时间:${time / 1000}s"},{tvCountDown.text = "倒计时结束!"},lifecycleScope)btnStart.setOnClickListener {myFlow.start()}btnStop.setOnClickListener {myFlow.stop()}}override fun onDestroyView() {super.onDestroyView()myFlow.release()}
}
总结
-
简单需求:优先选用CountDownTimer,避免重复造轮子
-
界面交互:使用Handler时注意与View的生命周期绑定
-
后台任务:Timer方案需配合Service使用
-
新项目推荐:采用Kotlin Flow实现,搭配协程更高效
-
性能关键:避免在倒计时回调中执行耗时操作
-
内存优化:所有方案都需注意释放资源