日常開發中在驗證碼發送等功能中會有倒計時需求,那么就通過繼承TextView實現一個倒計時控件CountdownTextView,倒計時通過Timer實現,狀態可自動切換,首先看下實現效果:
接下來通過代碼解讀一下
public class CountdownTextView extends AppCompatTextView {
private static final int THOUSAND = 1000;
private Handler mHandler = new Handler();
private String countdownText;
private String normalText;
private int countdownTime;//s
private int intervalTime = 1;//s
private Timer mCountdownTimer;
private AttributeSet mAttributeSet;
public CountdownTextView(Context context) {
this(context, null);
}
public CountdownTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CountdownTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mAttributeSet = attrs;
initView(context, attrs);
}
private void initView(Context context, @Nullable AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CountdownTextView);
countdownTime = typedArray.getInteger(R.styleable.CountdownTextView_countdownTime, 60);
intervalTime = typedArray.getInteger(R.styleable.CountdownTextView_intervalTime, 1);
normalText = typedArray.getString(R.styleable.CountdownTextView_normalText);
countdownText = typedArray.getString(R.styleable.CountdownTextView_countdownText);
if (TextUtils.isEmpty(normalText)) {
normalText = getContext().getString(R.string.get_verification_code);
}
if (TextUtils.isEmpty(countdownText)) {
countdownText = "";
}
setText(normalText);
setClickable(true);
typedArray.recycle();
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
}
/**
* 點擊自動開始計時
**/
@Override
public boolean performClick() {
startCountdown();
return super.performClick();
}
public void startCountdown() {
cancelCountdown();
setEnabled(false);
mCountdownTimer = new Timer();
mCountdownTimer.schedule(new TimerTask() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
countdownTime -= intervalTime;
if (countdownTime == 0) {
setText(normalText);
cancelCountdown();
} else {
setText(countdownText + countdownTime + "s");
}
}
});
}
}, 0, intervalTime * THOUSAND);
}
public void cancelCountdown() {
if (mCountdownTimer != null) {
mCountdownTimer.cancel();//注意:這兒必須先停止倒計時再初始化控件,否則有可能時間會亂
initView(getContext(), mAttributeSet);
setEnabled(true);
}
}
/**
* 與Window解綁自動停止計時
**/
@Override
protected void onDetachedFromWindow() {
cancelCountdown();
super.onDetachedFromWindow();
}
}
1.自定義屬性
主要定義了倒計時顯示文本內容、倒計時時長、倒計時間隔等,通過xml設置。
2.設置自動開啟和結束機制
設置點擊控件后自動開始計時,與Window解綁后自動停止計時。
開始方法是在performClick()中調用的,為啥不在點擊事件中調用呢?因為點擊事件屬于事件傳遞最終環節,而且每個控件點擊事件只能設置一次,所以若在點擊事件中設置的話只能手動調用開始計時方法了,而點擊事件會先傳遞到performClick(),然后再到onClick()方法,所以我們只要在performClick()中開啟倒計時就可以。需要說明一點,因為控件是繼承自TextView,TextView默認clickable為false,需要手動設置setClickable(true),不然事件就不會傳遞到performClick()了。
結束方法是在onDetachedFormWindow()中調用,控件與Window解綁時該方法被回調,從而自動停止倒計時。
還有一點就是在開始計時后該控件不能再次被點擊直到倒計時結束恢復初始狀態,通過setEnabled(false/true)來實現。至于為什么clickable為false就不能傳遞到點擊事件,enabled為false就不能響應點擊,具體請查看View的dispatchTouchEvent()方法。
3.控件使用
使用和TextView一樣樣的,就是setEnabled()和setClickable()別亂使用,否則就破壞整個機制了,開始計時方法startCountdown()和技術計時方法cancelCountdown()對外暴露,可隨時停止結束計時。
4.拓展
至于其他可在此機制上自由拓展,如控件樣式、字體大小等。
到這,一個簡單的倒計時控件就定義完了,讀友有疑問或不同見解可留言交流!