一. Handler实现
直接使用Handler的延时发送消息实现倒计时
kotlin
class MainActivity : CommonDataBindingActivity<ActivityMainBinding>() {
private var mCount = 10
private val mHandler: Handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
1 -> {
Log.e("xyh", "handleMessage mCount: $mCount")
if (mCount > 0) {
mCount--
startCountDown()
} else {
stopCountDown()
}
}
}
}
}
override fun getDataBindingLayoutId(): Int = R.layout.activity_main
override fun getStatisticTag(): String = "MainActivity"
override fun initData(savedInstanceState: Bundle?) {
}
override fun initializeViews() {
}
override fun setViewListeners() {
// 开始倒计时
mDataBinding.btn2.setOnClickListener {
Log.e("xyh", "开始倒计时 mCount: $mCount")
startCountDown()
}
}
private fun startCountDown() {
mHandler.sendEmptyMessageDelayed(1, 1000)
}
private fun stopCountDown() {
Log.e("xyh", "结束倒计时 mCount: $mCount")
mHandler.removeCallbacksAndMessages(null)
}
override fun onDestroy() {
super.onDestroy()
stopCountDown()
}
}
另一种做法是使用 Runnable 来实现
kotlin
class MainActivity : CommonDataBindingActivity<ActivityMainBinding>() {
private var mCount = 10
private val mHandler: Handler = Handler(Looper.getMainLooper())
override fun getDataBindingLayoutId(): Int = R.layout.activity_main
override fun getStatisticTag(): String = "MainActivity"
override fun initData(savedInstanceState: Bundle?) {
}
override fun initializeViews() {
}
override fun setViewListeners() {
// 开始倒计时
mDataBinding.btn2.setOnClickListener {
Log.e("xyh", "开始倒计时 mCount: $mCount")
startCountDown()
}
}
private fun startCountDown() {
mHandler.postDelayed(mRunnable, 1000)
}
private val mRunnable: Runnable = object : Runnable {
override fun run() {
mCount--
Log.e("xyh", "mCount: $mCount")
if (mCount > 0) {
mHandler.postDelayed(this, 1000)
}
}
}
}
二. 使用CountDownTimer实现
CountDownTimer
是 Android 提供的一个用于实现倒计时功能的实用类,它封装了Handler
的内部实现,使用起来更加简单方便。
CountDownTimer基本用法


