Android:如何暂停和恢复倒计时计时器?

38

我已经开发了一个倒计时器,但是当该计时器的TextView被点击时,我不确定如何暂停和恢复计时器。点击开始后再次点击可以暂停,再次点击可以恢复,这个计时器的TextView需要被点击。

以下是我的代码:

    Timer = (TextView) this.findViewById(R.id.time); //TIMER  
    Timer.setOnClickListener(TimerClickListener);
    counter = new MyCount(600000, 1000);
}//end of create 

private OnClickListener TimerClickListener = new OnClickListener() {
    public void onClick(View v) {
        updateTimeTask();
    }

    private void updateTimeTask() {
        if (decision == 0) {
            counter.start();
            decision = 1;
        } else if (decision == 2) {
            counter.onResume1();
            decision = 1;
        } else {
            counter.onPause1();
            decision = 2;
        }//end if  
    }

    ;
};

class MyCount extends CountDownTimer {
    public MyCount(long millisInFuture, long countDownInterval) {
        super(millisInFuture, countDownInterval);
    }//MyCount  

    public void onResume1() {
        onResume();
    }

    public void onPause1() {
        onPause();
    }

    public void onFinish() {
        Timer.setText("00:00");
        p1++;
        if (p1 <= 4) {
            TextView PScore = (TextView) findViewById(R.id.pscore);
            PScore.setText(p1 + "");
        }//end if  
    }//finish  

    public void onTick(long millisUntilFinished) {
        Integer milisec = new Integer(new Double(millisUntilFinished).intValue());
        Integer cd_secs = milisec / 1000;

        Integer minutes = (cd_secs % 3600) / 60;
        Integer seconds = (cd_secs % 3600) % 60;

        Timer.setText(String.format("%02d", minutes) + ":"
                + String.format("%02d", seconds));
        ///long timeLeft = millisUntilFinished / 1000;  
        /}//on tick  
}//class MyCount  

protected void onResume() {
    super.onResume();
    //handler.removeCallbacks(updateTimeTask);  
    //handler.postDelayed(updateTimeTask, 1000);  
}//onResume  

@Override
protected void onPause() {
    super.onPause();
    //do stuff  
}//onPause  

这肯定会帮助你http://stackoverflow.com/questions/3510433/countdown-timer-required-on-android - Vishal Khakhkhar
http://developer.android.com/reference/android/os/CountDownTimer.html - Nikunj Patel
我需要使用Crono来暂停和恢复吗? - Mineko
请查看以下链接:https://dev59.com/Qmox5IYBdhLWcg3w95A9#8858608 - Zar E Ahmer
9个回答

