Android使用CountDownTimer实现验证码倒计时

news/2024/7/5 18:33:41 标签: 移动开发

等待总是让人感到焦急和厌烦的,特别是看不到进展的等待。所以为了不让用户痴痴地等,我们在进行某些耗时操作时,一般都要设计一个进度条或者倒计时器,让进度可视化,告诉用户“等待之后更精彩”。在使用短信验证码注册或者登录App就可以看到这样的设计:点击“发送验证码”的按钮之后,按钮上就会出现倒计时(一般为60秒),倒计时结束之后,按钮的文字就会变成“重新发送”。

在Android中要实现这样的效果可以使用Handler发送消息,但其实还有一个已经封装好的抽象类可以帮上忙,那就是CountDownTimer,利用它,我们可以很轻松地实现倒计时。很久以前我就用过这个类,但是这几天写时发现了一个当初没有注意到的坑,因此打算写一篇博客记录下来。

1、需求分析

  1. 点击按钮之后,按钮文字变为“ns后发送验证码”(n为倒计时读数);

  2. 为了让倒计时更加醒目,将秒数和单位设为蓝色;

  3. 倒计时结束之后,按钮的文字显示为“重新发送”。

瞄一眼效果图:
倒计时效果图

2、工程创建和布局编写

创建工程就不用多说了,由于我们只需要看到按钮上的倒计时效果,不必输入手机号码,所以只要在界面上简单地放置一个按钮即可:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context="com.lindroid.countdowndemo.MainActivity">

    <Button
        android:id="@+id/btn_captcha"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#c7c7c7"
        android:text="获取验证码"
        android:textAllCaps="false"
        android:textColor="@android:color/black"
        android:textSize="18sp" />

</RelativeLayout>

3、如何使用CountDownTimer

CountDownTimer倒计时器的使用并不难,我们可以创建一个类继承它,并实现它的构造函数和重写两个方法:

    private CountTimer countTimer;

    /**
     * 点击按钮后倒计时
     */
    class CountTimer extends CountDownTimer {

        public CountTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        /**
         * 倒计时过程中调用
         *
         * @param millisUntilFinished
         */
        @Override
        public void onTick(long millisUntilFinished) {

        }

        /**
         * 倒计时完成后调用
         */
        @Override
        public void onFinish() {

        }
    }

大体的框架如上所述,我来稍微解释一下。首先是构造函数,里面有两个参数:

  • millisInFuture:倒计时的总时间,单位为毫秒

  • countDownInterval:倒计时的时间间隔,单位为毫秒
    比如我想设置10秒的倒计时,每隔1秒就读一次数,那么初始化就可以将数值传入:

CountTimer countTimer = = new CountTimer(10000, 1000);

除了构造函数,还有两个方法,它们的作用分别如下:

  • onTick:倒计时过程中调用

  • onFinish:倒计时结束后调用

那么怎么开启倒计时呢?只需要用countTimer去调用start方法就可以了。另外,为了节省资源,在Activity销毁时应该停止倒计时:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        countTimer.cancel();
    }

到这里,你应该知道怎么使用如何使用CountDownTimer了吧?如果还有疑问,可以在文末下载完整的代码。

4、实现简单的倒计时效果

现在我们先来实现点击按钮后就进行倒计时读数的效果,代码如下:

    CountTimer countTimer = new CountTimer(10000, 1000);

    /**
     * 点击按钮后倒计时
     */
    class CountTimer extends CountDownTimer {

        public CountTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        /**
         * 倒计时过程中调用
         *
         * @param millisUntilFinished
         */
        @Override
        public void onTick(long millisUntilFinished) {
            Log.e("Tag", "倒计时=" + (millisUntilFinished/1000));
            btnCaptcha.setText(millisUntilFinished / 1000 + "s后重新发送");
            //设置倒计时中的按钮外观
            btnCaptcha.setClickable(false);//倒计时过程中将按钮设置为不可点击
            btnCaptcha.setBackgroundColor(Color.parseColor("#c7c7c7"));
            btnCaptcha.setTextColor(ContextCompat.getColor(context, android.R.color.black));
            btnCaptcha.setTextSize(16);
        }

        /**
         * 倒计时完成后调用
         */
        @Override
        public void onFinish() {
            Log.e("Tag", "倒计时完成");
            //设置倒计时结束之后的按钮样式
            btnCaptcha.setBackgroundColor(ContextCompat.getColor(context, android.R.color.holo_blue_light));
            btnCaptcha.setTextColor(ContextCompat.getColor(context, android.R.color.white));
            btnCaptcha.setTextSize(18);
            btnCaptcha.setText("重新发送");
            btnCaptcha.setClickable(true);
        }
    }

倒计时的读数是实时的,毫无疑问应该在onTick方法中处理这些逻辑,倒计时完成后要将按钮文字改为“重新发送”,这个可以交给onFinish

运行一下,点击按钮,倒计时成功出现了,但是再点几次,诡异的事情发生了:有时候倒计时读数会漏掉某个数字,比如从10直接就到8了,打印出来的日志是这样的:

跳秒现象

这……到底是怎么回事?少掉的一秒难道是被某人给续了么?

5、CountDownTimer误差解决

为了找回生命中的这一秒钟,我在一个技术群里和小伙伴们讨论了很久,最后算是逃过了时间黑洞的魔爪。

我们采用的倒计时读数是将millisUntilFinished除于1000得到的,这里就有一个小小的陷阱了:millisUntilFinished是长整型变量,除于1000之后得到是整数部分。我们可以将millisUntilFinished的值打印出来看看:
millisUntilFinished的值

