当前位置: 首页 > news >正文

Android笔记(三十二):封装一个毫秒级别倒计时View

效果

倒计时View视频

背景

业务场景需要显示带有毫秒级别的倒计时,于是自己封装一个通用的倒计时组件

源码分析

  1. 核心倒计时逻辑,主要是每隔100毫秒计算一次从开始倒计时到现在的剩余时间,并通过process接口返回出去
  2. Handler每次设置100毫秒的延迟
  3. 将返回出来的时间解析出来
private fun formatTimeToView(remainTime: Long) {val lengthSec = remainTime / 1000val hours = lengthSec / 3600val rem = lengthSec % 3600val minutes = rem / 60val seconds = rem % 60val milliseconds = remainTime % 1000tvMill.text = String.format("%03d", milliseconds)tvHour.text = String.format("%02d", hours)tvMin.text = String.format("%02d", minutes)tvSecond.text = String.format("%02d", seconds)
}

完整源码

class MillCountdownView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {private val root = LayoutInflater.from(context).inflate(R.layout.view_count_down, this, true)private val countDownTask: CountDownRunnableprivate val tvHour: TextViewprivate val tvMin: TextViewprivate val tvSecond: TextViewprivate val tvMill: TextViewinit {background = context.getDrawable(R.drawable.count_down_item_bg)orientation = HORIZONTALgravity = Gravity.CENTER_VERTICALtvHour = root.findViewById(R.id.tvHour)tvMin = root.findViewById(R.id.tvMin)tvSecond = root.findViewById(R.id.tvSecond)tvMill = root.findViewById(R.id.tvMill)countDownTask = CountDownRunnable(1).apply {listener = object : TaskListener {override fun finish() {tvHour.text = "00"tvMin.text = "00"tvSecond.text = "00"tvMill.text = "000"Toast.makeText(context, "倒计时结束", Toast.LENGTH_SHORT).show()}override fun process(remainTime: Long) {if (remainTime < 1) {tvHour.text = "00"tvMin.text = "00"tvSecond.text = "00"tvMill.text = "000"return}formatTimeToView(remainTime)}}}}private fun formatTimeToView(remainTime: Long) {val lengthSec = remainTime / 1000val hours = lengthSec / 3600val rem = lengthSec % 3600val minutes = rem / 60val seconds = rem % 60val milliseconds = remainTime % 1000tvMill.text = String.format("%03d", milliseconds)tvHour.text = String.format("%02d", hours)tvMin.text = String.format("%02d", minutes)tvSecond.text = String.format("%02d", seconds)}/*** 预先展示倒计时文本* @param remainTime 倒计时时间,单位毫秒*/fun preShowRemainSecs(remainTime: Long) {countDownTask.totalCountDownTime = remainTimeformatTimeToView(remainTime)}/*** 开始倒计时* @param remainTime 倒计时时间,单位毫秒*/fun startCountdown(remainTime: Long) {countDownTask.destroy()countDownTask.totalCountDownTime = remainTimecountDownTask.start()}fun destroyCountdown() {countDownTask.destroy()}
}
class CountDownRunnable(@IntRange(from = 1)var totalCountDownTime: Long) : Runnable {private val mHandler = Handler(Looper.getMainLooper())var listener: TaskListener? = nullprivate var startCountDownTime = 0L //开始时当前系统时间private var isTaskExecuting = falseoverride fun run() {if (!isTaskExecuting) {return}val dur = SystemClock.elapsedRealtime() - startCountDownTimeval remainTime = totalCountDownTime - durval mill = remainTime % 1000if (remainTime <= 0) {listener?.finish()isTaskExecuting = falsereturn} else {listener?.process(remainTime)}mHandler.postDelayed(this, 100)}fun start() {startCountDownTime = SystemClock.elapsedRealtime()mHandler.post(this)isTaskExecuting = true}fun resume() {val remainTime = totalCountDownTime - ((SystemClock.elapsedRealtime() - startCountDownTime) / 1000).toInt()if (remainTime <= 0) {listener?.finish()return}mHandler.removeCallbacks(this)isTaskExecuting = truemHandler.post(this)}fun pause() {isTaskExecuting = falsemHandler.removeCallbacks(this)}fun destroy() {isTaskExecuting = falsemHandler.removeCallbacks(this)}
}interface TaskListener {fun finish()fun process(remainTime: Long)
}
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center_vertical"android:orientation="horizontal"tools:parentTag="LinearLayout"><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/tvHour"android:layout_width="wrap_content"android:layout_height="wrap_content"android:minWidth="30dp"android:gravity="center"android:textColor="#000"android:textSize="25sp"android:textStyle="bold"android:layout_marginStart="10dp"android:layout_marginVertical="5dp"tools:text="1" /><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/divider1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginHorizontal="4dp"android:gravity="center"android:text=":"android:textColor="#000"android:textSize="25sp"android:textStyle="bold" /><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/tvMin"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="5dp"android:gravity="center"android:textColor="#000"android:minWidth="30dp"android:textSize="25sp"android:textStyle="bold"tools:text="8" /><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/divider2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginHorizontal="4dp"android:gravity="center"android:text=":"android:textColor="#000"android:textSize="25sp"android:textStyle="bold" /><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/tvSecond"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:textColor="#000"android:textSize="25sp"android:textStyle="bold"android:minWidth="30dp"tools:text="3" /><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/divider3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginHorizontal="4dp"android:gravity="center"android:text=":"android:textColor="#000"android:textSize="25sp"android:textStyle="bold" /><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/tvMill"android:layout_width="wrap_content"android:minWidth="49dp"android:layout_height="wrap_content"android:layout_marginStart="5dp"android:gravity="center"android:textColor="#a00"android:textSize="25sp"android:textStyle="bold"tools:text="000"android:layout_marginEnd="10dp"/></merge>

