等待老是讓人感到焦急和厭煩的,特別是看不到進展的等待。因此爲了避免讓用戶癡癡地等,咱們在進行某些耗時操做時,通常都要設計一個進度條或者倒計時器,讓進度可視化,告訴用戶「等待以後更精彩」。在使用短信驗證碼註冊或者登陸App就能夠看到這樣的設計:點擊「發送驗證碼」的按鈕以後,按鈕上就會出現倒計時(通常爲60秒),倒計時結束以後,按鈕的文字就會變成「從新發送」。html
在Android中要實現這樣的效果可使用Handler發送消息,但其實還有一個已經封裝好的抽象類能夠幫上忙,那就是CountDownTimer
,利用它,咱們能夠很輕鬆地實現倒計時。好久之前我就用過這個類,可是這幾天寫時發現了一個當初沒有注意到的坑,所以打算寫一篇博客記錄下來。java
瞄一眼效果圖:
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
倒計時器的使用並不難,咱們能夠建立一個類繼承它,並實現它的構造函數和重寫兩個方法:框架
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
:倒計時的時間間隔,單位爲毫秒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
方法中處理這些邏輯,倒計時完成後要將按鈕文字改成「從新發送」,這個能夠交給onFinish
。ui
運行一下,點擊按鈕,倒計時成功出現了,可是再點幾回,詭異的事情發生了:有時候倒計時讀數會漏掉某個數字,好比從10直接就到8了,打印出來的日誌是這樣的:
這……究竟是怎麼回事?少掉的一秒難道是被某人給續了麼?
爲了找回生命中的這一秒鐘,我在一個技術羣裏和小夥伴們討論了好久,最後算是逃過了時間黑洞的魔爪。
咱們採用的倒計時讀數是將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);
}複製代碼
運行後試試,就能夠發現失去的那一秒又回來啦。
給同一字符串中的不一樣字符設置不一樣的字體顏色,這就須要用到SpannableString與SpannableStringBuilder相關的知識了,限於篇幅,這裏就不贅述了,能夠參考這篇文章:SpannableString與SpannableStringBuilder。這裏只簡單介紹一下:
int time = (int) (Math.round((double) millisUntilFinished / 1000) - 1);
//拼接要顯示的字符串
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(String.valueOf(time));
sb.append("s後從新發送");複製代碼
//字符「後」在字符串中的下標
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