Android实现一个完美的倒计时功能
作者:键盘上的麒麟臂 发布时间:2022-04-12 11:28:02
目录
一. 已有倒计时方案存在的问题
1. CountDownTimer
2. Handler
3. Timer
二. 自己封装倒计时
总结
一. 已有倒计时方案存在的问题
在开发倒计时功能时往往我们会为了方便直接使用CountDownTimer或者使用Handler做延时来实现,当然CountDownTimer内部封装也是使用的Handler。
如果只是做次数很少的倒计时或者不需要精确的倒计时逻辑那倒没关系,比如说我只要倒计时10秒,或者我大概5分钟请求某个接口
但是如果是需要做精确的倒计时操作,比如说手机发送验证码60秒,那使用现有的倒计时方案就会存在问题。可能有些朋友没有注意到这一点,下面我们就来简单分析一下现有倒计时的问题。
1. CountDownTimer
这个可能是用得最多的,因为方便嘛。但其实倒计时每一轮倒计时完之后都是存在误差的,如果看过CountDownTimer的源码你就会知道,他的内部是有做校准操作的。(源码很简单这里就不分析了)
但是如果你认真的测试过CountDownTimer,你就会发现,即便它内部有做校准操作,他的没一轮都是有偏差,只是他最后一次倒计时完之后的总共时间和开始倒计时的时间相比没偏差。
什么意思呢,意思就是1秒,2.050秒,3.1秒......,这样的每轮偏差,导致他会出现10.95秒,下一次12秒的情况,那它的回调中如果你直接做取整就会出现少一秒的情况,但实际是没少的。
这只是其中的一个问题,你可以不根据它的回调做展示,自己用一个整形累加做展示也能解决。但是他还有个问题,有概率直接出现跳秒,就是比如3秒,下次直接5秒,这是实际的跳秒,是少了一次回调的那种。
跳秒导致你如果直接使用它可能会大问题,你可能自测的时候没发现,到时一上线应用在用户那概率跳秒,那就蛋疼了。
2. Handler
不搞这么多花里胡哨的,直接使用Handler来实现,会有什么问题。
因为直接使用handler来实现,没有校准操作,每次循环会出现几毫秒的误差,虽然比CountDownTimer的十几毫秒的误差要好,但是在基数大的倒计时情况下误差会累计,导致最终结果和现实时间差几秒误差,时间越久,误差越大
3. Timer
直接使用Timer也一样,只不过他每轮的误差更小,几轮才有1毫秒的误差,但是没有校准还是会出现误差累计,时间越久误差越大。
二. 自己封装倒计时
既然无法直接使用原生的,那我们就自己做一个。
我们基于Handler进行封装,从上面可以看出主要为了解决两个问题,时间校准和跳秒。自己写一个CountDownTimer
public class CountDownTimer {
private int mTimes;
private int allTimes;
private final long mCountDownInterval;
private final Handler mHandler;
private OnTimerCallBack mCallBack;
private boolean isStart;
private long startTime;
public CountDownTimer(int times, long countDownInterval){
this.mTimes = times;
this.mCountDownInterval = countDownInterval;
mHandler = new Handler();
}
public synchronized void start(OnTimerCallBack callBack){
this.mCallBack = callBack;
if (isStart || mCountDownInterval <= 0){
return;
}
isStart = true;
if (callBack != null){
callBack.onStart();
}
startTime = SystemClock.elapsedRealtime();
if (mTimes <= 0){
finishCountDown();
return;
}
allTimes = mTimes;
mHandler.postDelayed(runnable, mCountDownInterval);
}
private final Runnable runnable = new Runnable() {
@Override
public void run() {
mTimes--;
if (mTimes > 0){
if (mCallBack != null){
mCallBack.onTick(mTimes);
}
long nowTime = SystemClock.elapsedRealtime();
long delay = (nowTime - startTime) - (allTimes - mTimes) * mCountDownInterval;
// 处理跳秒
while (delay > mCountDownInterval){
mTimes --;
if (mCallBack != null){
mCallBack.onTick(mTimes);
}
delay -= mCountDownInterval;
if (mTimes <= 0){
finishCountDown();
return;
}
}
mHandler.postDelayed(this, 1000 - delay);
}else {
finishCountDown();
}
}
};
private void finishCountDown(){
if (mCallBack != null){
mCallBack.onFinish();
}
isStart = false;
}
public void cancel(){
mHandler.removeCallbacksAndMessages(null);
isStart = false;
}
public interface OnTimerCallBack{
void onStart();
void onTick(int times);
void onFinish();
}
}
思路就是在倒计时开始前获取一次SystemClock.elapsedRealtime(),没轮倒计时再获取一次SystemClock.elapsedRealtime()相减得到误差,根据delay校准。然后使用while循坏来处理跳秒的操作,与原生的CountDownTimer不同,这里如果跳了多少秒,就会返回多少次回调。
来源:https://www.jianshu.com/p/f1b63b212335
猜你喜欢
- Java 中的运算符与 C 语言基本一致。1、算术运算符操作符描述例子+加法 : 相加运算符两侧的值A + B 等于 30-减法 : 左操作
- 前言Sharding-JDBC是ShardingSphere的第一个产品,也是ShardingSphere的前身。它定位为轻量级Java框架
- 今天要分享一个非常简单的功能:使用Android原生控件Gallery实现照片拖动的特效实现思路如下:在布局文件中定义一个Gallery控件
- 本文代码为原创一个简陋的管理系统,只做功能的测试。并没有去完善所有应有的功能,只做了输入输出查找,仅供参考! 菜单部分: 
- 有看过我之前发表过的C#相关文章分享和阅读过我代码的朋友们可能会在我的代码里面经常看到各种各样的λ表达式动态拼接,C#
- 1、dose not point to a valid jvm installation出错问题按照以下方法设置一定可以不会出现这个错误。我
- RestTemplate未设置超时时间,导致RabbitMQ队列大量堆积,消费者假死,不进行消费,类似线程堵塞。排查:从日志排查问题,在从进
- 最近的一个Android需要用到扫码功能,用的是Zxing开源库。Zxing的集成就不说了,但是Zxing默认的是横屏扫码,在实际生产中并不
- 一、背景1.1 应用系统的架构历史1.2 什么是微服务?起源:微服务的概念源于 2014 年 3 月 Martin Fowler 所写的一篇
- 找入口对 Spring 有一定基础的同学一定知道,请求入口是DispatcherServlet,所有的请求最终都会落到doDispatch方
- 需求读200+的CSV/EXCEL文件,按文件名称存到不同数据库前期准备环境maven + jdk8 + mysql代码展示pom文件<
- RecyclerView显示Item布局不一致在自定义RecyclerAdapter的时候,在重写onCreateViewHolder方法是
- RecyclerView目前来说对大家可能不陌生了。由于在公司的项目中,我们一直用的listview和gridview。某天产品设计仿照美团
- 本文为大家分享了Android Studio debug功能的具体使用方法,供大家参考,具体内容如下运行debug模式 1. 进入
- 我们可以试想ImageView能显示图片,而VideoView就是用来显示视频的。使用VideoView播放视频的步骤如下 【1】在界面布局
- 前言记得在介绍 motion_toast 一篇的时候,开篇有一张动图,边框是渐变色而且感觉是流动的。这个动效挺有趣的,当时也有人问怎么实现,
- 其他的不多说了!我们来看看效果吧 一、实现方式一:直接引入compile方式A
- 下载工具我想没有几个人不会用的吧,前段时间比较无聊,花了点时间用java写了个简单的http多线程下载程序,纯粹是无聊才写的,只实现了几个简
- FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化
- 使用resilience4j的库和Spring Boot设计高弹性的微服务。微服务本质上是分布式的。当您使用分布式系统时,请始终记住这一第一