Android 倒计时总结

news2025/6/1 11:08:14

文章目录

  • 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.INIT
    private var handler: Handler? = Handler(Looper.getMainLooper())
    private val weekOnTick = WeakReference(onTick)
    private val weekOnFinish = WeakReference(onFinish)
    private var currentTime = 0L

    private val runnable = object : Runnable {
        override fun run() {
            if (currentTime > 0) {
                // 进行中
                currentTime -= intervalTime
                weekOnTick.get()?.invoke(currentTime)
                handler?.postDelayed(this, intervalTime)
            } else {
                // 结束
                weekOnFinish.get()?.invoke()
                runType = RunType.STOP
            }
        }
    }

    fun start() {
        if (runType == RunType.RUNNING) return
        runType = RunType.RUNNING
        if (currentTime == 0L) {
            currentTime = totalTime
        }
        handler?.post(runnable)
    }

    fun pause() {
        if (runType != RunType.RUNNING) return
        runType = RunType.PAUSE
        handler?.removeCallbacksAndMessages(null)
    }

    fun stop() {
        runType = RunType.STOP
        handler?.removeCallbacksAndMessages(null)
        currentTime = 0
        weekOnFinish.get()?.invoke()
    }

    fun release() {
        runType = RunType.INIT
        handler?.removeCallbacksAndMessages(null)
        handler = null
        weekOnTick.clear()
        weekOnFinish.clear()
    }

    enum class RunType {
        INIT, RUNNING, PAUSE, STOP
    }
}

使用:

class HandlerFragment : BaseFragment() {
    private lateinit var tvCountDown: TextView
    private lateinit var btnStart: Button
    private lateinit var btnPause: Button
    private lateinit var btnStop: Button

    companion object {
        @JvmStatic
        fun newInstance() = HandlerFragment()
    }

    private lateinit var myHandler: MyHandler

    override 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.INIT
    private val weekOnTick = WeakReference(onTick)
    private val weekOnFinish = WeakReference(onFinish)
    private var countDownTimer: CountDownTimer? = null

    fun start() {
        if (runType == RunType.RUNNING) return
        runType = RunType.RUNNING
        countDownTimer = 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.STOP
        countDownTimer?.cancel()
        weekOnFinish.get()?.invoke()
    }

    fun release() {
        countDownTimer?.cancel()
        countDownTimer = null
        weekOnTick.clear()
        weekOnFinish.clear()
    }

    enum class RunType {
        INIT, RUNNING, STOP
    }
}

使用:

class CountDownTimerFragment : BaseFragment() {
    private lateinit var tvCountDown: TextView
    private lateinit var btnStart: Button
    private lateinit var btnStop: Button

    private lateinit var countDownTimer: MyCountDownTimer

    companion object {
        @JvmStatic
        fun 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.INIT
    private val weekOnTick = WeakReference(onTick)
    private val weekOnFinish = WeakReference(onFinish)
    private var timer: Timer? = null
    private var currentTime = 0L

    fun start() {
        if (runType == RunType.RUNNING) return
        runType = RunType.RUNNING
        currentTime = totalTime
        timer = Timer()
        timer!!.schedule(object : TimerTask() {
            override fun run() {
                if (currentTime <= 0) {
                    mainHandler.post {
                        weekOnFinish.get()?.invoke()
                    }
                    cancel()
                    runType = RunType.STOP
                } else {
                    currentTime -= intervalTime
                    mainHandler.post {
                        weekOnTick.get()?.invoke(currentTime)
                    }
                }
            }
        }, 0, intervalTime)
    }

    fun stop() {
        if (runType != RunType.RUNNING) return
        runType = RunType.STOP
        timer?.cancel()
        weekOnFinish.get()?.invoke()
    }

    fun release() {
        timer?.cancel()
        timer = null
        weekOnTick.clear()
        weekOnFinish.clear()
    }

    enum class RunType {
        INIT, RUNNING, STOP
    }
}

使用:

class TimerFragment : BaseFragment() {
    private lateinit var tvCountDown: TextView
    private lateinit var btnStart: Button
    private lateinit var btnStop: Button

    private lateinit var myTimer: MyTimer

