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

相关推荐
xzkyd outpaper2 小时前
onSaveInstanceState() 和 ViewModel 在数据保存能力差异
android·计算机八股
CYRUS STUDIO3 小时前
FART 脱壳某大厂 App + CodeItem 修复 dex + 反编译还原源码
android·安全·逆向·app加固·fart·脱壳
WAsbry3 小时前
现代 Android 开发自定义主题实战指南
android·kotlin·material design
xzkyd outpaper4 小时前
Android动态广播注册收发原理
android·计算机八股
唐墨1234 小时前
android与Qt类比
android·开发语言·qt
林林要一直努力5 小时前
Android Studio 向模拟器手机添加照片、视频、音乐
android·智能手机·android studio
AD钙奶-lalala5 小时前
Mac版本Android Studio配置LeetCode插件
android·ide·android studio
散人10246 小时前
Android Test3 获取的ANDROID_ID值不同
android·unit testing
雨白6 小时前
实现动态加载布局
android
帅得不敢出门7 小时前
Android设备推送traceroute命令进行网络诊断
android·网络