[Android][倒計時]

1.下筆緣由

之前接觸過倒計時庐船,使用的是Android內置的倒計時方法CountDownTimer,這個確實挺好用的。之后我也自定義了一個倒計時的類闸溃。今天只是做一下記錄整吆。需求是希望能夠完整的觸碰到倒計時的各個狀態(tài)。從倒計時開始辉川,暫停表蝙,恢復到結束,這幾個狀態(tài)下我都能定義我的操作乓旗。

2.流程

流程圖

3.Android自帶CountDownTimer實現(xiàn)

CountDownTimer其實是通過消息機制來實現(xiàn)倒計時的,想相信了解可以查看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;// 當前狀態(tài)

    @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 初始化數(shù)據(jù)
     */
    private void initCount()
    {
        totalTime = 30;// 總時間
        currentNum = totalTime;
        timerStatus = END;// 當前狀態(tài)
    }
    /**
     * 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;//倒計時的總數(shù),這是不變的
    private long timer_couting = min*60;//要被減少的倒計時
    private static int timerStatus = LCountDownTimer.PREPARE;//當前狀態(tài)

    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);
    }
}

但是這個類有個問題就是娱据,當總時間能夠整除時間間隔的時候蚪黑,是沒什么問題的,但是如果不能整除中剩,那么就會多執(zhí)行幾秒忌穿。所以說使用的時候最好先判斷是不是能夠整除,本來是打算實現(xiàn)像CountDownTimer一樣實現(xiàn)可以配置時間間隔结啼,或許我就不應該讓時間間隔的參數(shù)可配置掠剑,直接寫死時間間隔是1s,然后直接在countDowning(long count)方法里寫我的邏輯。
例如郊愧,我們假設總時間是30秒澡腾,時間間隔是4秒,總時間明顯不能整除時間間隔糕珊,如果執(zhí)行上面的代碼动分,那么它會執(zhí)行到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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市坟乾,隨后出現(xiàn)的幾起案子迹辐,更是在濱河造成了極大的恐慌,老刑警劉巖甚侣,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件明吩,死亡現(xiàn)場離奇詭異,居然都是意外死亡殷费,警方通過查閱死者的電腦和手機印荔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來详羡,“玉大人仍律,你說我怎么就攤上這事∈的” “怎么了水泉?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長窒盐。 經(jīng)常有香客問我草则,道長,這世上最難降的妖魔是什么蟹漓? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任畔师,我火速辦了婚禮,結果婚禮上牧牢,老公的妹妹穿的比我還像新娘看锉。我一直安慰自己,他們只是感情好塔鳍,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布伯铣。 她就那樣靜靜地躺著,像睡著了一般轮纫。 火紅的嫁衣襯著肌膚如雪腔寡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天掌唾,我揣著相機與錄音放前,去河邊找鬼。 笑死糯彬,一個胖子當著我的面吹牛凭语,可吹牛的內容都是我干的。 我是一名探鬼主播撩扒,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼似扔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起炒辉,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤豪墅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后黔寇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偶器,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年缝裤,在試婚紗的時候發(fā)現(xiàn)自己被綠了屏轰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡倘是,死狀恐怖亭枷,靈堂內的尸體忽然破棺而出袭艺,到底是詐尸還是另有隱情搀崭,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布猾编,位于F島的核電站瘤睹,受9級特大地震影響,放射性物質發(fā)生泄漏答倡。R本人自食惡果不足惜轰传,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘪撇。 院中可真熱鬧获茬,春花似錦、人聲如沸倔既。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渤涌。三九已至佩谣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間实蓬,已是汗流浹背茸俭。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留安皱,地道東北人调鬓。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像酌伊,于是被迫代替她去往敵國和親袖迎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內容