35
/*
 * 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);
                }
            }
        }
    };
}

来源: 这个Gist。


正如您所看到的,该链接是一段时间以前添加的,可能已过期。 - hexin
请提供其他链接或者您可以粘贴您的代码。 - Ahmad Arslan
@RishabhSrivastava 你可以使用 create() - matteolel
1
提供一个使用该类的示例会很有帮助。 - Codelaby
@QuentinRufin 这实际上是一个警告,但它非常重要:使用 Handler 常常会导致内存泄漏。为了避免这种情况,请确保在停止调用 Handler 的活动时清除所有消息的 Hander,并使用 Handler.removeCallbacksAndMessages(null)。 - FractalBob
显示剩余5条评论

18

为您的CountDownTimer创建一个漂亮简单的暂停/恢复的方法是创建一个独立的方法来启动、暂停和恢复定时器,方法如下:

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

timerStart有一个长参数,因为此参数将在下面的resume()方法中被重复使用。请记得存储您的milliTillFinished(以上作为milliLeft),以便在resume()方法中传递它。以下是暂停和恢复方法:

public void timerPause() {
        timer.cancel();
    }

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

这里是按钮的代码,供参考:

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

12

这里没有API可以暂停或恢复计时器。你应该cancel()计时器并将剩余时间存储在一个变量中。当再次点击恢复按钮时,使用变量中的值重新启动计时器。


你可能会对计时器感兴趣。


我需要使用savedInstanceState来存储剩余时间吗?还是我可以在代码中使用这段代码:///long timeLeft = millisUntilFinished / 1000; 感谢。 - Mineko
如果您知道包含计时器的Activity已被销毁并重新创建,则必须将计时器值存储在“savedInstanceState”中。 - Reno
抱歉,我是Android编程新手。我该如何使用savedInstanceState存储计时器的值? - Mineko
点击我之前评论中的链接,查看Reto的答案。 - Reno
我自己无法应用它。你能教我如何做吗?或者有人可以教我如何做吗?谢谢! - Mineko

6

我写了一个扩展版的CountDownTimer 在这里。试试看吧,留下你的评论:

例如:

// 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()

4
您可以尝试使用我创建的库,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();


        }
    };

使用 hourglass.startTimer(); 来启动计时器。

它还有一些帮助方法,可以暂停和恢复计时器。

hourglass.pauseTimer();

AND

hourglass.resumeTimer();

2
我对你制作这个精美工具表示赞赏,但请确保披露你的关联,因为我看到这是由你制作的。 - Pritt Balagopal
@Ankush 兄弟,如果我调用 pauseTimer(),就会调用 onTimerTick()。我该如何停止它? - Zia

3

我有一个简单的解决方案。你只需要添加一个额外的变量来存储当前时间。唯一的改动是在顶部和onTick()中加入currentMillis。使用cancel()来暂停。
PS:我正在使用butterknife库,它用于避免使用findviewbyid和setonclicklisteners。如果你不想使用它,那么你可以使用基本的设置监听器和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);
}

}


2
尽管问题没有包含 Kotlin 标签,但由于有 Android 标签,我仍然认为这个答案属于这里。
我制作了一个非常简单的库,它在内部使用 CountDownTimer,并受到此 answer 的启发。到目前为止,它已经在我的非常基本的用例中按预期工作:
暂停应用程序/活动时计时器暂停,恢复应用程序/活动时计时器恢复。
使用方法:
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()
    }
}

源代码:

可恢复的CustomTimer.kt(50行)


0
    public static void setTimerMillis(Context context, long millis)
 { SharedPreferences sp = 
context.getSharedPreferences(SessionManager.FILE_USER, Context.MODE_PRIVATE); 
SharedPreferences.Editor spe = sp.edit(); spe.putLong(SessionManager.TIMER_MILLIS, millis); spe.apply(); }



       void setExamTimer() {
            setTimerColors();
            final long maxTimeToShow = 60000;            //testing
            final long lastTimeinMillisLeftSaved = TestyBookHelper.getTimerMillis(context); //take saved value from sharedpref
            final long intervalTime = 1000;
            donut_exam_timer.setMax((int) maxTimeToShow);
            CountDownTimer countDownTimer = new CountDownTimer(lastTimeinMillisLeftSaved, intervalTime) {
                @Override
                public void onTick(long millis) {
                    TestyBookHelper.setTimerMillis(context, millis);
                    String hms = String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(millis),
                            TimeUnit.MILLISECONDS.toMinutes(millis) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
                            TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));               System.out.println(hms);
                    donut_exam_timer.setText(hms);
                    donut_exam_timer.setProgress(millis);
                }
                @Override
                public void onFinish() {
                }
            };
            countDownTimer.start();
        }

  public static long getTimerMillis(Context context) {
    SharedPreferences sp = context.getSharedPreferences(SessionManager.FILE_USER, Context.MODE_PRIVATE);
    return sp.getLong(SessionManager.TIMER_MILLIS, 60000L);}

只需要将倒计时的timeLeft存储在SharePreference中,然后就可以开始了。 - Sam p
通常情况下,如果答案包含代码的意图和解决问题的原因,而不会引入其他问题,那么这些答案会更有帮助。感谢您提高答案的参考价值并使其更易于理解! - Tim Diekmann
请使用SharedPreference来保存您上次剩余的毫秒数,当您再次打开应用程序时,请获取上次保存的毫秒数。 - Sam p
如果您想在您的回答中添加更多信息,请[编辑]它。 - Tim Diekmann

0

这是我的代码。希望它简单易懂且有用。

public class MainActivity extends AppCompatActivity {

private long startMillis = 10000; //milliseconds the start value
private CountDownTimer countDownTimer;
private Button btnStart;
private boolean isStarted;
private long remainderMillis// get remaing milli seconds


protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    this.Init();
StartTime()



}

private void Init() {
    isStarted = false;
   
    remainderMillis = startMillis; // remaingMillis equals startMillis at the first start
    this.btnStart.setOnClickListener(new View.OnClickListener() { // Start pause controller button
        @Override
        public void onClick(View v) {
            if (isStarted){
                PauseGame();
            }else{
                StartGame();
            }
        }
    });
}
private void StartTimer(){
    this.countDownTimer = new CountDownTimer(remaingMillis,1000) {
        @Override
        public void onTick(long millisUntilFinished) {
            remainderMillis = millisUntilFinished;

            int totalSecs = (int)millisUntilFinished/1000; //convert to seconds
            int minutes = (totalSecs%3600)/60; // Convert to minutes
            int seconds = totalSecs%60; // Convert to seconds
            String timeString = String.format("%02d:%02d",minutes,seconds); // Formatting to mm:ss
            countDownView.setText(timeString); // Display in textview
        }

        @Override
        public void onFinish() {
    // Beeping if the timer onfinish
            ToneGenerator toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC,100);
            toneGenerator.startTone(ToneGenerator.TONE_CDMA_PIP,500);


        }
    }.start();
    btnStart.setImageResource(R.mipmap.pause); //change button image to Pause mipmap
}
private void PauseTimer(){
    isStarted = false;
    countDownTimer.cancel(); //You can cancel here the countDownTimer, but the current milliseconds store in the remainderMillis variable, so in the StartTimer() void can be contiune the timer
    btnStart.setImageResource(R.mipmap.play);


}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接