    companion object {
        @JvmStatic
        fun 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.INIT
    private val weekOnTick = WeakReference(onTick)
    private val weekOnFinish = WeakReference(onFinish)
    private var job: Job? = null
    private var currentTime = 0L

    fun start() {
        if (runType == RunType.RUNNING) return
        runType = RunType.RUNNING
        job = scope.launch {
            flow {
                currentTime = totalTime
                while (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) return
        runType = RunType.STOP
        job?.cancel()
        weekOnFinish.get()?.invoke()
    }

    fun release() {
        job?.cancel()
        job = null
        weekOnTick.clear()
        weekOnFinish.clear()
    }

    enum class RunType {
        INIT, RUNNING, STOP
    }
}

使用:

class FlowFragment : BaseFragment() {
    private lateinit var tvCountDown: TextView
    private lateinit var btnStart: Button
    private lateinit var btnStop: Button

    private lateinit var myFlow: MyFlow

    companion object {
        @JvmStatic
        fun 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实现,搭配协程更高效

  • 性能关键:避免在倒计时回调中执行耗时操作

  • 内存优化:所有方案都需注意释放资源

源码下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2392567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于 Redis 实现分布式锁:原理及注意事项

文章目录 基于 Redis 实现分布式锁&#xff1a;原理及注意事项基于 Redis 实现分布式锁的原理Redis 分布式锁的过期时间和锁续期机制如何防止锁被其他 goroutine 删除&#xff1f;Redis 分布式锁存在的单点故障问题&#xff1a;基于 RedLock 的解决方案高并发场景中 Redis 分布…

手机设备多?怎样设置IP保证不关联

在移动互联网时代&#xff0c;多设备运营&#xff08;如电商、游戏工作室、社交媒体矩阵&#xff09;常面临IP关联风险&#xff0c;轻则账号受限&#xff0c;重则封禁。以下提供6种高效设置独立IP的方法&#xff0c;结合技术原理与实操建议&#xff0c;助您打造稳定合规的运营环…

哈尔滨工业大学计算机系统大作业程序人生-Hello’s P2P

摘 要 文章以C语言程序设计经典案例hello.c为研究对象&#xff0c;系统解析程序在计算机系统中的完整生命周期。剖析源代码通过预处理、编译、汇编、链接四阶段演化为可执行目标程序的编译系统工作机制&#xff0c;继而从进程视角揭示程序运行时计算机体系结构的协同运作&…

Linux系统管理与编程24:基础条件准备-混搭“本地+阿里云”yum源

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 1.添加宿主机共享文件夹 Linux虚拟机可以和宿主机共享文件夹&#xff0c;这样有利于工具文件的共享。具体操作如下&#xff1a; 1&#xff09;vmware workstation共享文件夹 虚拟机…

如何在 Windows 10 PC 上获取 iPhone短信

您可以轻松地将媒体数据从 iPhone 传输到 Windows 计算机&#xff0c;并直接访问计算机上的数据。但是&#xff0c;您可以在 Windows 10 PC 上接收 iPhone 短信吗&#xff1f;有什么功能或工具支持它吗&#xff1f;如果您发现在 Windows 10 PC 上接收 iPhone 消息很困难&#x…

Linux 系统中的软链接与硬链接

目录 一、什么是软链接&#xff1f; 1. 创建软链接 2. 软链接的特性 3. 软链接的用途 二、什么是硬链接&#xff1f; 1. 创建硬链接 2. 硬链接的特性 3. 硬链接的用途 4. 目录硬链接的特殊性 ​编辑 三、软链接与硬链接的区别 1. inode 编号 2. 路径依赖 3. 删除行…

Python爬虫第22节- 结合Selenium识别滑动验证码实战

目录 一、引言 二、滑动验证码原理与反爬机制 2.1 验证码原理 2.2 反爬机制 三、工程实战&#xff1a;滑动验证码识别全流程 3.1 工程准备 3.1.1 环境依赖 3.1.2 目标网站与验证码识别案例 3.2 核心破解流程 3.2.1 自动化打开网页与登录 3.2.2 获取验证码图片&#…

Escrcpy(安卓手机投屏软件) v1.29.6 中文绿色版

在数字设备日益普及的今天&#xff0c;用户对于设备的控制和管理需求也在不断增加。对于Android设备用户来说&#xff0c;Escrcpy这款强大的工具无疑是一个福音。它不仅提供了直观的图形化界面&#xff0c;让用户能够轻松显示和控制自己的Android设备&#xff0c;还以完全免费开…

Linux:深入理解网络层

网络层在复杂的网络环境中确定一个合适的路径.传输到指定的网络中 一、网络层的理解 问题1&#xff1a;为什么要有网络层的概念呢&#xff1f;&#xff1f; ——>我们先来讲一个故事&#xff1a; 假设我在学校里被誉为数学大神&#xff0c;是因为我的数学有考满分的能力&…

Linux_编辑器Vim基本使用

✨✨ 欢迎大家来到小伞的大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;LInux_st 小伞的主页&#xff1a;xiaosan_blog 制作不易&#xff01;点个赞吧&#xff01;&#xff01;谢谢喵&#xff01;&a…

vue展示修改前后对比,并显示修改标注diff

动态父组件 <template><el-buttontype"primary"size"small"plainclick"showDiffDialog(subItem)">查看修改内容</el-button><TextDiffDialogv-model:visible"diffDialogVisible":before"currentDiffItem?.…

LiveWallpaperMacOS:让你的 Mac 桌面动起来

随着桌面美化需求的不断提升,用户对于桌面壁纸的要求已经不再局限于静态图片。越来越多的 Mac 用户希望桌面能像 Windows 一样,拥有动态壁纸,展现个性、提升体验。LiveWallpaperMacOS 正是这样一款让你的 Mac 桌面焕发活力的开源项目。 本文将详细介绍 LiveWallpaperMacOS …

[预训练]Encoder-only架构的预训练任务核心机制

原创文章1FFN前馈网络与激活函数技术解析&#xff1a;Transformer模型中的关键模块2Transformer掩码技术全解析&#xff1a;分类、原理与应用场景3【大模型技术】Attention注意力机制详解一4Transformer核心技术解析LCPO方法&#xff1a;精准控制推理长度的新突破5Transformer模…

07-后端Web实战(部门管理)

5. 修改部门 对于任何业务的修改功能来说&#xff0c;一般都会分为两步进行&#xff1a;查询回显、修改数据。 5.1 查询回显 5.1.1 需求 当我们点击 "编辑" 的时候&#xff0c;需要根据ID查询部门数据&#xff0c;然后用于页面回显展示。 5.1.2 接口描述 参照参照…

mysql ACID 原理

序言&#xff1a;ACID 是一组数据库设计原则&#xff0c;他是业务数据和关键业务程序的可靠性保障。 1、atomicity&#xff08;原子性&#xff09; 依赖如下能力 autocommit commit rollback2、一致性 2.1 double write buffer 1、定义&#xff1a;double write buffer 是…

[Rust_1] 环境配置 | vs golang | 程序运行 | 包管理

目录 Rust 环境安装 GoLang和Rust 关于Go 关于Rust Rust vs. Go&#xff0c;优缺点 GoLang的优点 GoLang的缺点 Rust的优点 Rust的缺点 数据告诉我们什么&#xff1f; Rust和Go的主要区别 (1) 性能 (2) 并发性 (3) 内存安全性 (4) 开发速度 (5) 开发者体验 Ru…

二十五、面向对象底层逻辑-SpringMVC九大组件之HandlerMapping接口设计

一、引言&#xff1a;MVC架构的交通枢纽 在Spring MVC框架中&#xff0c;HandlerMapping接口扮演着"请求导航仪"的关键角色&#xff0c;它决定了HTTP请求如何被路由到对应的Controller处理器。作为MVC模式的核心组件之一&#xff0c;HandlerMapping在请求处理的生命…

HUAWEI交换机配置镜像口验证(eNSP)

技术术语&#xff1a; 流量观察口&#xff1a;就是我们常说的镜像口&#xff0c;被观察的流量的引流目的端口 流量源端口&#xff1a;企业生产端口&#xff0c;作为观察口观察对象。 命令介绍&#xff1a; [核心交换机]observe-port [观察端口ID或编号&#xff08;数字&am…

前端vue3实现图片懒加载

场景和指令用法 场景:电商网站的首页通常会很长&#xff0c;用户不一定能访问到页面靠下面的图片&#xff0c;这类图片通过懒加载优化手段可以做到只有进入视口区域才发送图片请求 核心原理:图片进入视口才发送资源请求 首先&#xff1a;我们需要定义一个全局的指令&#x…

计算机网络-MPLS VPN应用场景与组网

上一篇文章我们通过一个基础实验实现了企业分支间的MPLS VPN互联&#xff0c;如果还不理解的可以多看几遍前面的文章或者多敲下实验。今天来学习几种常见的MPLS VPN应用场景与这些场景下MPLS VPN的部署方法。 一、MPLS VPN典型应用 目前&#xff0c;MPLS VPN的主要应用包括企…