I have a function that has a timer that I want to "restart" every time you click the button. I tried doing this but when the button is clicked several times it appears that there are several timers still going on when I only want the one. How do I fix this?
So, onClick => Cancel last timer => Start new timer
public void startService(android.view.View view) {
final SharedPreferences sP = getSharedPreferences("com.example.safetyNet", MODE_PRIVATE);
final Button button = findViewById(R.id.button3);
final Intent transIntent = new Intent(this, CheckPinActivity.class);
CountDownTimer cdt = new CountDownTimer(sP.getInt("TiT", 10) * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
button.setText(String.valueOf(millisUntilFinished).substring(0,2));
}
#Override
public void onFinish() {
if(sP.getBoolean("lockedDown", false) == true){
startActivity(transIntent);
}
}
};
cdt.cancel();
cdt.start();
}
The problem is that, everytime you call the method "startService(android.view.View view) {}", a new CountDownTimer is created, so the previous CountDownTimer that you created is not the same reference as this one.
To solve that, you are going to have to create the CountDownTimer as a class member for your class:
public YourClass {
private CountDownTimer cdt;
.... (do whatever)....
public void startService(android.view.View view) {
final SharedPreferences sP = getSharedPreferences("com.example.safetyNet", MODE_PRIVATE);
final Button button = findViewById(R.id.button3);
final Intent transIntent = new Intent(this, CheckPinActivity.class);
if (cdt == null) {
cdt = new CountDownTimer(sP.getInt("TiT", 10) * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
button.setText(String.valueOf(millisUntilFinished).substring(0,2));
}
#Override
public void onFinish() {
if(sP.getBoolean("lockedDown", false) == true){
startActivity(transIntent);
}
}
};
} else {
cdt.cancel();
}
cdt.start();
}
Hope that helps you
Related
My problem is towards the bottom, I have a boolean, ongoing, which is false. It is supposed to become true with a button press, then initiate a countdown timer, but the timer is not initiated. The timer works placed elsewhere in the code. Sorry if it's a complete mess I'm new to this.
public int counter;
public boolean ongoing = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//0-59 number picker to represent seconds
final NumberPicker secPicker = (NumberPicker) findViewById(R.id.secPicker);
secPicker.setMaxValue(59);
Button btnStart = (Button) findViewById(R.id.btnStart);
Button btnSet = (Button) findViewById(R.id.btnSet);
Button btnPause = (Button) findViewById(R.id.btnPause);
final TextView label = (TextView) findViewById(R.id.txtCount);
//sets the time chosen to timer with button press btnSet
btnSet.setOnClickListener(
new View.OnClickListener() {
public void onClick(View v) {
counter = secPicker.getValue();
label.setText(String.valueOf(counter));
}
}
);
//starts timer from selected time, from number picker, with btnStart
btnStart.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
ongoing = true;
}
});
//disables, not worried about this yet
btnPause.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
ongoing = false;
}
});
//supposed to turn on a timer with button press btnStart, but it does nothing this is my problem.
if (ongoing == true) {
//any replacement for 9999999, as an infinite value?
new CountDownTimer(999999999, 1000) {
public void onTick(long millisUntilFinished) {
label.setText(String.valueOf(counter));
counter--;
if (counter == -1) {
counter = 0;
cancel();
}
}
public void onFinish() {
}
}.start();
}
}
}
if (ongoing == true) // this will never work for you
You have putted above code inside oncreate before you click anything it will get call.
Either you should write below code inside btnStart click event or create one method and call from it.
//supposed to turn on a timer with button press btnStart, but it does nothing this is my problem.
if (ongoing == true) {
//any replacement for 9999999, as an infinite value?
new CountDownTimer(999999999, 1000) {
public void onTick(long millisUntilFinished) {
label.setText(String.valueOf(counter));
counter--;
if (counter == -1) {
counter = 0;
cancel();
}
}
public void onFinish() {
}
}.start();
}
Hope these help you.
In my game the user gets a point when he clicks a button within five seconds. Now I want the timer to decrease the time with every point the user gets - so e.g. with zero points the user has five seconds, and when he has one point he gets only 4.5 seconds to click it again and get the second point. Do I solve it with a for loop?
public class GameScreen extends Activity {
public int score = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
Button count = (Button) findViewById(R.id.button1);
text = (TextView) this.findViewById(R.id.textView3);
tvscore = (TextView) findViewById(R.id.score);
timer();
}
public void gameover() {
Intent intent = new Intent(this, GameOverScreen.class);
startActivity(intent);
}
public void onClick (View view) {
score++;
tvscore.setText(String.valueOf(score));
timer();
}
public void timer(){
new CountDownTimer(5000, 10) {
public void onTick(long millisUntilFinished) {
text.setText(""+String.format("%02d:%03d",
TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished)),
TimeUnit.MILLISECONDS.toMillis(millisUntilFinished) - TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished))
));
if(animationRunning) {
cancel();
}
}
public void onFinish() {
text.setText("Too slow.");
gameover();
}
}.start();
}
}
What about doing this:
public void timer(float time) {
new CountDownTimer(time, 10) {
// YOUR CODE
}
}
public void onClick (View view) {
score++;
tvscore.setText(String.valueOf(score));
timer(Math.max(5000-score*500, 2000));
}
I suppose each click (score) will decrease the time with 500 millis...
Limit - Math.max(a, b) will choose the biggest value. Means when 5000-score*500 will be lower than 2000, it will choose 2000 millis instead
Only timer method:
public void timer() {
float time = Math.max(5000-score*500, 2000)
new CountDownTimer(time, 10) {
// YOUR CODE
}
}
Working on an Android app where each activity has its own timer. Originally I had code like this in every class
public void tTimer() {
mTextField = (TextView) findViewById(R.id.countDown_timer);
final CountDownTimer tCounter = new CountDownTimer(7000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTextField
.setText("Time Remaining: "
+ String.format(
"%d min, %d sec",
TimeUnit.MILLISECONDS
.toMinutes(millisUntilFinished),
TimeUnit.MILLISECONDS
.toSeconds(millisUntilFinished)
- TimeUnit.MINUTES
.toSeconds(TimeUnit.MILLISECONDS
.toMinutes(millisUntilFinished))));
}
#Override
public void onFinish() {
mTextField.setText("Done!");
Intent i = new Intent (ColdActivity.this, PopUpActivity.class);
startActivity(i);
}
};
Button startButton = (Button) findViewById(R.id.timer_button_start);
/* When the Start Button is touched, the Countdown Timer will start */
startButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(
ColdActivity.this);
builder.setNegativeButton("Dismiss",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1) {
tCounter.start();
// Once the user has been notified that they should increase
// their alarm volume, and they dismiss the notification, the timer
// will start
}
});
builder.setTitle("Notification");
builder.setMessage("It is recommended that you turn your Alarm/Ringtone volume on so that you can hear the alarm when it is finished");
AlertDialog dlg = builder.create();
dlg.show();
}
});
Button stopButton = (Button) findViewById(R.id.timer_button_cancel);
/* When the Stop Button is touched, the Countdown Timer will stop */
stopButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
tCounter.cancel();
}
});
}
I am now trying to create a new class that has a method called tTimer() like in the above code, so that I can instantiate a new object of that class, and then call the method in onCreate(), that way I don't have to rewrite timer code in every class, like I had previously done.
I tried to parameterize the method like this:
CustomTimerActivity cGT = new CustomTimerActivity();
cGT.tTimer(7000, ColdActivity.this, R.id.countDown_timer, R.id.timer_button_start, R.id.timer_button_cancel);
And what I keep getting is a NullPointerException, which points to my class called CustomTimerActivity
Why am I not able to pass the resource Id's as parameters? Or is the NPE coming from something else?
Here is the CustomTimerActivity I made:
public class CustomTimerActivity extends Activity {
TextView mTextField;
int mId;
public void tTimer(long millisecs, final Activity activity1, int tv, int start, int cancel) {
mTextField = (TextView) findViewById(tv);
final CountDownTimer tCounter = new CountDownTimer(millisecs, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTextField
.setText("Time Remaining: "
+ String.format(
"%d min, %d sec",
TimeUnit.MILLISECONDS
.toMinutes(millisUntilFinished),
TimeUnit.MILLISECONDS
.toSeconds(millisUntilFinished)
- TimeUnit.MINUTES
.toSeconds(TimeUnit.MILLISECONDS
.toMinutes(millisUntilFinished))));
}
#Override
public void onFinish() {
mTextField.setText("Done!");
Intent i = new Intent (activity1, PopUpActivity.class);
startActivity(i);
}
};
Button startButton = (Button) findViewById(start);
/* When the Start Button is touched, the Countdown Timer will start */
startButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(
activity1);
builder.setNegativeButton("Dismiss",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface arg0, int arg1) {
tCounter.start();
// Once the user has been notified that they should increase
// their alarm volume, and they dismiss the notification, the timer
// will start
}
});
builder.setTitle("Notification");
builder.setMessage("It is recommended that you turn your Alarm/Ringtone volume on so that you can hear the alarm when your tea is finished");
AlertDialog dlg = builder.create();
dlg.show();
}
});
Button stopButton = (Button) findViewById(cancel);
/* When the Stop Button is touched, the Countdown Timer will stop */
stopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tCounter.cancel();
}
});
}
}
The issue must be something to do with findViewById() and the Id that is used as input, right?
Here is another NullPointerException I get when the Intent is called.
The Activity has not been initialized in the constructor. Only collect the parameters in the constructor and do the initialization in the onCreate(Bundle) method!
However, if you want to call it from a givin activity, you have to call .findViewById on that class, not on your own class
I have question i have a chronometer in my app, when i back to my main window, and get back to Timer activity, I just want to resume a time on chronometer from CountDownTimer, how to to this ? Bundle doesn't help me and similar topics :( I will be very gratefull for any help.
public class Timer extends Activity {
private boolean start = false; // Semafor to button
private Chronometer timer; // Chronometr
private CountDownTimer count; // CountDown class
private final long startTime = 30 * 1000; // High
private final long end = 1 * 1000; //LOw
private ImageButton startB; // Button
private int VIBRATION_TIME = 1000; //vibration time
private Bundle state = new Bundle(); //button state
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// Show the Up button in the action bar.
getActionBar().setDisplayHomeAsUpEnabled(true);
}
this.timer = ((Chronometer) findViewById(R.id.chronometer1));
count = new MyCountDownTimer(startTime, end);
timer.setText("00:" + String.valueOf(startTime / 1000));
startB = (ImageButton) this.findViewById(R.id.start_pause);
startB.setBackgroundResource(R.drawable.start_button);
startB.setOnClickListener(new View.OnClickListener() {
// click init
#Override
public void onClick(View v) {
start = !start;
if (start == true) {
count.start(); // start count
startB.setBackgroundResource(R.drawable.stop_button);
} else {
count.cancel(); // pause count
startB.setBackgroundResource(R.drawable.start_button);
}
}
});
}
....
#Override
protected void onResume() {
super.onResume();
start = state.getBoolean("Button");
}
#Override
protected void onPause() {
super.onPause();
state.putBoolean("Button", start);
//count.cancel(); I don't wanna this!
}
class MyCountDownTimer extends CountDownTimer {
public MyCountDownTimer(long startTime, long interval) {
super(startTime, interval);
}
#Override
public void onFinish() {
timer.setText("00:00");
// TO DO :
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(VIBRATION_TIME);
startB.setBackgroundResource(R.drawable.start_button);
}
#Override
public void onTick(long millisUntilFinished) {
long tmp = millisUntilFinished / 1000;
timer.setText("00:" + tmp);
}
}
}
Make use of onSaveInstanceState. Override this method, and save your current time into the Bundle. Then, on onCreate, extract the value from the Bundle and apply it to your View.
Hmm yes that is correct what Alex says but I have new idea. CountDownTimer start count, we click backButton activity is killed, but CountDownTimer still counting, but when I use this activity again, I can run new timer and this is crazy, there is any way to do this like that : In create method we check that CountDownTimer is still running and we just update a chronometer, but if not we create everything from new? At now my solutions is tricky: override the onBackPressed() and add this moveTaskToBack(true); but this not back to my mainActivity but return to telephone screen.
I have created a Quiz app for Android using the tutorial here: http://automateddeveloper.blogspot.co.uk/2011/06/getting-started-complete-android-app.html
For each question, the user will only have 20 seconds to answer it. If he/she fails to answer in 20 seconds, an AlertDialog will popup and the game will terminate.
To do this, I have added a counter in the OnCreate method of QuestionActivity class:
final TextView myCounter = (TextView) findViewById(R.id.countdown);
new CountDownTimer(20000, 1000) {
#Override
public void onFinish() {
timeUp();
}
#Override
public void onTick(long millisUntilFinished) {
myCounter.setText("Time left: "
+ String.valueOf(millisUntilFinished / 1000));
}
}.start();
public void timeUp() {
AlertDialog.Builder builder = new AlertDialog.Builder(
QuestionActivity.this);
builder.setTitle("Times up!")
.setMessage("Game over")
.setCancelable(false)
.setNeutralButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
QuestionActivity.this.finish();
}
});
AlertDialog alert = builder.create();
alert.show();
}
The counter works displays and functions on screen correctly. After answering a question, the activity moves to the next question and the counter resets itself to 20 seconds again.
The problem
The quiz has 15 questions, after answering 3 or 4 questions, the app crashes and I get the following error:
07-18 00:49:05.530: E/AndroidRuntime(4867): android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy#41533a80 is not valid; is your activity running?
I believe this relates to the AlertDialog. I have looked up this error code on Stackoverflow and the popular solution is to pass ActivityName.this as the context when building the AlertDialog.
Unfortunately this does not solve the problem.
I believe the counter is setting a time limit of 20 seconds for the whole activity. My requirement is 20 seconds for each question.
However, the counter resets to 20 seconds when the user press the' Next button and the activity moves to the next question.
Should I be resetting the counter when the user presses the Next button? Here is the OnClickListener code for the Next button
if (currentGame.isGameOver()) {
Intent i = new Intent(this, EndgameActivity.class);
startActivity(i);
finish();
} else {
Intent i = new Intent(this, QuestionActivity.class);
startActivity(i);
SHOULD I ADD SOMETHING HERE?
finish();
Can anyone help me code a solution to my problem?
Here is all the code in QuestionActivity.class
public class QuestionActivity extends SherlockActivity implements
OnClickListener {
private Question currentQ;
private GamePlay currentGame;
private Context context;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.question);
getSupportActionBar().hide();
/**
* Configure current game and get question
*/
currentGame = ((ChuckApplication) getApplication()).getCurrentGame();
currentQ = currentGame.getNextQuestion();
Button nextBtn = (Button) findViewById(R.id.nextBtn);
nextBtn.setOnClickListener(this);
Button quitBtn = (Button) findViewById(R.id.quitBtn);
quitBtn.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
finish();
}
});
//Update the question and answer options..
setQuestions();
final TextView myCounter = (TextView) findViewById(R.id.countdown);
new CountDownTimer(20000, 1000) {
#Override
public void onFinish() {
// myCounter.setText("Time up!");
timeUp(context);
}
#Override
public void onTick(long millisUntilFinished) {
myCounter.setText("Time left: "
+ String.valueOf(millisUntilFinished / 1000));
}
}.start();
}
public void timeUp(Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(
QuestionActivity.this);
builder.setTitle("Times up!")
.setMessage("Game over")
.setCancelable(false)
.setNeutralButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
QuestionActivity.this.finish();
}
});
AlertDialog alert = builder.create();
alert.show();
}
/**
* Method to set the text for the question and answers from the current
* games current question
*/
private void setQuestions() {
// set the question text from current question
String question = Utility.capitalise(currentQ.getQuestion()) + "?";
TextView qText = (TextView) findViewById(R.id.question);
qText.setText(question);
// set the available options
List<String> answers = currentQ.getQuestionOptions();
TextView option1 = (TextView) findViewById(R.id.answer1);
option1.setText(Utility.capitalise(answers.get(0)));
TextView option2 = (TextView) findViewById(R.id.answer2);
option2.setText(Utility.capitalise(answers.get(1)));
TextView option3 = (TextView) findViewById(R.id.answer3);
option3.setText(Utility.capitalise(answers.get(2)));
TextView option4 = (TextView) findViewById(R.id.answer4);
option4.setText(Utility.capitalise(answers.get(3)));
}
public void onClick(View arg0) {
//validate a checkbox has been selected
if (!checkAnswer())
return;
//check if end of game
if (currentGame.isGameOver()) {
Intent i = new Intent(this, EndgameActivity.class);
startActivity(i);
finish();
} else {
Intent i = new Intent(this, QuestionActivity.class);
startActivity(i);
finish();
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
return true;
}
return super.onKeyDown(keyCode, event);
}
/**
* Check if a checkbox has been selected, and if it has then check if its
* correct and update gamescore
*/
private boolean checkAnswer() {
String answer = getSelectedAnswer();
if (answer == null) {
// Log.d("Questions", "No Checkbox selection made - returning");
return false;
} else {
// Log.d("Questions",
// "Valid Checkbox selection made - check if correct");
if (currentQ.getAnswer().equalsIgnoreCase(answer)) {
// Log.d("Questions", "Correct Answer!");
currentGame.incrementRightAnswers();
} else {
// Log.d("Questions", "Incorrect Answer!");
currentGame.incrementWrongAnswers();
}
return true;
}
}
private String getSelectedAnswer() {
RadioButton c1 = (RadioButton) findViewById(R.id.answer1);
RadioButton c2 = (RadioButton) findViewById(R.id.answer2);
RadioButton c3 = (RadioButton) findViewById(R.id.answer3);
RadioButton c4 = (RadioButton) findViewById(R.id.answer4);
if (c1.isChecked()) {
return c1.getText().toString();
}
if (c2.isChecked()) {
return c2.getText().toString();
}
if (c3.isChecked()) {
return c3.getText().toString();
}
if (c4.isChecked()) {
return c4.getText().toString();
}
return null;
}
I think the activity doesn't exist anymore at a certain point when you try to make the dialog(probably when the CountDownTimer is near the end?!?).
Anyway I think finishing and starting the same activity for each question isn't such a good idea, instead you could use the current activity and simply restart the timer. For example:
public class QuestionActivity extends SherlockActivity implements
OnClickListener {
private CountDownTimer mCountDown;
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
mCountDown = new CountDownTimer(20000, 1000) {
#Override
public void onFinish() {
// myCounter.setText("Time up!");
timeUp(context);
}
#Override
public void onTick(long millisUntilFinished) {
myCounter.setText("Time left: "
+ String.valueOf(millisUntilFinished / 1000));
}
}.start();
// ...
and in the onClick callback do the same to setup a new question, stop the old timer and restart the new timer:
//check if end of game
if (currentGame.isGameOver()) {
Intent i = new Intent(this, EndgameActivity.class);
startActivity(i);
finish();
} else {
if (mCountDown != null) {
mCountDown.cancel();
}
currentQ = currentGame.getNextQuestion();
setQuestions();
mCountDown = new CountDownTimer(20000, 1000) {
#Override
public void onFinish() {
// myCounter.setText("Time up!");
timeUp(context);
}
#Override
public void onTick(long millisUntilFinished) {
myCounter.setText("Time left: "
+ String.valueOf(millisUntilFinished / 1000));
}
}.start();
}
Also, in the callback for the Dialog's Button I would first close the Dialog before finishing the Activity:
((AlertDialog) dialog).dismiss();
QuestionActivity.this.finish();