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

相关推荐
安东尼肉店3 小时前
Android compose屏幕适配终极解决方案
android
2501_916007473 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
feiyangqingyun4 小时前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户2018792831678 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子8 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
小趴菜82278 小时前
安卓接入Max广告源
android
齊家治國平天下8 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO9 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel9 小时前
Android 安卓RIL介绍
android·安卓·ril
写点啥呢9 小时前
android12解决非CarProperty接口深色模式设置后开机无法保持
android·车机·aosp·深色模式·座舱