I am trying to create a timer with a circle progress bar. I have found a good example to begin with http://www.androidtutorialshub.com/android-count-down-timer-tutorial/. This example has the looks that I need but I want to add pause and resume functionality to it. So, I do have the pause and resume working, however after I click "Pause" and then "Resume" the progress bar starts from scratch for a spit of a second and then resumes from the position pause was set on(which is the goal). My guess is that the progress bar is set to 100% somewhere and then replaced with pause position, but I cant find the part where that might happen.
Modified mainActivity code:
public class MainActivity extends AppCompatActivity {
private long timeCountInMilliSeconds = 1 * 60000;
long startTime;
long difference;
long difference1;
private enum TimerStatus {
STARTED,
STOPPED,
}
private TimerStatus timerStatus = TimerStatus.STOPPED;
private ProgressBar progressBarCircle;
private EditText editTextMinute;
private TextView textViewTime;
private ImageView imageViewReset;
private ImageView imageViewStartStop;
private CountDownTimer countDownTimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBarCircle = (ProgressBar) findViewById(R.id.progressBarCircle);
editTextMinute = (EditText) findViewById(R.id.editTextMinute);
textViewTime = (TextView) findViewById(R.id.textViewTime);
imageViewReset = (ImageView) findViewById(R.id.imageViewReset);
imageViewStartStop = (ImageView) findViewById(R.id.imageViewStartStop);
imageViewReset.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
reset();
}
});
imageViewStartStop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
pauseResume();
}
});
}
private void reset() {
stopCountDownTimer();
restartCountDownTimer();
}
private void pauseResume() {
if (timerStatus == TimerStatus.STOPPED) {
timeCountInMilliSeconds = 1 * 50000;
// setProgressBarValues(timeCountInMilliSeconds);
progressBarCircle.setMax((int) (timeCountInMilliSeconds) / 1000);
progressBarCircle.setProgress((int) (timeCountInMilliSeconds) / 1000);
imageViewReset.setVisibility(View.VISIBLE);
imageViewStartStop.setImageResource(R.drawable.icon_stop);
editTextMinute.setEnabled(false);
timerStatus = TimerStatus.STARTED;
startCountDownTimer();
startTime = System.currentTimeMillis();
} else {
imageViewReset.setVisibility(View.GONE);
imageViewStartStop.setImageResource(R.drawable.icon_start);
editTextMinute.setEnabled(true);
timerStatus = TimerStatus.STOPPED;
stopCountDownTimer();
if(difference == 0) {
difference = System.currentTimeMillis() - startTime;
}
else{
difference1 = System.currentTimeMillis() - startTime;
difference += difference1;}
}
}
private void startCountDownTimer() {
countDownTimer = new CountDownTimer(timeCountInMilliSeconds - difference, 1000) {
#Override
public void onTick(long millisUntilFinished) {
textViewTime.setText(hmsTimeFormatter(millisUntilFinished));
progressBarCircle.setProgress((int) (millisUntilFinished / 1000));
}
#Override
public void onFinish() {
textViewTime.setText(hmsTimeFormatter(timeCountInMilliSeconds - difference));
// setProgressBarValues(timeCountInMilliSeconds - difference);
progressBarCircle.setMax((int) (timeCountInMilliSeconds - difference) / 1000);
progressBarCircle.setProgress((int) (timeCountInMilliSeconds - difference) / 1000);
imageViewReset.setVisibility(View.GONE);
imageViewStartStop.setImageResource(R.drawable.icon_start);
editTextMinute.setEnabled(true);
timerStatus = TimerStatus.STOPPED;
}
}.start();
countDownTimer.start();
}
private void restartCountDownTimer() {
difference = 0;
difference1 = 0;
countDownTimer = new CountDownTimer(timeCountInMilliSeconds, 1000) {
#Override
public void onTick(long millisUntilFinished) {
textViewTime.setText(hmsTimeFormatter(millisUntilFinished));
progressBarCircle.setProgress((int) (millisUntilFinished / 1000));
}
#Override
public void onFinish() {
textViewTime.setText(hmsTimeFormatter(timeCountInMilliSeconds));
// setProgressBarValues(timeCountInMilliSeconds);
progressBarCircle.setMax((int) (timeCountInMilliSeconds) / 1000);
progressBarCircle.setProgress((int) (timeCountInMilliSeconds) / 1000);
imageViewReset.setVisibility(View.GONE);
imageViewStartStop.setImageResource(R.drawable.icon_start);
editTextMinute.setEnabled(true);
timerStatus = TimerStatus.STOPPED;
}
}.start();
countDownTimer.start();
}
private void stopCountDownTimer() {
countDownTimer.cancel();
}
private String hmsTimeFormatter(long milliSeconds) {
String hms = String.format("%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(milliSeconds),
TimeUnit.MILLISECONDS.toMinutes(milliSeconds) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milliSeconds)),
TimeUnit.MILLISECONDS.toSeconds(milliSeconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliSeconds)));
return hms;
}
}
The XML file is the same as in the link provided above(the link to the sample code).
Any help would be very much appreciated.
Probably the error is in your pauseResume. There you say progressBarCircle.setProgress((int) (timeCountInMilliSeconds) / 1000);. This sets the current progress to the specified value. In your case this means the same value as you max value. I think you should move the whole block up into the onCreate method.
timeCountInMilliSeconds = 1 * 50000;
progressBarCircle.setMax((int) (timeCountInMilliSeconds) / 1000);
progressBarCircle.setProgress((int) (timeCountInMilliSeconds) / 1000);
It should be enough to initialize these values once at the beginning.
[Edit]
The behavior seems good to me. Let's say you press pause if the timer is at 47sec, now depends on your timing, the timer can be paused at 999 milliseconds or 001 msec. (to point edge cases). In the first case you have to wait "nearly" one second, to be precise 999 msec., until the timer switches visibly the state. In the seconds case it feels like the app responds directly, because you only have to wait 001 msec. If you want to fix that, normalize your difference value like so:
private void pauseResume() {
if (timerStatus == TimerStatus.STOPPED) {
...
} else {
...
difference += (((int) (System.currentTimeMillis() - startTime) / 1000) + 1) * 1000;
}
}
I also removed the usless difference1 variable.
Another improvement I made, I've removed the if else statement, because it is also useless.
You can also remove the + 1 to always wait 1sec. before you see something visual.
Related
I am trying to show a countDownTimer in my activity. the value I am using in the countDowntimer is coming from a recyclerview of another activity using bundle. when I click one of the recyclerview time for the first time, the countDownTimer shows the time running perfectly. but when I select another time, the old instance continues to run. I have implemented the cancel and onFinish() methods to but they are not working as I want. I searched everywhere but everyone suggested about cancel and onFinish() methods. I am unable to find a solution now.
public void getData() {
if(countDownTimer!=null){
countDownTimer.cancel();
}
if (replaceTime != null) {
time = Integer.parseInt(replaceTime);
} else
Toast.makeText(FullTimerActivity.this, "",
Toast.LENGTH_LONG).show();
totalTimeCountInMilliseconds = time;
timeBlinkInMilliseconds = 30 * 1000;
countDownTimer = new CountDownTimer(totalTimeCountInMilliseconds, 500) {
#Override
public void onTick(long leftTimeInMilliseconds) {
long seconds = leftTimeInMilliseconds / 1000;
if (leftTimeInMilliseconds < timeBlinkInMilliseconds) {
if (blink) {
// mTextField.setVisibility(View.VISIBLE);
// if blink is true, textview will be visible
} else {
// mTextField.setVisibility(View.INVISIBLE);
}
blink = !blink;
}
String a = String.format("%02d", seconds / 60) + ":" + String.format("%02d", seconds % 60);
textView.setText(a);
}
#Override
public void onFinish() {
Toast.makeText(FullTimerActivity.this, "Finished", Toast.LENGTH_SHORT).show();
}
}.start();
}
thank you in advance.
In onFinish() also set the text to TextView like below
textView.setText("0");
I want to build a 5 second timer that counts down to 0 in 1 second intervils and then resets to the initial value of 5 seconds. The timer needs to run continously. After looking at this thread,
Android - loop part of the code every 5 seconds
I used the CountDownTimer Class and called the start() method within the onFinish() method so that when the timer finished, it would reset to 5. It does run on a continous loop, however I notice that after the first loop, it either countsdown as 4-3-2-1-0 or 5-3-2-1-0.
Can somebody explain to me why this happens?
private long START_TIME_IN_MILLIS = 5000;
//set up our variables
private CountDownTimer timer;
private TextView textView;
private Button starttimer;
private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text_view_countdown);
starttimer = findViewById(R.id.button_start_pause);
//set onclik listener when touch imput button
starttimer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
beginTime();
}
});
}
//creating my own method
private void beginTime() {
timer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
start();
}
}.start();
}
private void updateCountDownText(){
int minutes= (int) (mTimeLeftInMillis / 1000)/ 60;
int seconds= (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.getDefault(),"%02d:%02d", minutes, seconds);
textView.setText(timeLeftFormatted);
}
}
Try this
#Override
public void onFinish() {
mTimeLeftInMillis = START_TIME_IN_MILLIS;
}
I need a count up timer in my application. I browsed many forums about this subject, but I could not find anything. Actually I understood we can do this with chronometer, but I have 2 problem with chronometer:
I cannot using chronometer in Service because chronometer needs a layout.
I cannot initialize chronometer to count more than 1 hour.
My code is here:
stopWatch = new Chronometer (MainActivity.this);
startTime = SystemClock.elapsedRealtime();
stopWatch.start();
stopWatch.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer arg0) {
countUp = (SystemClock.elapsedRealtime() - arg0.getBase()) / 1000;
String asText = (countUp / 60) + ":" + (countUp % 60);
Log.i("t", asText);
}
});
You can use a countDownTimer in reverse and get the time elapsed.
long totalSeconds = 30;
long intervalSeconds = 1;
CountDownTimer timer = new CountDownTimer(totalSeconds * 1000, intervalSeconds * 1000) {
public void onTick(long millisUntilFinished) {
Log.d("seconds elapsed: " , (totalSeconds * 1000 - millisUntilFinished) / 1000);
}
public void onFinish() {
Log.d( "done!", "Time's up!");
}
};
To start the timer.
timer.start();
To stop the timer.
timer.cancel();
The sec,min and hr increments everytime the values hit 59,59,23 respectively. Each values are displayed in different views creating a digital stopwatch.
checkin.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(final View view) {
checkin.setEnabled(false);
new CountDownTimer(300000000, 1000){
public void onTick(long millisUntilFinished){
sec++;
if(sec==59) {
min++;
sec=0;
}
if(min==59){
min=0;
hr++;
}
if(hr==23){
hr=00;
}
secView.setText(String.valueOf(sec));
minView.setText(String.valueOf(min));
hrView.setText(String.valueOf(hr));
}
public void onFinish(){
Snackbar.make(view, "Finish", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
}.start();
}
});
Using RxJava, you can write
Disposable var = Observable
.interval(1, TimeUnit.SECONDS)
.subscribe(
time -> {
long minutes = time / 60;
long second = time % 60;
timer.setText("" + minutes + ":" + second);
});
you can stop the timer by using
var.dispose()
You can use this code to do so:
https://gist.github.com/MiguelLavigne/8809180c5b8fe2fc7403
/**
* Simple timer class which count up until stopped.
* Inspired by {#link android.os.CountDownTimer}
*/
public abstract class CountUpTimer {
private final long interval;
private long base;
public CountUpTimer(long interval) {
this.interval = interval;
}
public void start() {
base = SystemClock.elapsedRealtime();
handler.sendMessage(handler.obtainMessage(MSG));
}
public void stop() {
handler.removeMessages(MSG);
}
public void reset() {
synchronized (this) {
base = SystemClock.elapsedRealtime();
}
}
abstract public void onTick(long elapsedTime);
private static final int MSG = 1;
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
synchronized (CountUpTimer.this) {
long elapsedTime = SystemClock.elapsedRealtime() - base;
onTick(elapsedTime);
sendMessageDelayed(obtainMessage(MSG), interval);
}
}
};
}
How can I keep the value of my timer resuming after going to another activity , my problem is that it's set to default if I switched to another activity I thought of using sharedpreference but it won't help because I need it to keep decrementing in the background.
public void reset()
{
countDownTimer.cancel();
}
private void setTimer() {
int time = 5;
if(countDownTimer==null)
{
totalTimeCountInMilliseconds = 60 * time * 1000;
}
else
{
reset();
totalTimeCountInMilliseconds = 60 * time * 1000;
}
}
private void startTimer() {
countDownTimer = new CountDownTimer(totalTimeCountInMilliseconds, 500) {
// 500 means, onTick function will be called at every 500
// milliseconds
//#Override
public void onTick(long leftTimeInMilliseconds) {
seconds = leftTimeInMilliseconds / 1000;
textViewShowTime.setText(String.format("%02d", seconds / 60)
+ ":" + String.format("%02d", seconds % 60));
// format the textview to show the easily readable format
}
#Override
public void onFinish() {
// this function will be called when the timecount is finished
textViewShowTime.setText("Time up!");
textViewShowTime.setVisibility(View.VISIBLE);
}
}.start();
}
You should stop your CountDownTimer in onPause and restart in onResume, your textViewShowTime might not be valid while your activity is in background.
If you need to call some code every 500ms no matter what activity you are in, then consider using AlarmManager.
I want to do countdown timer with pause and restart.Now i am displaying countdown timer By implenting ontick() and onfinish().please help me out.HEre is th code for countdown timer
final CountDownTimer Counter1 = new CountDownTimer(timervalue1 , 1000)
{
public void onTick(long millisUntilFinished)
{
System.out.println("onTick method!"(String.valueOf(millisUntilFinished/1000)));long s1=millisUntilFinished;
}
public void onFinish()
{
System.out.println("Finished!");
}
}
in onTick method..save the milliseconds left
long s1=millisUntilFinished;
when you want to pause the timer use..
Counter.cancel();
when you want to resume create a new countdowntimer with left milliseconds..
timervalue=s1
counter= new Counter1();
counter.start();
See this link
I would add something to the onTick handler to save the progress of the timer in your class (number of milliseconds left).
In the onPause() method for the activity call cancel() on the timer.
In the onResume() method for the activity create a new timer with the saved number of milliseconds left.
Refer the below links
LINK
LINK
My first answer on stackOverFlow, hope it should help :) ...
This is how I solved the problem, control timer from Fragment, Bottomsheet, Service, Dialog as per your requirement, keep a static boolean variable to control.
declare in your Activity:
long presetTime, runningTime;
Handler mHandler =new Handler();
Runnable countDownRunnable;
Toast toastObj;
public static boolean shouldTimerRun = true;
TextView counterTv;
In onCreate:
presetTime =60000L;
runningTime= presetTime;
//setting up Timer
countDownRunnable=new Runnable() {
#Override
public void run() {
if (shouldTimerRun) //if false, it runs but skips counting
{
counterTv.setText(simplifyTimeInMillis(runningTime));
if (runningTime==0) {
deployToast("Task Completed"); //show toast on task completion
}
runningTime -= 1000;
presetTime = runningTime; //to resume the timer from last position
}
mHandler.postDelayed(countDownRunnable,1000); //simulating on-tick
}
};
mHandler.post(countDownRunnable); // Start our CountdownTimer
Now, whenever you want to pause the timer change the value of shouldTimerRun false and to resume make it true.
#Override
public void onResume() {
super.onResume();
shouldTimerRun=true;
}
#Override
public void onPause() {
super.onPause();
shouldTimerRun=false;
deployToast("Timer is paused !!");
}
Helping methods: (can be skipped)
public static String simplifyTimeInMillis(long time) {
String result="";
long difference = time;
long secondsInMilli = 1000;
long minutesInMilli = secondsInMilli * 60;
long hoursInMilli = minutesInMilli * 60;
if (difference<1000){
return "0";
}
if (difference>=3600000) {
result = result + String.valueOf(difference / hoursInMilli) + "hr ";
difference = difference % hoursInMilli;
}
if (difference>=60000) {
result = result + String.valueOf(difference / minutesInMilli) + "m ";
difference = difference % minutesInMilli;
}
if (difference>=1000){
result = result + String.valueOf(difference / secondsInMilli) + "s";
}
return result;
}
public void deployToast(String msg){
if (toastObj!=null)
toastObj.cancel();
toastObj = Toast.makeText(mContext,msg,Toast.LENGTH_SHORT);
toastObj.show();
}
I'm using two private vars in this case:
private long startPauseTime;
private long pauseTime = 0L;
public void pause() {
startPauseTime = System.currentTimeMillis();
}
public void resumen(){
pauseTime += System.currentTimeMillis() - startPauseTime;
}
I am afraid that it is not possible to pause or stop CountDownTimer and pausing or stopping in onTick has no effect whatsoever user TimerTask instead.
Set up the TimerTask
class UpdateTimeTask extends TimerTask {
public void run() {
long millis = System.currentTimeMillis() - startTime;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
timeLabel.setText(String.format("%d:%02d", minutes, seconds));
}
}
if(startTime == 0L) {
startTime = evt.getWhen();
timer = new Timer();
timer.schedule(new UpdateTimeTask(), 100, 200);
}
You can add event listener's like this..
private Handler mHandler = new Handler();
...
OnClickListener mStartListener = new OnClickListener() {
public void onClick(View v) {
if (mStartTime == 0L) {
mStartTime = System.currentTimeMillis();
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 100);
}
}
};
OnClickListener mStopListener = new OnClickListener() {
public void onClick(View v) {
mHandler.removeCallbacks(mUpdateTimeTask);
}
};
For more refer to Android Documentation.
//This timer will show min:sec format and can be paused and resumed
public class YourClass extends Activity{
TextView timer;
CountDownTimer ct;
long c = 150000; // 2min:30sec Timer
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.YourXmlLayout);
timer = (TextView)findViewById(R.id.Yourtimer)
startTimer(); // it will start the timer
}
public void startTimer(){
ct = new CountDownTimer(c,1000) {
#Override
public void onTick(long millisUntilFinished) {
// Code to show the timer in min:sec form
// Here timer is a TextView so
timer.setText(""+String.format("%02d:%02d",millisUntilFinished/60000,(millisUntilFinished/1000)%60));
c = millisUntilFinished; // it will store millisLeft
}
#Override
public void onFinish() {
//your code here
}
};
ct.start();
}
/*===========================================================
*after creating this you can pause this by typing ct.cancel()
*and resume by typing startTimer()*/
public class MainActivity extends AppCompatActivity {
TextView textView;
CountDownTimer ctimer;
boolean runCountDown;
private long leftTime;
private static final long MILL_IN_FUTURE = 6000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text_view);
textView.setText("Click to start");
textView.setOnClickListener(this::clickStartAndPauseAndResume);
leftTime = MILL_IN_FUTURE;
}
public void clickStartAndPauseAndResume(View view) {
if (!runCountDown) {
long time = (leftTime == 0 || leftTime == MILL_IN_FUTURE) ? MILL_IN_FUTURE : leftTime;
ctimer = new CountDownTimer(time, 1) {
#Override
public void onTick(long l) {
leftTime = l;
textView.setText(l + "ms");
}
#Override
public void onFinish() {
textView.setText("Done");
leftTime = 0;
runCountDown = false;
textView.postDelayed(new Runnable() {
#Override
public void run() {
textView.setText("Click to start");
}
}, 1000);
}
}.start();
runCountDown = true;
} else {
ctimer.cancel();
textView.setText(textView.getText() + "\n Click to resume");
runCountDown = false;
}
}
}
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");
}
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();
}