现在明白为什么看不到读数9了吗?那是因为程序执行虽然很快,但再快也是需要时间的,所以从10秒倒计时到9秒时,millisUntilFinished会比9000稍小一点,是8999,而长整型8999除于1000之后就得到8了。当然,既然是误差那就有多种情况,少掉的数字不一定是9,这里只是我针对我遇到的情况而言。

知道原因之后就好办了,我们可以先将millisUntilFinished转换成double类型后再除于1000,这样就可以保留小数部分了,然后使用Math类中的round方法四舍五入,但是这样倒计时的话会从10到2,这显然不行,所以再减去1,让它从9到1。修改后的onTick方法代码是这样的:

        public void onTick(long millisUntilFinished) {
            //处理后的倒计时数值
            int time = (int) (Math.round((double) millisUntilFinished / 1000) - 1);
            btnCaptcha.setText(String.valueOf(time)+"s后重新发送");
            //设置倒计时中的按钮外观
            btnCaptcha.setClickable(false);//倒计时过程中将按钮设置为不可点击
            btnCaptcha.setBackgroundColor(Color.parseColor("#c7c7c7"));
            btnCaptcha.setTextColor(ContextCompat.getColor(context, android.R.color.black));
            btnCaptcha.setTextSize(16);
        }

运行后试试,就可以发现失去的那一秒又回来啦。

6、给倒计时读数和单位设置前景色

给同一字符串中的不同字符设置不同的字体颜色,这就需要用到SpannableString与SpannableStringBuilder相关的知识了,限于篇幅,这里就不赘述了,可以参考这篇文章:SpannableString与SpannableStringBuilder。这里只简单介绍一下:

6.1 拼接字符串

            int time = (int) (Math.round((double) millisUntilFinished / 1000) - 1);
            //拼接要显示的字符串
            SpannableStringBuilder sb = new SpannableStringBuilder();
            sb.append(String.valueOf(time));
            sb.append("s后重新发送");

6.2 设置要显示的文字样式

            //字符“后”在字符串中的下标
            int index = String.valueOf(sb).indexOf("后");
            //给秒数和单位设置蓝色前景色
            ForegroundColorSpan colorSpan = new ForegroundColorSpan(ContextCompat.getColor(context, android.R.color.holo_blue_dark));
            sb.setSpan(colorSpan, 0, index, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
            btnCaptcha.setText(sb);

这次运行之后就可以看到跟效果图一样的效果了。最后给一下源码:
CountDownTimerDemo

参考文章

SpannableString与SpannableStringBuilder
谷歌文档之CountDownTimer


http://www.niftyadmin.cn/n/1120690.html

相关文章

简单的Chrome 扩展开发

一、入门 这是制作chrome扩展插件的入门指南&#xff0c;不需要任何编程基础&#xff0c;看完这个后&#xff0c;我们就着手做自己的Chrome插件了。好吧&#xff0c;我们现在就开始&#xff0c;其实我也是个新手。 准备工具做任何事情都要有个工具&#xff0c;制作chrome插件需…

Html直接表单直传阿里云存储OSS示例

目的 本教程的目录是通过三个例子介绍如何在Html表单提交直传OSS第一个例子&#xff1a;讲解如何在JS直接签名&#xff0c;直接表单上传到OSS第二个例子&#xff1a;讲解如何在从后端PHP获取签名&#xff0c;然后直接表单上传到OSS第三个例子&#xff1a;讲解如何在从后端PHP…

用于增强压缩视频质量的可变形卷积密集网络

DEFORMABLE CONVOLUTION DENSE NETWORK FOR COMPRESSED VIDEO QUALITY ENHANCEMENT ABSTRACT 与传统的视频质量增强不同&#xff0c;压缩视频质量增强的目标是减少视频压缩带来的伪影。现有的多帧压缩视频质量增强方法严重依赖于光流&#xff0c;效率低&#xff0c;性能有限。…

PostgreSQL PostGIS 的5种空间距离排序(knn)算法

摘要&#xff1a; 标签 PostgreSQL , PostGIS , operator , ops , knn 背景 PostgreSQL GiST索引支持排序接口&#xff0c;可以支撑空间、标准、数组、文本向量、文本等类型的排序。标签 PostgreSQL , PostGIS , operator , ops , knn 背景 PostgreSQL GiST索引支持排序接口&am…

HDU1598 find the most comfortable road 【并查集】+【枚举】

find the most comfortable road Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3686 Accepted Submission(s): 1565Problem DescriptionXX星有很多城市&#xff0c;城市之间通过一种奇怪的快速公路SARS(Super…

XenServer安装篇一

XenServer安装篇一安装前提 1、 CPU如果是Intel&#xff0c;必须支持Intel-V&#xff0c;CPU如果是AMD&#xff0c;则必须支持AMD-V 2、 下载安装VMWare WorkStation 6.5 三、 VMWare安装前设置 1、 创建新的虚拟机&#xff0c;并选择自定义Custom&#xff08;Advanced&…

Logistic Regression 之错误翻译

根据周志华老师的讲法&#xff0c;这里 logistic 是对数几率的意思&#xff0c;所以正确的翻译方法应该叫 对数几率回归&#xff0c; 所以不要以为这个东西叫 逻辑回归&#xff0c;逻辑回归是错误的翻译。 转载于:https://www.cnblogs.com/Neo007/p/8301746.html

c++中获取代码运行时间

include<ctime> time_t begin,end; beginclock(); { .............//被测试的代码 } endclock(); cout<<"运行时间: "<<double(end-begin)/CLOCKS_PER_SEC<<endl; ******** *****注意得到的时间单位为s&#xff0c;所以精度不是很高。转载…