I'm learning android development, and what I'm trying to do is to have a label that counts down from 40 minutes, and when it reaches 0 it would stop counting and do something else. This is my code:
#Override
protected void onStart() {
super.onStart();
count = 2400;
final Timer t = new Timer();//Create the object
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
minLeft = (int) Math.floor(count / 60);
secLeft = count - minLeft * 60;
counter = minLeft + ":" + secLeft;
TextView tv = (TextView) findViewById(R.id.timer);
Log.i(MainActivity.TAG,minLeft+", "+secLeft+", "+counter);
tv.setText(counter);
count--;
if (minLeft <= 0 && secLeft <= 0) {
t.cancel();
count = 2400;
onFinish();
}
}
}, 1000, 1000);
}
But, when I go to that activity by clicking a button in the main activity, the label has the text "Timer" (its original text), and after a few seconds the app crashes with CalledFromWrongThreadException, but the line that causes the problem seems to be the one where I set the text of the TextView.
Please help, thanks in advance.
Your scheduled task runs on the background thread.
And you try to set the text to the textview from this background thread.
However in Android all view related operations have to be done on the main thread.
That is way in your scheduled task you have to use something like:
#Override
protected void onStart() {
super.onStart();
count = 2400;
final Timer t = new Timer();//Create the object
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
minLeft = (int) Math.floor(count / 60);
secLeft = count - minLeft * 60;
counter = minLeft + ":" + secLeft;
// the name of your actual activity
MyActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
TextView tv = (TextView) findViewById(R.id.timer);
Log.i(MainActivity.TAG,minLeft+", "+secLeft+", "+counter);
tv.setText(counter);
}
});
count--;
if (minLeft <= 0 && secLeft <= 0) {
t.cancel();
count = 2400;
onFinish();
}
}
}, 1000, 1000);
}
Please also note, that this code can be written more elegantly, without all/so many anonymous classes, but it should do the trick.
Related
I making a timer in my android app. I have a scenario where I dont need to stop the timer even if the app is closed. If the timer is running and user closes the app and reopen it after sometime. He should see the latest time on the timer. But currently I am not able to show the latest time. I am only able to show the time when the user killed the app. I am storing the time of the timer and when the user open the app I am putting the stored time back to the timer. But I want to show the latest time. Here what I have done till now. I am using chronometer widget.
MainActivity.class:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = MainActivity.class.getSimpleName();
Chronometer tvTextView;
Button btnStart, btnStop;
private int state = 0; //0 means stop state,1 means play, 2 means pause
SharedPreferences sharedPreferences;
private boolean running = false;
private long pauseOffSet = -1;
ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTextView = findViewById(R.id.textview);
progressBar = findViewById(R.id.puzzleProgressBar);
btnStart = findViewById(R.id.button1);
btnStop = findViewById(R.id.button2);
btnStart.setOnClickListener(this);
btnStop.setOnClickListener(this);
sharedPreferences = getSharedPreferences("myprefs", MODE_PRIVATE);
state = sharedPreferences.getInt("state", 0);
tvTextView.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer chronometer) {
long time = SystemClock.elapsedRealtime() - chronometer.getBase();
pauseOffSet=time;
Log.e(TAG,"pauseOffSet "+pauseOffSet);
if (time >= 79200000) {
tvTextView.setBase(SystemClock.elapsedRealtime());
tvTextView.stop();
running = false;
progressBar.setProgress(0);
} else {
chronometer.setText(setFormat(time));
int convertTime = (int) time;
progressBar.setProgress(convertTime);
}
}
});
if (state == 1) { // its in play mode
running = true;
tvTextView.setBase(SystemClock.elapsedRealtime() - sharedPreferences.getLong("milli", 0));
tvTextView.start();
} else if (state == 2) { //its in pause mode
running = false;
pauseOffSet = sharedPreferences.getLong("milli", -1);
long time = SystemClock.elapsedRealtime() - pauseOffSet;
tvTextView.setBase(time);
int convertTime = (int) pauseOffSet;
progressBar.setProgress(convertTime);
} else {
running = false;
tvTextView.setBase(SystemClock.elapsedRealtime());
}
}
public void onClick(View v) {
if (btnStart == v) {
if (!running) {
if (pauseOffSet != -1) {
pauseOffSet = sharedPreferences.getLong("milli", -1);
}
tvTextView.setBase(SystemClock.elapsedRealtime() - pauseOffSet);
tvTextView.start();
state = 1;
pauseOffSet = 0;
running = true;
}
} else if (btnStop == v) {
if (running) {
tvTextView.stop();
pauseOffSet = SystemClock.elapsedRealtime() - tvTextView.getBase();
state = 2;
running = false;
}
}
}
#Override
protected void onStop() {
super.onStop();
sharedPreferences.edit().putLong("milli", pauseOffSet).commit();
sharedPreferences.edit().putInt("state", state).commit();
}
#Override
protected void onDestroy() {
Log.e(TAG, "onDestroy called, state: " + state);
super.onDestroy();
}
String setFormat(long time) {
int h = (int) (time / 3600000);
int m = (int) (time - h * 3600000) / 60000;
int s = (int) (time - h * 3600000 - m * 60000) / 1000;
String hh = h < 10 ? "0" + h : h + "";
String mm = m < 10 ? "0" + m : m + "";
String ss = s < 10 ? "0" + s : s + "";
return hh + ":" + mm + ":" + ss;
}
}
this is my first time writing here. I am learning Android development and trying to make a stopwatch (countdown timer) app, which has 12 rounds and pause - example 10 sec between them. I wrote a code which works fine but I want to write it more "readable and nicely" so I would like to ask you for your help. As you can see, I don't want to write every time under public void run() in if-else if statement new roundtime number and setting text because it would take too much space. That is why I wrote it just for example(not 12 times) so you can see what I mean. Do you have any suggestions how to write it better for 12 rounds? Can I use a loop? If u have easier way for countdown round after round I would appreciate it. I am sorry for my english. Any comments are welcome. Enjoy coding.
This is my code:
public class Main3Activity extends AppCompatActivity {
Handler handler;
TextView textView;
ImageView imageView2;
ImageView imageView3;
Boolean checker=false;
long secondtime=1000;
long roundtime1= 10000;
long roundtime2=10000;
long pause= 31000;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
textView=(TextView)findViewById(R.id.textView3);
imageView2=(ImageView)findViewById(R.id.imageView2);
imageView3=(ImageView)findViewById(R.id.imageView3);
imageView2.setOnClickListener(
new ImageView.OnClickListener(){
#Override
public void onClick(View v) {
handler = new Handler();
if (checker == false) {
handler.post(r);
checker = true;
}
imageView3.setOnClickListener(
new ImageView.OnClickListener() {
#Override
public void onClick(View v) {
handler.removeCallbacks(r);
}
}
);
}
final Runnable r= new Runnable() {
#Override
public void run() {
roundtime = roundtime - secondtime;
int seconds = (int) (roundtime / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
if (roundtime >= 0) {
textView.setText(String.format("%d:%02d", minutes, seconds));
handler.postDelayed(r, 1000);
} else if (pause > 0) {
pause = pause - secondtime;
int secondss = (int) (pause / 1000);
int minutess = secondss / 60;
secondss = secondss % 60;
textView.setText(String.format("%d:%02d", minutess, secondss));
handler.postDelayed(r, 1000);
} else if (roundtime2 > 0) {
roundtime2 = roundtime2 - secondtime;
int secondsss = (int) (roundtime2 / 1000);
int minutesss = secondsss / 60;
secondsss = secondsss % 60;
textView.setText(String.format("%d:%02d", minutesss, secondsss));
handler.postDelayed(r, 1000);
}else{
textView.setText("Over");
}
}
};
}
);
}
}
Runnable r = new Runnable(){
public void run(){
try{
For (int i = 60; i >= 1; i--) {
System.out.print(i + "\r");
// Let the thread sleep for a while.
Thread.sleep(1000);
TextView box2 = (TextView) findViewById(R.id.box2);
box2.setText("\r" + i + " sec left");
}
} catch (InterruptedException e) {
}
}
How to make number running from 60 to 0 and print on text view in android studio and what is the correct way to print in text view in android studio.thank you for your help!
Initialize your TextView in onCreate() first:
TextView box2 = (TextView) findViewById(R.id.box2);
Then you can use a handler with postDelayed() method to create a countdown timer:
final Handler handler = new Handler();
handler.postDelayed(new Runnable(){
public void run() {
for (int i = 60; i >= 1; i--) {
box2.setText("\r" + i + " sec left");
}
handler.postDelayed(this, 1000);
}
}, 0);
I have a game where image Views changes on a timer. When a button is clicked (stop) the update is called to update score and spins. I need to stop when the whammy(); is called and run a frame animation then start the timer back when the animation has ended. I have tried to call sleep(2500); but it cause the everything to wait the 2.5 seconds and the frame animation then runs with the image view timer. That just won't work for what I need. Below is the section of code I need help with:
public void onClick(View v) {
if (v.getId() == R.id.btstop) {
final Button bstop = (Button) findViewById(R.id.btstop);
bstop.setEnabled(false);
updateState();
}
}
/**
* set random view. get random 1 point from class utils and display to 1
* position random
*/
public void setRandomView() {
int item = new Random().nextInt(18);
current = Utils.getAPoint();
int img = current.getImg();
int score = current.getScore();
int spin = current.getSpin();
iv[item].setImageResource(img);
dataPoint.get(item).setImg(img);
dataPoint.get(item).setScore(score);
dataPoint.get(item).setSpin(spin);
ivcenter.setImageResource(img);
}
/**
* display Name,score & spin to screen
*/
public void setPoint() {
tvspin.setText(spins + "\n" + name);
tvpoint.setText(point + "");
}
/**
* Start Thread Sets the speed of the game board Enable STOP button to true
*
* #param time
*/
public void start(int time) {
countDownTimer = new CountDownTimer(time, 300) {
#Override
public void onTick(long millisUntilFinished) {
// call to set random view
setRandomView();
}
#Override
public void onFinish() {
// TODO Auto-generated method stub
}
}.start();
}
/**
* stop timer thread
*/
public void stop() {
try {
countDownTimer.cancel();
} catch (Exception e) {
// TODO: handle exception
} finally {
}
}
/**
* update state game and check what tile is in the center
*/
public void updateState() { // get score,spin current point
int scoreCurrent = current.getScore();
int spinCurrent = current.getSpin();
spins--;// Take one from spins
// if score = 2, that mean to double score
if (scoreCurrent == 2) {
point = point * 2;
playmp3(R.raw.correct);
}
// if score = 0, that means tile is a whammy
if (scoreCurrent == 0) {
playmp3(R.raw.buzz);
whammy();// I need to put everything else on hold here and animation here
point = 0;// sets the score to 0
whammy++; // Add a whammy to the whammy count
// Display the whammy that was added
for (int i = 0; i < whammy; i++) {
popup[i].setVisibility(View.VISIBLE);
}
// sleep(2500);
if (whammy == 4) {
stop();
stopmp3();
callFinish();
}
}
// else ,score = score current + point.getscore
else {
playmp3(R.raw.correct);
point = point + scoreCurrent;
}
// spins--;// Take one from spint
// if spin > 0, that mean is add a spin
if (spinCurrent > 0) {
spins = spins + spinCurrent;
}
setPoint(); // display spin,score and name to screen
sleep(2000); // Adds a delay so that the whammy pop up can be seen
// if spint >0 start new data random
if (spins > 0) {
stop();
final Button bstop = (Button) findViewById(R.id.btstop);
bstop.setEnabled(true);
start(10000 * 60 * 60 * 24);
} else {
// stop all
stop();
stopmp3();
// if spin < 2, that round <2 move to spin activity
if (round < 2) {
Intent it = new Intent(getBaseContext(), SpinActivity.class);
startActivity(it);
finish();
}
// else move to high score activity
else {
callFinish();
}
}
}
private void whammy() {
// set up the whammy act animation
final ImageView act = (ImageView) findViewById(R.id.whammyact);
act.setBackgroundResource(R.drawable.tnt_animation);
actAnim = (AnimationDrawable) act.getBackground();
actAnim.setVisible(true, true);
}
OK Here are the changes that were made to make the animation run and finish before the game timer gets started back...
IF THERE ARE QUESTION OR ANYONE NEED HELP FEEL FREE TO ASK.
if (scoreCurrent == 0) {
countDownTimer.cancel();
countDownTimer = null;
// Use Timer to set visibility to GONE after the animation finishes.
TimerTask timerTask = new TimerTask(){
#Override
public void run() {
PressActivity.this.runOnUiThread(new Runnable(){
public void run() {
resumeLogicAfterPress();
}
});}};
Timer timer = new Timer();
timer.schedule(timerTask, getWhammyAnimationTotalDuration(actAnim));
} else {
resumeLogicAfterPress();
}
}
public void resumeLogicAfterPress() {
// Display the whammy that was added
for (int i = 0; i < whammy; i++) {
popup[i].setVisibility(View.VISIBLE);
}
sleep(2500); //Adds a delay so that the whammy pop up can be seen
//Start the background music
playmp3(mpPress, R.raw.press);
// if whammy = 4. stop all and move to high score
if (whammy == 4) {
stop();
stopmp3();
callFinish();
}
// if spint >0 start new data random
if (spint > 0) {
stop();
final Button bstop = (Button) findViewById(R.id.btstop);
bstop.setEnabled(true);
start(10000 * 60 * 60 * 24);
} else {
// stop all
stop();
stopmp3();
// if spin < 2, that round <2 move to spin activity
if (spin < 2) {
Intent it = new Intent(getBaseContext(), SpinActivity.class);
startActivity(it);
finish();
}
// esle move to high score activity
else {
callFinish();
}
}
}
private void whammy() {
// set up the whammy act animation
final ImageView act = (ImageView) findViewById(R.id.whammyact);
act.setBackgroundResource(R.drawable.tnt_animation);
actAnim = (AnimationDrawable) act.getBackground();
actAnim.setVisible(true, true);
}
I'm trying to make a countdown timer starting at 10 minutes, similair to a basketball scoreboard: 10:00 to 00:00. How would I do that? This is my code:
private TextView Timer;
Handler handler = new Handler();
private int length = 120000;
private int decision = 0;
MyCount counter;
public String formatTime(long millis) {
String output = "00:00";
long seconds = millis / 1000;
long minutes = seconds / 60;
seconds = seconds % 60;
minutes = minutes % 60;
String sec = String.valueOf(seconds);
String min = String.valueOf(minutes);
if (seconds < 10)
sec = "0" + seconds;
if (minutes < 10)
min= "0" + minutes;
output = min + " : " + sec;
return output;
}//formatTime
#Override
public void onCreate(Bundle cute) {
super.onCreate(cute);
counter = new MyCount(length, 1000);
updateTime();
handler.removeCallbacks(updateTimeTask);
handler.postDelayed(updateTimeTask, 1000);
}//end of cuteness
private Runnable updateTimeTask = new Runnable() {
public void run() {
updateTime();
handler.postDelayed(this, 1000);
}
};
private void updateTime() {
switch (decision) {
case 0:
startTime = 0L;
counter.start();
decision=1;
break;
case 1:
counter.onPause();
decision=0;
break;
}
}//updateTime
class MyCount extends CountDownTimer {
public MyCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}//MyCount
public void onPause() {
//do stuff later
onPause();
}//finish
public void onTick(long millisUntilFinished) {
Timer.setText("" + formatTime(millisUntilFinished));
}//on tick
#Override
public void onFinish() {
onStop();
}//finish
}//class MyCount
Any help would be appreciated. thanks!
This does not have to be too hard. You've already created your functionality for writing your time in letters, now you need to count down. Starting a timer is easy, just do this in your start button event handler (or whatever you choose to use) (modified example from the android developer reference:
// New timer for 10 minutes, starts after initialization
new MyCount(600000, 1000)
{
// Updates the text on your "scoreboard" every second
public void onTick(long millisUntilFinished)
{
Timer.setText("Time remaining: " + formatTime(millisUntilFinished));
}
public void onFinish()
{
mTextField.setText("done!");
}
}.start();
And that's all you need! You can skip your UpdateTime function and your updateTimeTask. Just replace all this on your onCreate method
counter = new MyCount(length, 1000);
updateTime();
handler.removeCallbacks(updateTimeTask);
handler.postDelayed(updateTimeTask, 1000);
With my code. Or modify it as you please!
why don't you just create 3 textviews so that it will be easier for you to code?
one for the minutes.
one for the colon.
one for the seconds.
then use the code he's using.
hope I helped.
The way i think of making a count down is just saving at the beginning the (current time + 10 minutes) aside and then just subtract it from current time every second in your handler and display the result in the desired format..