前言
等待總是讓人感到焦急和厭煩的,特別是看不到進展的等待。所以為了不讓用戶癡癡地等,我們在進行某些耗時操作時,一般都要設計一個進度條或者倒計時器,讓進度可視化,告訴用戶“等待之后更精彩”。在使用短信驗證碼注冊或者登錄App就可以看到這樣的設計:點擊“發送驗證碼”的按鈕之后,按鈕上就會出現倒計時(一般為60秒),倒計時結束之后,按鈕的文字就會變成“重新發送”。
在Android中要實現這樣的效果可以使用Handler發送消息,但其實還有一個已經封裝好的抽象類可以幫上忙,那就是CountDownTimer,利用它,我們可以很輕松地實現倒計時。很久以前我就用過這個類,但是這幾天寫時發現了一個當初沒有注意到的坑,因此打算寫一篇博客記錄下來。
1、需求分析
瞄一眼效果圖:

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() { } }大體的框架如上所述,我來稍微解釋一下。首先是構造函數,里面有兩個參數:
比如我想設置10秒的倒計時,每隔1秒就讀一次數,那么初始化就可以將數值傳入:
CountTimer countTimer = = new CountTimer(10000, 1000);
除了構造函數,還有兩個方法,它們的作用分別如下:
那么怎么開啟倒計時呢?只需要用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的值打印出來看看:

現在明白為什么看不到讀數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
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。
新聞熱點
疑難解答