这两天做了一个功能,输入密码错误后要间隔一定时间才能再次输入,间隔时间要显示给用户,效果如下:
利用android已有的CountDownTimer实现;一开始的想法是继承CountDownTimer,构造函数中直接传入一开始显示的文字和结束后的文字,以及显示文字的控件,在这个类内部自己解析文字,把里面时间相关的,比如“1分钟”、“100秒”用剩余时间代替,时间变化时调用控件的setText()方法。做好后发现这个类功能太繁琐,相当于是把倒计时和解析文字合并到一起了,不如把这两个功能分开,倒计时就只做倒计时,文字解析用另外的函数,当时间变化时,通过回调到activity,在activity中做文字处理和显示,这样使得功能解耦和,并且不需要传入activity中的textview,可以实现计时器的单例。
下面是计时器的代码:
public class MyCountDownTimer extends CountDownTimer {
private static final String CLASS_TAG = "MyCountDownTimer";
private static Lock mLock = new ReentrantLock();
private CountDownCallback mCountDownCallback;
private static MyCountDownTimer ins;
public static MyCountDownTimer getIns(long millisInFuture, long countDownInterval, CountDownCallback countDownCallback){
if (mLock.tryLock()){
try{
if (ins == null){
ins = new MyCountDownTimer(millisInFuture, countDownInterval, countDownCallback);
}
}catch (Exception e){
Log.e(CLASS_TAG, "exception:" + e);
}finally {
mLock.unlock();
Log.i(CLASS_TAG, "mLock.unlock()");
}
}
Log.i(CLASS_TAG, "ins:" + ins);
return ins;
}
/**
* @param millisInFuture The number of millis in the future from the call
* to {@link #start()} until the countdown is done and {@link #onFinish()}
* is called.
* @param countDownInterval The interval along the way to receive
* {@link #onTick(long)} callbacks.
*/
private MyCountDownTimer(long millisInFuture, long countDownInterval, CountDownCallback countDownCallback) {
super(millisInFuture, countDownInterval);
mCountDownCallback = countDownCallback;
mCountDownCallback.startCountDown(millisInFuture);
}
@Override
public void onTick(long millisUntilFinished) {
mCountDownCallback.onCountDown(millisUntilFinished);
}
@Override
public void onFinish() {
mCountDownCallback.endCountDown();
}
interface CountDownCallback {
void startCountDown(long millisInFuture);
void onCountDown(long millisUntilFinished);
void endCountDown();
}
}
使用时,在activity需要一个callback,并传入给构造函数:
private MyCountDownTimer.CountDownCallback countDownCallback = new MyCountDownTimer.CountDownCallback() {
@Override
public void startCountDown(long millisInFuture) {
textView.setText("开始倒计时");
}
@Override
public void onCountDown(long millisUntilFinished) {
textView.setText("剩余时间" + Utils.millisToChinese(millisUntilFinished));
}
@Override
public void endCountDown() {
textView.setText("倒计时结束");
}
};
调用计时器:
MyCountDownTimer myCountDownTimer = MyCountDownTimer.getIns(50 * 1000, 1000, countDownCallback);
myCountDownTimer.start();
还有个用来解析时间的方法,传入毫秒时间,转换成xx小时xx分钟xx秒,放在工具类:
public class Utils {
/**
* 毫秒转换中文时间
* @param ms
* @return
*/
public static String millisToChinese(long ms) {
int ss = 1000;
int mi = ss * 60;
int hh = mi * 60;
int dd = hh * 24;
long day = ms / dd;
long hour = (ms - day * dd) / hh;
long minute = (ms - day * dd - hour * hh) / mi;
long second = (ms - day * dd - hour * hh - minute * mi) / ss;
StringBuilder stringBuilder = new StringBuilder();
if (day >= 1){
stringBuilder.append(day).append("天").append(hour).append("小时").append(minute).append("分钟").append(second).append("秒");
}else if (hour >= 1){
stringBuilder.append(hour).append("小时").append(minute).append("分钟").append(second).append("秒");
}else if (minute >= 1){
stringBuilder.append(minute).append("分钟").append(second).append("秒");
}else if (second >= 1){
stringBuilder.append(second).append("秒");
}
return stringBuilder.toString();
}
}
用到了单例模式构造计时器,开始倒计时后再次点击开始倒计时会重新开始计时;如果没有使用单例,多次点击会创建多个计时器,这样每个计时器时间变化后,文字会叠加到控件上。
下面是多次点击开始计时的打印,说明确实是单例,并且finnally中释放锁的操作在return前执行:
03-01 03:13:11.028 2893-2893/com.acxingyun.countdowntimer I/MainActivity: btnClicked...
03-01 03:13:11.031 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: mLock.unlock()
03-01 03:13:11.031 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: ins:com.acxingyun.countdowntimer.MyCountDownTimer@443b05d
03-01 03:13:14.054 2893-2893/com.acxingyun.countdowntimer I/MainActivity: btnClicked...
03-01 03:13:14.055 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: mLock.unlock()
03-01 03:13:14.055 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: ins:com.acxingyun.countdowntimer.MyCountDownTimer@443b05d
03-01 03:13:15.687 2893-2893/com.acxingyun.countdowntimer I/MainActivity: btnClicked...
03-01 03:13:15.687 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: mLock.unlock()
03-01 03:13:15.687 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: ins:com.acxingyun.countdowntimer.MyCountDownTimer@443b05d
03-01 03:13:23.805 2893-2893/com.acxingyun.countdowntimer I/MainActivity: btnClicked...
03-01 03:13:23.805 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: mLock.unlock()
03-01 03:13:23.805 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: ins:com.acxingyun.countdowntimer.MyCountDownTimer@443b05d
03-01 03:18:25.615 2893-2893/com.acxingyun.countdowntimer I/MainActivity: btnClicked...
03-01 03:18:25.615 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: mLock.unlock()
03-01 03:18:25.615 2893-2893/com.acxingyun.countdowntimer I/MyCountDownTimer: ins:com.acxingyun.countdowntimer.MyCountDownTimer@443b05d