相关文章:

Android笔记(三十二):封装一个毫秒级别倒计时View

效果 倒计时View视频 背景 业务场景需要显示带有毫秒级别的倒计时&#xff0c;于是自己封装一个通用的倒计时组件 源码分析 核心倒计时逻辑&#xff0c;主要是每隔100毫秒计算一次从开始倒计时到现在的剩余时间&#xff0c;并通过process接口返回出去Handler每次设置100毫秒…...

[产品管理-60]:马斯洛需求层次与产品的情感化设计

目录 一、概述 1、马斯洛需求层次理论概述 2、产品情感化设计与马斯洛需求层次的关系 3、产品情感化设计的实践案例 二、马斯洛需求层次与用户情感程度&#xff08;本能、行为、反思&#xff09;的关系 1、马斯洛需求层次与用户情感程度概述 2、马斯洛需求层次与用户情感…...

Python接口自动化测试自学指南(项目实战)

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 接口自动化测试是指通过编写程序来模拟用户的行为&#xff0c;对接口进行自动化测试。Python是一种流行的编程语言&#xff0c;它在接口自动化测试中得到了广…...

ESLint 使用教程(三):12个ESLint 配置项功能与使用方式详解

前言 在现代前端开发中&#xff0c;代码质量与一致性是至关重要的&#xff0c;ESLint 正是为此而生的一款强大工具&#xff0c;本文将带您详细了解 ESLint 的配置文件&#xff0c;并通过通俗易懂的方式讲解其主要配置项及其配置方法。此外&#xff0c;我们还将探讨一些高级配置…...

如何将 EDB 文件导入 Ansys HFSS 和 Ansys Q3D

EDB 文件包含有关印刷电路板 &#xff08;PCB&#xff09; 的基本数据&#xff0c;包括其布局、组件、连接性和电磁属性。将 EDB 文件导入 Ansys 工具是利用仿真和分析功能设计高效、可靠和高性能电子系统的关键步骤。在这里&#xff0c;我将向您展示如何将 EDB 文件导入 Ansys…...

HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac

寻找模拟器 背景&#xff1a; 运行的是h5&#xff0c;模拟器是网易MuMu。 首先检查一下是否配置dab环境&#xff0c;adb version 配置一下hbuilderX的adb&#xff1a; 将命令输出的路径配置到hbuilderx里面去&#xff0c;然后重启下HbuilderX。 开始安装基座…一直安装不…...

智慧流控 力行天地 | 同元软控受邀参加第十三届全国流体传动与控制学术会议

2024年10月27日-30日&#xff0c;由中国机械工程学会流体传动与控制分会主办的第十三届全国流体传动与控制学术会议在秦皇岛召开。大会以“智慧流控 力行天地”为主题&#xff0c;来自全国高校、科研院所及企事业单位的专家学者出席本次会议。 大会围绕工程应用、新型流控元件、…...

Python日志分析与故障定位

Python日志分析与故障定位 目录 &#x1f4ca; 分布式系统日志分析&#xff1a;ELK Stack与Fluentd⚡ 实时日志流处理与异常检测&#x1f40d; 使用Python分析并处理海量日志数据&#x1f6a8; 自动化故障检测与报警系统&#x1f50d; 故障根因分析&#xff08;Root Cause An…...

w029基于springboot的网上购物商城系统研发

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0…...

