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

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

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

一、需求分析

  1. 点击按钮以后,按钮文字变为“ns后发送验证码”(n为倒计时读数);
  2. 为了让倒计时更加醒目,将秒数和单位设为蓝色;
  3. 倒计时结束以后,按钮的文字显示为“从新发送”。

瞄一眼效果图:
android

倒计时效果图
倒计时效果图

二、工程建立和布局编写

建立工程就不用多说了,因为咱们只须要看到按钮上的倒计时效果,没必要输入手机号码,因此只要在界面上简单地放置一个按钮便可:app

<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>复制代码

三、如何使用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() {

        }
    }复制代码

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

  • millisInFuture:倒计时的总时间,单位为毫秒
  • countDownInterval:倒计时的时间间隔,单位为毫秒
    好比我想设置10秒的倒计时,每隔1秒就读一次数,那么初始化就能够将数值传入:
    CountTimer countTimer = = new CountTimer(10000, 1000);复制代码
    除了构造函数,还有两个方法,它们的做用分别以下:
  • onTick:倒计时过程当中调用
  • onFinish:倒计时结束后调用

那么怎么开启倒计时呢?只须要用countTimer去调用start方法就能够了。另外,为了节省资源,在Activity销毁时应该中止倒计时:函数

@Override
    protected void onDestroy() {
        super.onDestroy();
        countTimer.cancel();
    }复制代码

到这里,你应该知道怎么使用如何使用CountDownTimer了吧?若是还有疑问,能够在文末下载完整的代码。布局

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

如今咱们先来实现点击按钮后就进行倒计时读数的效果,代码以下:字体

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方法中处理这些逻辑,倒计时完成后要将按钮文字改成“从新发送”,这个能够交给onFinishui

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

跳秒现象
跳秒现象

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

五、CountDownTimer偏差解决

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

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

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);
        }复制代码

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

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

给同一字符串中的不一样字符设置不一样的字体颜色,这就须要用到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

相关文章
相关标签/搜索