核心代碼
public class VerifyCodeCountDown extends CountDownTimer {
private final Button btn;
public VerifyCodeCountDown(Button btn, long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
this.btn = btn;
}
@Override
public void onTick(long millisUntilFinished) {
btn.setText(String.format("重新獲取 (%ds)", millisUntilFinished / 1000));
btn.setEnabled(false);
btn.setBackgroundColor(Color.GRAY);
}
@Override
public void onFinish() {
btn.setText("獲取驗證碼");
btn.setEnabled(true);
btn.setBackgroundColor(Color.BLUE);
}
}
使用方式
Button btnSend = findViewById(R.id.btn_send);
btnSend.setOnClickListener(v -> {
new VerifyCodeCountDown(btnSend, 60_000, 1_000).start();
// 發送驗證碼網絡請求
sendSmsCode();
});
進階:防重複點擊 + 處理配置變更
// ViewModel 持有倒計時狀態 (LiveData)
private MutableLiveData countDown = new MutableLiveData<>(0L);
public void startCountDown() {
if (countDown.getValue() > 0) return; // 防重複
CountDownTimer timer = new CountDownTimer(60_000, 1_000) {
@Override public void onTick(long ms) { countDown.postValue(ms); }
@Override public void onFinish() { countDown.postValue(0L); }
};
timer.start();
// 綁定到 View: countDown.observe(this, ms -> updateBtn(ms));
}
常見坑
- Activity 銷毀導致倒計時丟失:用 ViewModel/Service 持有 Timer,或
onSaveInstanceState 保存剩餘時間。
- 主線程阻塞:
CountDownTimer 內部用 Handler,本身不阻塞,但 onTick 執行耗時操作會卡 UI。
- 文本閃爍:
setText 頻繁調用,可用 setText(String.format(...)) 減少 GC。