Android倒计时

这两天做了一个功能,输入密码错误后要间隔一定时间才能再次输入,间隔时间要显示给用户,效果如下:
Android倒计时_第1张图片
Android倒计时_第2张图片

利用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

源码:
https://github.com/acxingyun/CountDownTimer

你可能感兴趣的:(Android倒计时)