使用介紹 開發中經常會遇到一些和倒計時有關的場景,比如發送驗證碼的按鈕,會在點擊發送後,顯示倒計時間,倒計時結束後才能夠刷新按鈕,再次允許點擊。為了不阻塞軟體的運行,又要實時刷新界面,我們通常會用到 Handler 或者 AsyncTask 等技術,自己寫邏輯實現。其實 Android 中已經封裝好 ...
使用介紹
開發中經常會遇到一些和倒計時有關的場景,比如發送驗證碼的按鈕,會在點擊發送後,顯示倒計時間,倒計時結束後才能夠刷新按鈕,再次允許點擊。為了不阻塞軟體的運行,又要實時刷新界面,我們通常會用到 Handler 或者 AsyncTask 等技術,自己寫邏輯實現。其實 Android 中已經封裝好了一套 CountDownTimer 來實現這個功能需求。
CountDownTimer(long millisInFuture, long countDownInterval)
CountDownTimer的兩個參數分別表示倒計時的總時間 millisInFuture 和間隔時間 countDownInterval。
具體的調用如下:
TextView vertifyBtn;
CountDownTimer timer = new CountDownTimer(60000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
vertifyBtn.setText((millisUntilFinished / 1000) + " second");
}
@Override
public void onFinish() {
vertifyBtn.setEnabled(true);
vertifyBtn.setText("Send");
}
};
timer.start();
上面的調用舉例表示總計 60 秒,每 1 秒都會執行一次 onTick 方法,其參數 millisUntilFinished 表示倒計時剩餘時間毫秒數,最後倒計時結束執行 onFinish 方法。
實現原理
下麵是 CountDownTimer 的源碼,代碼非常少,很好理解。從源代碼中可以看出,其實 CountDownTimer 也是利用 Handler 的消息處理機制來實現效果的。初始化設定好起始和終止時間後,每隔一定的間隔時間通過 Handler 給主線程發送消息,然後再在消息處理中回調方法。好好利用官方封裝好的工具類,可以避免我們重覆的造輪子,當然瞭解輪子的原理就更好了!
package android.os;
public abstract class CountDownTimer {
private final long mMillisInFuture;
private final long mCountdownInterval;
private long mStopTimeInFuture;
private boolean mCancelled = false;
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(MSG);
}
public synchronized final CountDownTimer start() {
mCancelled = false;
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
public abstract void onTick(long millisUntilFinished);
public abstract void onFinish();
private static final int MSG = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}