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

相關文章
相關標籤/搜索