Android实现倒计时的几种方案

一. 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中销毁资源哦。

相关推荐
编程乐学(Arfan开发工程师)2 小时前
06、基础入门-SpringBoot-依赖管理特性
android·spring boot·后端
androidwork3 小时前
使用 Kotlin 和 Jetpack Compose 开发 Wear OS 应用的完整指南
android·kotlin
繁依Fanyi4 小时前
Animaster:一次由 CodeBuddy 主导的 CSS 动画编辑器诞生记
android·前端·css·编辑器·codebuddy首席试玩官
奔跑吧 android6 小时前
【android bluetooth 框架分析 02】【Module详解 6】【StorageModule 模块介绍】
android·bluetooth·bt·aosp13·storagemodule
田一一一10 小时前
Android framework 中间件开发(三)
android·中间件·framework·jni
androidwork14 小时前
掌握 Kotlin Android 单元测试:MockK 框架深度实践指南
android·kotlin
田一一一14 小时前
Android framework 中间件开发(二)
android·中间件·framework
追随远方15 小时前
FFmpeg在Android开发中的核心价值是什么?
android·ffmpeg
神探阿航16 小时前
HNUST湖南科技大学-安卓Android期中复习
android·安卓·hnust
千里马-horse18 小时前
android vlc播放rtsp
android·media·rtsp·mediaplayer·vlc