倒计时实现
kotlin
class MainActivity : CommonDataBindingActivity<ActivityMainBinding>() {
private var timer: CountDownTimer? = null
override fun getDataBindingLayoutId(): Int = R.layout.activity_main
override fun getStatisticTag(): String = "MainActivity"
override fun initData(savedInstanceState: Bundle?) {
}
override fun initializeViews() {
}
override fun setViewListeners() {
// 开始倒计时
mDataBinding.btn2.setOnClickListener {
startCountDown()
}
}
private fun startCountDown() {
var count = 5
timer = object : CountDownTimer(1000L * count, 1000) {
override fun onTick(millisUntilFinished: Long) {
if (count > 0) {
count--
Log.e("xyh", "onTick 倒计时 count: $count")
} else {
Log.e("xyh", "onTick 倒计时结束了 count: $count")
}
}
override fun onFinish() {
Log.e("xyh", "onFinish: 倒计时结束了")
}
}
timer?.start()
}
override fun onDestroy() {
super.onDestroy()
timer?.cancel()
}
}
三、直接用Time、TimeTask实现
kotlin
class MainActivity : CommonDataBindingActivity<ActivityMainBinding>() {
private var timer: Timer? = null
override fun getDataBindingLayoutId(): Int = R.layout.activity_main
override fun getStatisticTag(): String = "MainActivity"
override fun initData(savedInstanceState: Bundle?) {
}
override fun initializeViews() {
}
override fun setViewListeners() {
// 开始倒计时
mDataBinding.btn2.setOnClickListener {
startCountDown()
}
}
private fun startCountDown() {
var count = 5
timer = Timer()
val timeTask = object : TimerTask() {
override fun run() {
if (count > 0) {
count--
Log.e("xyh", " 倒计时 count: $count")
} else {
timer?.cancel()
Log.e("xyh", " 倒计时结束了 count: $count")
}
}
}
timer?.schedule(timeTask, 1000, 1000)
}
override fun onDestroy() {
super.onDestroy()
timer?.cancel()
}
}
四、使用框架RxJava
kotlin
class MainActivity : CommonDataBindingActivity<ActivityMainBinding>() {
private var mDisposables: Disposable? = null
override fun getDataBindingLayoutId(): Int = R.layout.activity_main
override fun getStatisticTag(): String = "MainActivity"
override fun initData(savedInstanceState: Bundle?) {
}
override fun initializeViews() {
}
override fun setViewListeners() {
// 开始倒计时
mDataBinding.btn2.setOnClickListener {
startCountDown()
}
}
private fun startCountDown() {
var count = 5
//三个参数,分别是第一次发送延迟,间隔时间,时间单位
mDisposables = Observable.interval(0, 1, TimeUnit.SECONDS)
.take(count.toLong())
.map {
return@map count - it
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
Log.e("xyh", "startCountDown: $it")
}
}
override fun onDestroy() {
super.onDestroy()
mDisposables?.dispose()
}
}
五、Kotlin Flow 的实现
上面的方法都需要销毁资源,好麻烦,能不能自动取消?协程不就行了。
是的 lifecycleScope 根据生命周期自动取消的协程作用域,配合Flow的操作符完成倒计时岂不是完美。
好吧,你是自动倒计时了。结束之后取消协程,销毁也能取消协程,那如果我想手动的取消倒计时怎么办?比如倒计时60秒我就要在第50秒的时候强制取消协程怎么办?
launch方法返回的不就是Job 对象吗?根据此上下文对象不就可以取消协程了吗?
看看灵活的Flow倒计时如何实现。
arduino
// 协程核心库
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
// 协程Android支持库
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
定义一个扩展方法
kotlin
/**
* 倒计时扩展方法实现
*/
fun FragmentActivity.countDown(
time: Int = 5,
start: (scop: CoroutineScope) -> Unit,
end: () -> Unit,
next: (time: Int) -> Unit
) {
lifecycleScope.launch {
// 在这个范围内启动的协程会在Lifecycle被销毁的时候自动取消
flow {
(time downTo 0).forEach {
delay(1000)
emit(it)
}
}.onStart {
// 倒计时开始 ,在这里可以让Button 禁止点击状态
start(this@launch)
}.onCompletion {
// 倒计时结束 ,在这里可以让Button 恢复点击状态
end()
}.catch {
//错误
Log.e("xyh", it.message ?: "Unkown Error")
}.collect {
// 在这里 更新值来显示到UI
next(it)
}
}
}
使用
kotlin
class MainActivity : CommonDataBindingActivity<ActivityMainBinding>() {
override fun getDataBindingLayoutId(): Int = R.layout.activity_main
override fun getStatisticTag(): String = "MainActivity"
override fun initData(savedInstanceState: Bundle?) {
}
override fun initializeViews() {
}
override fun setViewListeners() {
// 开始倒计时
mDataBinding.btn2.setOnClickListener {
startCountDown()
}
}
private fun startCountDown() {
var count = 10
var timeDownScope: CoroutineScope? = null
countDown(count,
start = {
timeDownScope = it
Log.e("xyh", "startCountDown start: 倒计时开始")
},
end = {
Log.e("xyh", "startCountDown end: 倒计时结束")
},
next = {
Log.e("xyh", "startCountDown next: $it")
//在第5秒的时候强制取消
if (it == 5) {
timeDownScope?.cancel()
}
}
)
}
}
总结
倒计时的实现是我们常用的功能,如果你的项目是Kotlin构建的,那么我建议使用Flow来实现这种功能,使用扩展函数进行封装,使用起来更加的简单。
如果你们项目是Java语言实现的,那么同样的可以选择一种方式进行一个工具类的封装,也能达到同样的效果,只是记得需要在onDestory中销毁资源哦。