Android: How to pause and resume a Count Down Timer?

后端 未结 8 1608
长发绾君心
长发绾君心 2020-12-02 17:20

I have developed a Count Down Timer and I am not sure how to pause and resume the timer as the textview for the timer is being clicked. Click to start then click again to pa

相关标签:
8条回答
  • 2020-12-02 17:48

    I wrote a extended CountDownTimer here. Let's try this and leave your comment:

    Eg:

    // Init timer
    lateinit var timerExt: CountDownTimerExt
    
    timerExt = object : CountDownTimerExt(TIMER_DURATION, TIMER_INTERVAL) {
        override fun onTimerTick(millisUntilFinished: Long) {
            Log.d("MainActivity", "onTimerTick $millisUntilFinished")
        }
    
        override fun onTimerFinish() {
            Log.d("MainActivity", "onTimerFinish")
        }
    
    }
    
    // Start/Resume timer
    timerExt.start()
    
    // Pause timer
    timerExt.pause()
    
    // Restart timer
    timerExt.restart()
    
    0 讨论(0)
  • 2020-12-02 17:49
    /*
     * Copyright (C) 2010 Andrew Gainer
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    // Adapted from Android's CountDownTimer class
    
    package com.cycleindex.multitimer;
    
    import android.os.Handler;
    import android.os.Message;
    import android.os.SystemClock;
    
    /**
     * Schedule a countdown until a time in the future, with
     * regular notifications on intervals along the way.
     *
      * The calls to {@link #onTick(long)} are synchronized to this object so that
     * one call to {@link #onTick(long)} won't ever occur before the previous
     * callback is complete.  This is only relevant when the implementation of
     * {@link #onTick(long)} takes an amount of time to execute that is significant
     * compared to the countdown interval.
     */
    public abstract class CountDownTimerWithPause {
    
        /**
         * Millis since boot when alarm should stop.
         */
      private long mStopTimeInFuture;
    
      /**
       * Real time remaining until timer completes
       */
        private long mMillisInFuture;
    
        /**
         * Total time on timer at start
         */
        private final long mTotalCountdown;
    
        /**
         * The interval in millis that the user receives callbacks
         */
        private final long mCountdownInterval;
    
        /**
         * The time remaining on the timer when it was paused, if it is currently paused; 0 otherwise.
         */
        private long mPauseTimeRemaining;
    
        /**
         * True if timer was started running, false if not.
         */
        private boolean mRunAtStart;
    
        /**
         * @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 in millis at which to execute
         *   {@link #onTick(millisUntilFinished)} callbacks
         * @param runAtStart True if timer should start running, false if not
         */
        public CountDownTimerWithPause(long millisOnTimer, long countDownInterval, boolean runAtStart) {
            mMillisInFuture = millisOnTimer;
            mTotalCountdown = mMillisInFuture;
            mCountdownInterval = countDownInterval;
            mRunAtStart = runAtStart;
        }
    
        /**
         * Cancel the countdown and clears all remaining messages
         */
        public final void cancel() {
            mHandler.removeMessages(MSG);
        }
    
        /**
         * Create the timer object.
         */
        public synchronized final CountDownTimerWithPause create() {
            if (mMillisInFuture <= 0) {
                onFinish();
            } else {
              mPauseTimeRemaining = mMillisInFuture;
            }
    
            if (mRunAtStart) {
              resume();
            }
    
            return this;
        }
    
        /**
         * Pauses the counter.
         */
      public void pause () {
        if (isRunning()) {
          mPauseTimeRemaining = timeLeft();
          cancel();
        }
      }
    
      /**
       * Resumes the counter.
       */
      public void resume () {
        if (isPaused()) {
          mMillisInFuture = mPauseTimeRemaining;
          mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
              mHandler.sendMessage(mHandler.obtainMessage(MSG));
          mPauseTimeRemaining = 0;
        }
      }
    
      /**
       * Tests whether the timer is paused.
       * @return true if the timer is currently paused, false otherwise.
       */
      public boolean isPaused () {
        return (mPauseTimeRemaining > 0);
      }
    
      /**
       * Tests whether the timer is running. (Performs logical negation on {@link #isPaused()})
       * @return true if the timer is currently running, false otherwise.
       */
      public boolean isRunning() {
        return (! isPaused());
      }
    
      /**
       * Returns the number of milliseconds remaining until the timer is finished
       * @return number of milliseconds remaining until the timer is finished
       */
      public long timeLeft() {
        long millisUntilFinished;
        if (isPaused()) {
          millisUntilFinished = mPauseTimeRemaining;
        } else {
          millisUntilFinished = mStopTimeInFuture - SystemClock.elapsedRealtime();
          if (millisUntilFinished < 0) millisUntilFinished = 0;
        }
        return millisUntilFinished;
      }
    
      /**
       * Returns the number of milliseconds in total that the timer was set to run
       * @return number of milliseconds timer was set to run
       */
      public long totalCountdown() {
        return mTotalCountdown;
      }
    
      /**
       * Returns the number of milliseconds that have elapsed on the timer.
       * @return the number of milliseconds that have elapsed on the timer.
       */
      public long timePassed() {
        return mTotalCountdown - timeLeft();
      }
    
      /**
       * Returns true if the timer has been started, false otherwise.
       * @return true if the timer has been started, false otherwise.
       */
      public boolean hasBeenStarted() {
        return (mPauseTimeRemaining <= mMillisInFuture);
      }
    
        /**
         * Callback fired on regular interval
         * @param millisUntilFinished The amount of time until finished
         */
        public abstract void onTick(long millisUntilFinished);
    
        /**
         * Callback fired when the time is up.
         */
        public abstract void onFinish();
    
    
        private static final int MSG = 1;
    
    
        // handles counting down
        private Handler mHandler = new Handler() {
    
            @Override
            public void handleMessage(Message msg) {
    
                synchronized (CountDownTimerWithPause.this) {
                    long millisLeft = timeLeft();
    
                    if (millisLeft <= 0) {
                        cancel();
                      onFinish();
                    } else if (millisLeft < mCountdownInterval) {
                        // no tick, just delay until done
                        sendMessageDelayed(obtainMessage(MSG), millisLeft);
                    } else {
                        long lastTickStart = SystemClock.elapsedRealtime();
                        onTick(millisLeft);
    
                        // take into account user's onTick taking time to execute
                        long delay = mCountdownInterval - (SystemClock.elapsedRealtime() - lastTickStart);
    
                        // special case: user's onTick took more than mCountdownInterval to
                        // complete, skip to next interval
                        while (delay < 0) delay += mCountdownInterval;
    
                        sendMessageDelayed(obtainMessage(MSG), delay);
                    }
                }
            }
        };
    }
    

    Source : This Gist.

    0 讨论(0)
  • 2020-12-02 17:50

    You can try using a library I created, Hourglass

    Hourglass hourglass = new Hourglass(50000, 1000) {
            @Override
            public void onTimerTick(long timeRemaining) {
                // Update UI
                Toast.show(MainActivity.this, String.valueOf(timeRemaining), Toast.LENGTH_SHORT).show();
            }
    
            @Override
            public void onTimerFinish() {
                // Timer finished
                Toast.show(MainActivity.this, "Timer finished", Toast.LENGTH_SHORT).show();
    
    
            }
        };
    

    Use hourglass.startTimer(); to start the timer.

    It has helper methods which allow to pause and resume the timer.

    hourglass.pauseTimer();
    

    AND

    hourglass.resumeTimer();
    
    0 讨论(0)
  • 2020-12-02 17:51

    Although the question doesn't include the Kotlin tag, I still think this answer belongs here due to Android tag.

    I've made a very simple library that uses the CountDownTimer internally and inspired by this answer. So far, it works as intended with my very basic use case:

    Pause the timer when the app/activity is paused, and resume when the app/activity resumes.

    Usage:

    class GameActivity : Activity() {
    
        private var timer = CustomTimer(30000, 1000) // 30 seconds duration at 1 second interval
    
        override fun onCreate(savedInstanceState: Bundle?) {
            ...
            timer.onTick = { millisUntilFinished ->
                textView.text = "seconds remaining: " + millisUntilFinished / 1000
            }
            timer.onFinish = {
                TODO("do something here")
            }
        }
    
        override fun onResume() {
            super.onResume()
            timer.resume()
        }
    
        override fun onPause() {
            super.onPause()
            timer.pause()
        }
    }
    

    Source code:

    Resumable CustomTimer.kt (50 lines)

    0 讨论(0)
  • 2020-12-02 17:59

    A nice and simple way to create a Pause/Resume for your CountDownTimer is to create a separate method for your timer start, pause and resume as follows:

    public void timerStart(long timeLengthMilli) {
            timer = new CountDownTimer(timeLengthMilli, 1000) {
    
                @Override
                public void onTick(long milliTillFinish) {
                    milliLeft=milliTillFinish;
                    min = (milliTillFinish/(1000*60));
                    sec = ((milliTillFinish/1000)-min*60);
                    clock.setText(Long.toString(min)+":"+Long.toString(sec));
                    Log.i("Tick", "Tock");
                }
             }
             timer.start();
    

    The timerStart has a long parameter as it will be reused by the resume() method below. Remember to store your milliTillFinished (above as milliLeft) so that you may send it through in your resume() method. Pause and resume methods below respectively:

    public void timerPause() {
            timer.cancel();
        }
    
        private void timerResume() {
            Log.i("min", Long.toString(min));
            Log.i("Sec", Long.toString(sec));
            timerStart(milliLeft);
        }
    

    Here is the code for the button FYI:

    startPause.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(startPause.getText().equals("Start")){
                        Log.i("Started", startPause.getText().toString());
                        startPause.setText("Pause");
                        timerStart(15*1000);
                    } else if (startPause.getText().equals("Pause")){
                        Log.i("Paused", startPause.getText().toString());
                        startPause.setText("Resume");
                        timerPause();
                    } else if (startPause.getText().equals("Resume")){
                        startPause.setText("Pause");
                        timerResume();
                    }
    
    0 讨论(0)
  • 2020-12-02 18:03

    I have a simple solution. All you have to do is add an extra variable which stores the current time. The only addition is currentMillis at top and in onTick(). Use cancel() to pause.
    PS: I'm using butterknife library, it's used to avoid using findviewbyid and setonclicklisteners. If you do not want to use it then you can just use the basic way of setting listeners and findviewbyid.

    @BindView(R.id.play_btn) ImageButton play;
    @BindView(R.id.pause_btn) ImageButton pause;
    @BindView(R.id.close_btn) ImageButton close;
    @BindView(R.id.time) TextView time;
    private CountDownTimer countDownTimer;
    private long currentMillis=10;
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game);
        ButterKnife.bind(this);
    }
    
    @OnClick(R.id.play_btn) void play_game(){
        this.play();
    }
    
    @OnClick(R.id.pause_btn) void pause_game(){
        this.pause();
    }
    
    @OnClick(R.id.close_btn) void close_game(){
        this.close();
    }
    
    void play(){
        play.setVisibility(View.GONE);
        close.setVisibility(View.GONE);
        pause.setVisibility(View.VISIBLE);
    
        time.setText(""+currentMillis);
        countDownTimer = new CountDownTimer(currentMillis*1000,1000) {
    
            @Override
            public void onTick(long millisUntilFinish) {
                currentMillis=millisUntilFinish/1000;
                time.setText(""+millisUntilFinish/1000);
            }
    
            @Override
            public void onFinish() {
                time.setText("Done!");
            }
        };
    
        countDownTimer.start();
    }
    
    void pause(){
        play.setVisibility(View.VISIBLE);
        close.setVisibility(View.VISIBLE);
        pause.setVisibility(View.GONE);
        countDownTimer.cancel();
    }
    
    void close(){
        if(countDownTimer!=null){
            countDownTimer.cancel();
            countDownTimer=null;
        }
        Intent intent = new Intent(this,MainActivity.class);
        startActivity(intent);
    }
    

    }

    0 讨论(0)
提交回复
热议问题