[Android][倒计时]

1.下笔缘由

之前接触过倒计时,使用的是Android内置的倒计时方法CountDownTimer,这个确实挺好用的。之后我也自定义了一个倒计时的类。今天只是做一下记录。需求是希望能够完整的触碰到倒计时的各个状态。从倒计时开始,暂停,恢复到结束,这几个状态下我都能定义我的操作。

2.流程

流程图

3.Android自带CountDownTimer实现

CountDownTimer其实是通过消息机制来实现倒计时的,想相信了解可以查看CountDownTimer类的源码。
CountDownTimer使用起来很简单,只需要实例化一个CountDownTimer(总时间, 时间间隔)对象,在onTick(long millisUntilFinished)每隔n秒(也就是时间间隔)会回调一次方法onTick,如果没有调用cancel(),那么倒计时结束的时候会调用onFinish()方法。
开始倒计时:countDownTimer.start();
取消倒计时:countDownTimer.cancel();

public class MainActivity extends Activity implements OnClickListener
{
    public static final int END = 0;
    public static final int START = 1;
    public static final int PASUSE = 2;
    public static final int RESUME = 3;
    private Button btnStart, btnCancel, btnPause, btnResume;
    private CountDownTimer countDownTimer = null;
    private int totalTime = 30;// 总时间
    private int countNum = 1;//间隔时间。每隔n秒会回调一次方法onTick
    private int min = 1000;// 1秒
    private int currentNum = -1;
    private static int timerStatus = END;// 当前状态

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("lgy", "==================onCreate");
        btnStart = (Button) findViewById(R.id.cbtn_start);
        btnStart.setOnClickListener(this);
        btnCancel = (Button) findViewById(R.id.cbtn_cancel);
        btnCancel.setOnClickListener(this);
        btnPause = (Button) findViewById(R.id.cbtn_pause);
        btnPause.setOnClickListener(this);
        btnResume = (Button) findViewById(R.id.cbtn_resume);
        btnResume.setOnClickListener(this);
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        Log.i("lgy", "==================onDestroy");
    }

    @Override
    public void onClick(View v)
    {
        switch (v.getId())
        {
        case R.id.cbtn_start:
            initCount();

            if (countDownTimer != null)
            {
                countDownTimer.cancel();
                countDownTimer = null;
            }
            initCountDownTimer();
            countDownTimer.start();
            timerStatus = START;
            break;
        case R.id.cbtn_cancel:
            if (countDownTimer != null)
            {
                countDownTimer.cancel();
                countDownTimer = null;
            }
            timerStatus = END;
            break;
        case R.id.cbtn_pause:
            if (timerStatus == START || timerStatus == PASUSE)
            {
                countDownTimer.cancel();
                countDownTimer = null;
                timerStatus = PASUSE;
            }
            break;
        case R.id.cbtn_resume:

            if (timerStatus == PASUSE)
            {
                if (currentNum > 0)
                {
                    initCountDownTimer();
                }
                countDownTimer.start();
                timerStatus = RESUME;
            }
            break;
        }
    }

    /**
     * Administrator
     * 2017-1-9
     *TODO 初始化数据
     */
    private void initCount()
    {
        totalTime = 30;// 总时间
        currentNum = totalTime;
        timerStatus = END;// 当前状态
    }
    /**
     * Administrator
     * 2017-1-9
     *TODO 实例化CountDownTimer对象
     */
    private void initCountDownTimer()
    {
        countDownTimer = new CountDownTimer(currentNum * min,
                countNum * min)
        {

            @Override
            public void onTick(long millisUntilFinished)
            {
                currentNum = (int) (millisUntilFinished / 1000);
                Log.i("lgy", "==================onTick:"
                        + millisUntilFinished / 1000);
            }

            @Override
            public void onFinish()
            {
                Log.i("lgy", "==================onFinish");
            }
        };
    }
}

4.自定义倒计时

首先先定义一个接口,是为了能在倒计时各个阶段都能自定义我的操作:

/**
 * Created by LGY on 2016/11/28.
 */
public interface CountDownCallback {
    public void start();
    public void pause();
    public void resume();
    public void cancel();
    public void countDowning(long count);
    public void end();
}

然后自定义倒计时控件:

/**
 * Created by LGY on 2016/11/28.
 */
public class LCountDownTimer {
    public static final int PREPARE = 0;
    public static final int START = 1;
    public static final int PASUSE = 2;
    public static final int RESUME = 3;
    private long countDownInterval = 1;//时间间隔
    private int min = 1000;// 1秒
    private long distination_total = min*60;//倒计时的总数,这是不变的
    private long timer_couting = min*60;//要被减少的倒计时
    private static int timerStatus = LCountDownTimer.PREPARE;//当前状态

    private CountDownCallback callback = null;

    private Timer timer;
    private TimerTask timerTask;
    public LCountDownTimer(CountDownCallback callback)
    {
        this.callback = callback;
    }
    /**
     * start count down
     * @throws Exception The counted number is less than zero!
     */
    public void startTimer() throws Exception{
        if (distination_total<=0)
        {
            throw new Exception("The counted number is less than zero!");
        }
        initTimerStatus();
        timerStatus = LCountDownTimer.START;
        if(callback!=null)
            callback.start();
        countTimer();
    }