Uniapp全局文件执行顺序详解

Uniapp全局文件执行顺序详解 在Uni-App项目中&#xff0c;全局文件的执行顺序对于深入理解应用的启动和初始化流程至关重要。本文将详细阐述这些文件的执行顺序&#xff0c;并提供相应的示例代码&#xff0c;以便开发者更好地理解和应用。 1. index.html 文件描述&#xff1…...

车企死亡加速,买车看好这三条线

文 | AUTO芯球 作者 | 雷慢 真不是我危言耸听&#xff0c; 新能源车是真不能随便买啊&#xff0c; 就在这几天&#xff0c;哪吒被传出要裁员70%&#xff0c; 多少车主&#xff0c;多少员工和家庭要失眠了&#xff0c; 哪吒也回应了&#xff0c;说没有裁员&#xff0c;只是精…...

SpringClud一站式学习之Eureka服务治理(二)

SpringClud一站式学习之Eureka服务治理 引言1. 搭建Eureka Server1.1. 添加Eureka Server依赖1.2. 添加 Eureka Server注解1.3. 配置Eureka Server1.4. 运行Eureka Server 2. 搭建Eureka Client 服务提供者2.1. 添加依赖2.2. 添加注解2.3. 配置Eureka Client2.4. 启动服务 3. 搭…...

空间解析几何【上】

文章目录 两向量共线&三向量共面线段定比分点内积&外积&混合积内积(点积)外积(叉积)几何性质混合积轮换对称性对换改变一次符号线性性质几何性质球面方程特点空间平面参数方程行列式方程(点位式)向量式方程三点式方程行列式方程点法式一般式截距式法式方程离…...

Python 获取PDF的各种页面信息(页数、页面尺寸、旋转角度、页面方向等)

目录 安装所需库 Python获取PDF页数 Python获取PDF页面尺寸 Python获取PDF页面旋转角度 Python获取PDF页面方向 Python获取PDF页面标签 Python获取PDF页面边框信息 了解PDF页面信息对于有效处理、编辑和管理PDF文件至关重要。PDF文件通常包含多个页面&#xff0c;每个页…...

独孤思维:曾经副业赚大钱的人,怎么不见了

01 总有一双眼睛默默关注你。 别以为自己每天做项目&#xff0c;日更文章&#xff0c;没人看。 总会有人默默观察你。 看你能坚持多久&#xff0c;看多段时间&#xff0c;你是不是还在。 今天上午&#xff0c;有个2年前认识的副业同行&#xff0c;今天突然跟我发消息。 说…...

OpenGL 异常处理-glCreateShader失败

【1】glCreateShader创建顶点着色器时候报错&#xff0c;如下 【2】原因分析 初始化失败&#xff0c;你使用一个扩extension loader library来访问现代OpenGL&#xff0c;当需要初始化它时&#xff0c;加载器需要一个当前的上下文来加载 【3】解决办法 GLenum glew_err gle…...

【el-pagination的使用及修改分页组件的整体大小修改默认样式的宽度详细教程】

今天遇到个bug&#xff0c;使用element-puls中的分页的时候&#xff0c;长度会超出盒子&#xff0c;今天教大家如何修改el-pagination的宽度&#xff0c;以及修改分页组件的整体大小 直接修改 style"width: 100%; margin-top: 10px"不生效 控制台修改el-pagination…...

Uniapp的学习

uniapp的内容和vue网页开发会有很多区别&#xff0c;但是都是基于vue开发的&#xff0c;大多数业务还是在vue打交道&#xff0c;但是这些uniapp的特殊的知识点也是要掌握好的。 基本配置 创建uniapp项目 npx degit dcloudio/uni-preset-vue#vite-ts 项目名 &#xff1a;用于…...

C#-万物之父object、装箱拆箱

万物之父&#xff1a;object 基于里氏替换原则&#xff0c;可以用object容器装载一切类型的变量。可以用来表示不确定类型&#xff0c;作为函数参数类型 object是所有类型的基类 装箱拆箱 用object存值类型&#xff08;装箱&#xff09;→ 把值类型用引用类型存储&#xff0c;…...

AI大模型重塑软件开发流程:从自动化编码到智能协作的未来展望

目录 1. 引言&#xff1a;AI大模型的崛起与软件开发的变革 1.1 AI大模型的兴起与发展背景 1.2 软件开发的现状与痛点 1.3 AI大模型如何解决这些问题 2. AI大模型的工作原理与技术背景 2.1 什么是AI大模型&#xff1f; 2.2 深度学习与自然语言处理技术的演变 2.3 大模型…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...