preface

The native countdown function is relatively simple, there is no pause and restart timer function, so copy the native CountDownTimer to make a more useful countdown function.

Custom class

public class LCountDownTimer {

    /** ** time, i.e. the start time, commonly known as the total countdown time */
    private long mMillisInFuture;
    /** * Boolean value indicating whether the timer is cancelled * is set to true only when cancel is called
    private boolean mCancelled = false;
    /** * The interval at which the user receives the callback, usually 1 second */
    private long mCountdownInterval;
    /** * Record the pause time */
    private long mStopTimeInFuture;
    /** * mas.what value */
    private static final int MSG = 520;
    /** ** pause when the remaining time */
    private long mCurrentMillisLeft;
    /** * Pause is set to true only when pause is called
    private boolean mPause = false;
    /** * listener */
    private TimerListener mCountDownListener;
    /** * Whether to create start */
    private boolean isStart;

    private LCountDownTimer(){
        isStart = true;
    }

    public LCountDownTimer(long millisInFuture, long countdownInterval) {
        long total = millisInFuture + 20;
        this.mMillisInFuture = total;
        //this.mMillisInFuture = millisInFuture;
        this.mCountdownInterval = countdownInterval;
        isStart = true;
    }

    /** * start the countdown, each click, will restart */
    public synchronized final void start() {
        if (mMillisInFuture <= 0 && mCountdownInterval <= 0) {
            throw new RuntimeException("you must set the millisInFuture > 0 or countdownInterval >0");
        }
        mCancelled = false;
        long elapsedRealtime = SystemClock.elapsedRealtime();
        mStopTimeInFuture = elapsedRealtime + mMillisInFuture;
        mPause = false;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        if(mCountDownListener! =null){ mCountDownListener.onStart(); }}/** * cancel the timer */
    public synchronized final void cancel() {
        if(mHandler ! =null) {
            / / pause
            mPause = false;
            mHandler.removeMessages(MSG);
            / / cancel
            mCancelled = true;
            if(mCountDownListener! =null){ mCountDownListener.onCancel(); }}}/** * press pause, press again to continue countdown */
    public synchronized final void pause() {
        if(mHandler ! =null) {
            if (mCancelled) {
                return;
            }
            if (mCurrentMillisLeft < mCountdownInterval) {
                return;
            }
            if(! mPause) { mHandler.removeMessages(MSG); mPause =true;
                if(mCountDownListener! =null){ mCountDownListener.onPause(); }}}}/** * resume pause, start */
    public synchronized final  void resume() {
        if (mMillisInFuture <= 0 && mCountdownInterval <= 0) {
            throw new RuntimeException("you must set the millisInFuture > 0 or countdownInterval >0");
        }
        if (mCancelled) {
            return;
        }
        // The remaining time is less than that
        if(mCurrentMillisLeft < mCountdownInterval || ! mPause) {return;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mCurrentMillisLeft;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        mPause = false;
        if(mCountDownListener! =null){ mCountDownListener.onResume(); }}@SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            synchronized (LCountDownTimer.this) {
                if (mCancelled) {
                    return;
                }
                // Number of milliseconds left
                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
                if (millisLeft <= 0) {
                    mCurrentMillisLeft = 0;
                    if(mCountDownListener ! =null) { mCountDownListener.onFinish(); }}else if (millisLeft < mCountdownInterval) {
                    mCurrentMillisLeft = 0;
                    // If the remaining time is less than one interval, no notification will be given
                    sendMessageDelayed(obtainMessage(MSG), millisLeft);
                } else {
                    // Have time to spare
                    long lastTickStart = SystemClock.elapsedRealtime();
                    if(mCountDownListener ! =null) {
                        mCountDownListener.onTick(millisLeft);
                    }
                    mCurrentMillisLeft = millisLeft;
                    // Consider how long the user's onTick takes to execute
                    // Print this delay, about 997 milliseconds
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
                    // Special case: if the user's onTick method takes longer than interval, skip to the next interval
                    // Note that in the onTick callback method, do not do time-consuming operations
                    boolean isWhile = false;
                    while (delay < 0){
                        delay += mCountdownInterval;
                        isWhile = true;
                    }
                    if(isWhile){ } sendMessageDelayed(obtainMessage(MSG), delay); }}}};/** * Set the total countdown time *@paramMillisieverts */
    public void setMillisInFuture(long millisInFuture) {
        long total = millisInFuture + 20;
        this.mMillisInFuture = total;
    }

    /** * Sets the countdown interval value *@paramCountdownInterval Specifies the interval. The value is 1000 milliseconds */
    public void setCountdownInterval(long countdownInterval) {
        this.mCountdownInterval = countdownInterval;
    }

    /** * set timer listener *@param countDownListener                 listener
     */
    public void setCountDownListener(TimerListener countDownListener) {
        this.mCountDownListener = countDownListener; }}Copy the code

Abstract classes used:

public abstract class TimerListener {

    /** ** when the countdown starts */
   public void onStart(){

   }

    /** * when the countdown resumes pause */
   public void onResume(){

   }

    /** * when the countdown is paused */
   public void onPause(){

   }

    /** ** When the countdown ends */
    public void onFinish(){

    }

    /** ** When the countdown is cancelled */
    public void onCancel(){

    }
    /** Countdown is in progress *@paramZipuntilfinished */
    public abstract void onTick(long millisUntilFinished);

}
Copy the code

Use the sample

It’s easy to use, so here’s a simple demo.

class CountDownActivity: AppCompatActivity(R.layout.activity_countdown) {
    // Initialize the countdown
    private val mLTime by lazy {
        LCountDownTimer(9*1000 + 100.1000)}override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)

        btnCountdownStart.setOnClickListener {
            mLTime.start()
        }
        btnCountdownPause.setOnClickListener {
            mLTime.pause()
        }
        btnCountdownResume.setOnClickListener {
            mLTime.resume()
        }
        btnCountdownCancel.setOnClickListener {
            mLTime.cancel()
        }
        btnCountdownStart2.setOnClickListener {
            mLTime.start()
        }

        // Time monitor
        mLTime.setCountDownListener(object :TimerListener(){
            override fun onTick(millisUntilFinished: Long) {
                Log.e(TAG, "onTick: $millisUntilFinished");
                tvCountDownTime.text = "Countdown:${millisUntilFinished/1000}"
            }

            override fun onStart(a) {
                super.onStart()
                Log.e(TAG, "onStart: ");
            }

            override fun onResume(a) {
                super.onResume()
                Log.e(TAG, "onResume: ");
            }

            override fun onPause(a) {
                super.onPause()
                Log.e(TAG, "onPause: ");
            }

            override fun onFinish(a) {
                super.onFinish()
                Log.e(TAG, "onFinish: ");
                tvCountDownTime.text = "The countdown is over."
            }

            override fun onCancel(a) {
                super.onCancel()
                Log.e(TAG, "onCancel: ");
                tvCountDownTime.text = "Countdown canceled."}})}override fun onDestroy(a) {
        super.onDestroy()
        mLTime.cancel()
    }
}
Copy the code