    /**
     * cancel count down
     */
    public void cancelTimer(){
        if (timer!=null)
        {
            timer.cancel();
            timer = null;
        }
        if (timerTask!=null)
        {
            timerTask.cancel();
            timerTask = null;
        }
        callback.cancel();
    }
    /**
     * pause count down
     */
    public void pauseTimer(){
        if (timer==null)
            return;
        timer.cancel();
        timerTask.cancel();
        callback.pause();
        timerStatus = LCountDownTimer.PASUSE;
    }

    /**
     * resume count down
     */
    public void resumeTimer(){
        if (timer==null||timerStatus!=LCountDownTimer.PASUSE)
            return;
        countTimer();
        callback.resume();
        timerStatus = LCountDownTimer.RESUME;
    }
    /**
     * initialize
     */
    private void initTimerStatus(){
        if (timer!=null)
        {
            timer.cancel();
            timer = null;
        }
        if (timerTask!=null)
        {
            timerTask.cancel();
            timerTask = null;
        }
        timerStatus = LCountDownTimer.PREPARE;
        timer_couting = distination_total;
    }

    public int getTimerStatus()
    {
        return timerStatus;
    }

    /**
     * set the total time
     * @param timer_couting
     */
    public void setTimer_couting(long timer_couting)
    {
        this.timer_couting = timer_couting*min;
        this.distination_total = timer_couting*min;
    }
    

    /**
     * Administrator
     * 2017-1-9
     *TODO set the countDown Interval
     * @param countDownInterval
     */
    public void setCountDownInterval(long countDownInterval)
    {
        this.countDownInterval = countDownInterval*min;
    }
    /**
     * counting
     */
    private void countTimer()
    {
        timer = new Timer();
        timerTask = new TimerTask()
        {
            @Override
            public void run() {

                if(timer_couting<=0){
                    callback.end();
                    initTimerStatus();
                    return;
                }
                if(callback!=null)
                    callback.countDowning(timer_couting);
                timer_couting -=countDownInterval;
            }
        };
        timer.scheduleAtFixedRate(timerTask, 0, countDownInterval);
    }
}

但是这个类有个问题就是,当总时间能够整除时间间隔的时候,是没什么问题的,但是如果不能整除,那么就会多执行几秒。所以说使用的时候最好先判断是不是能够整除,本来是打算实现像CountDownTimer一样实现可以配置时间间隔,或许我就不应该让时间间隔的参数可配置,直接写死时间间隔是1s,然后直接在countDowning(long count)方法里写我的逻辑。
例如,我们假设总时间是30秒,时间间隔是4秒,总时间明显不能整除时间间隔,如果执行上面的代码,那么它会执行到32秒。所以,可以直接在countDowning(long count)中定义逻辑,这么做就不会多运行2秒了。

    @Override
    public void countDowning(long count) {
        if (count!=0&&(((count/1000+2)%4)==0))
        {
            Log.i("lgy","count:"+count);
        }
    }

最后是调用这个自定义倒计时控件的例子:

public class MainActivity3 extends Activity implements CountDownCallback,View.OnClickListener {

    //ui countdown
    private Button btnStart,btnCancel,btnPause,btnResume;
    private LCountDownTimer mLCountDownTimer = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        btnStart = (Button) findViewById(R.id.btn_start);
        btnStart.setOnClickListener(this);
        btnCancel = (Button) findViewById(R.id.btn_cancel);
        btnCancel.setOnClickListener(this);
        btnPause = (Button) findViewById(R.id.btn_pause);
        btnPause.setOnClickListener(this);
        btnResume = (Button) findViewById(R.id.btn_resume);
        btnResume.setOnClickListener(this);
        mLCountDownTimer = new LCountDownTimer(this);

        mLCountDownTimer.setTimer_couting(30);
        mLCountDownTimer.setCountDownInterval(1);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start:
            try
            {
                mLCountDownTimer.startTimer();
            } catch (Exception e)
            {
                e.printStackTrace();
            }
                break;
            case R.id.btn_cancel:
                mLCountDownTimer.cancelTimer();
                break;
            case R.id.btn_pause:
                mLCountDownTimer.pauseTimer();
                break;
            case R.id.btn_resume:
                mLCountDownTimer.resumeTimer();
                break;
        }
    }

    @Override
    public void start() {
        Log.i("lgy","start================");
    }

    @Override
    public void pause() {
        Log.i("lgy","pause================");
    }

    @Override
    public void resume() {
        Log.i("lgy","resume================");
    }

    @Override
    public void cancel() {
        Log.i("lgy","cancel================");
    }

    @Override
    public void countDowning(long count) {
//      if (count!=0&&(((count/1000+2)%4)==0))
//      {
//            Log.i("lgy","count:"+count);
//      }
        Log.i("lgy","count:"+count);
    }

    @Override
    public void end()
    {
        Log.i("lgy","end================");
    }
}

5.源码地址

https://github.com/lgygg/CountDownTimer

6.参考文章

http://blog.csdn.net/arjinmc/article/details/47775995

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容