I'm designing a game and have a problem with countdowntimer not stopping. The timer starts normally as it should, and should stop in the end() method. It works well when ENEMY_HULL is checked as 0 or less, which is calulated seperatly when the users presses a button, and checkEnemyDefeat() is called, but it does not work when ENEMY_CREW is 0. On both situations the message in log cat that the timer is stopped appears, so my guess is that .cancel() is called. I've included all code that seems relevant.
NOTE: All variables including CountDownTimer are global variables
Here is the method containing the timer:
private void BadGameLoop() {
if (GAME_PAUSED == true && !GAME_TIME_STARTED) {
Log.d(" -------- ", "TIMER STARTED");
GAME_PAUSED = false;
GAME_TIME_STARTED = true;
gameTimer = new CountDownTimer(GAME_TIME, UPDATE_RATE) {
#Override
public void onTick(long millisUntilFinished) {
TICKS++;
timer.setText(String.valueOf(GAME_TIME / 1000 - TICKS / 10));
if((CREW_BOARDING > 0 || ENEMY_CREW_BOARDING > 0) && TICKS%10 == 0){
crewFightCalculator();
}
updateViews();
}
#Override
public void onFinish() {
TICKS=0;
GAME_TIME_STARTED = false;
GAME_PAUSED = true;
end();
}
};gameTimer.start();
}
}
Next is the crewFightCalculator method:
private void crewFightCalculator(){
// this just changes the values of ENEMY_CREW (global variable) and then
//calls checkEnemyDefeat() to verify if it should end the timer or not
checkEnemyDefeat();
}
The checkEnemyDefeat and end methods:
private void checkEnemyDefeat(){
if(ENEMY_HULL <= 0){
updateViews();
end();
}else if ( ENEMY_CREW < 1){
updateViews();
end();
}
}
private void end(){
if(GAME_TIME_STARTED){
GAME_TIME_STARTED = false;
gameTimer.cancel();
Log.d("---------"," TIMER STOPPED !");
}
// do more stuff
}
It is simply bizzare to me why it dosen't work. My only guess is that it has something to do with the fact that .cancel() is not triggered by a chain of functions started by user input .
The only solution to this problem that I found, was to modify the onTick method, so that it should check every time if it should run or stop the timer like this:
private void BadGameLoop() {
if (GAME_PAUSED == true && !GAME_TIME_STARTED) {
Log.d(" -------- ", "TIMER STARTED");
GAME_PAUSED = false;
GAME_TIME_STARTED = true;
gameTimer = new CountDownTimer(GAME_TIME, UPDATE_RATE) {
#Override
public void onTick(long millisUntilFinished) {
if(GAME_TIME_STARTED) {
TICKS++;
timer.setText(String.valueOf(GAME_TIME / 1000 - TICKS / 10));
if((CREW_BOARDING > 0 || ENEMY_CREW_BOARDING > 0) && TICKS%10 == 0){
crewFightCalculator();
}
updateViews();
}else{
gameTimer.cancel();
}}
#Override
public void onFinish() {
TICKS=0;
GAME_TIME_STARTED = false;
GAME_PAUSED = true;
end();
}
};gameTimer.start();
}
}
Still, i can't figure out why it didn't work properly before.
Related
I have a 5 seconds count down timer and I need to detect the amplitude of acceleration within the duration. If the amplitude meet the statement, a true will be return. However, due to the void method OnTick and also it is an inner class, I couldn't return or pass any value to outer class.
public boolean low_high_Detection(final double ampA) {
new CountDownTimer(5000, 1000) {
public void onTick(long millisUntilFinished) {
final double low_TH = 9.0, high_TH = 10.5;
boolean lying_on_floor = false;
if(ampA > low_TH && ampA <high_TH)
{
lying_on_floor = true;
}
}
public void onFinish() {
Toast.makeText(detect.getBaseContext(), "5 seconds dectection over",Toast.LENGTH_SHORT).show();
}
}.start();
if (lying_on_floor == true)
{
return true;
}
return false;
}
Anyone can suggest me how can I fix this method? Or there is another way to deal with it.
This can be achieved with an anonymous class as a listener.
Declare an interface which actues as a listener.
public interface LowHightDetectionListener {
void onDetected(boolean result);
}
Then, to call this method and pass an instance of this listener to the method low_high_Detection()
low_high_Detection(ampA, new LowHightDetectionListener {
#Override
public void onDetected(boolean result) {
// Process the result
}
});
Here you get the "returned" value.
To return the desired value inside the low_high_Detection() method you need to call the listener.
public void low_high_Detection(final double ampA, final LowHightDetectionListener callback) {
new CountDownTimer(5000, 1000) {
public void onTick(long millisUntilFinished) {
final double low_TH = 9.0, high_TH = 10.5;
boolean lying_on_floor = false;
if(ampA > low_TH && ampA <high_TH) {
lying_on_floor = true;
callback.onDetected(true); // EXAMPLE OF RETURNING VALUE INSIDE AN ANONYMOUS CLASS
}
}
public void onFinish() {
Toast.makeText(detect.getBaseContext(), "5 seconds dectection over",Toast.LENGTH_SHORT).show();
}
}.start();
if (lying_on_floor == true) {
callback.onDetected(true);
return;
}
callback.onDetected(false);
}
}
I want to log when something happened and delay 1 minitues. so I use a timer and timertask live this:
private Timer _checkTimer;
private boolean _timerStart = false;
private void requestPlayPosterItem(long id) {
//
if(_timerStart && _checkTimer != null ){
_checkTimer.cancel();
}
_checkTimer = new Timer();
_checkTimer.schedule(new TimerTask(){
#Override
public void run() {
_timerStart = false;
// do log
}
}, 30000);
_timerStart = true;
// others -----
}
and at activity onPause(), I cancel the timer too:
if(_checkTimer != null && _timerStart){
_checkTimer.cancel();
}
my question: _checkTimer can reuse? is there resource leak(timer)?
I'm trying to use this timer, like on this question suggested:
https://stackoverflow.com/questions/13806545/how-to-extend-countdown-timer-with-pause,
but doesn't work as expected. The pause/resume works fine, but if I cancel and recreate the timer, then the counting starts from the last paused time. I need to start from the initial value.
For eg the counter's intial value is 3 minutes. If I paused it at 2 minutes, then when I'm trying to create it again it starts from 2 minutes.
Any help?
public class MainActivity extends ButtonMethods implements OnClickListener {
private CountDownTimerWithPause timerPausable = null;
int milis = 180000;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timerPausable = new CountDownTimerWithPause(milis, 1000, true)
{
#Override
public void onTick(long millisUntilFinished)
{
timer.setText("" + millisUntilFinished / 1000);
}
#Override
public void onFinish()
{
timer.setText("180");
DisableRandomButtons();
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bNewWord:
if(clicked == false){
clicked = true;
timerPausable.create();
}
else{
clicked = false;
timerPausable.cancel();
timerPausable=null;
milis=180000;
timerPausable.create();
}
break;
case R.id.imageView2:
tapsound.start();
if(isclicked == false){
isclicked = true;
timerPausable.pause();
}
else{
isclicked = false;
timerPausable.resume();
}
break;
Its hard to say what your exact problem is without seeing your code but what I did was call cancel() on my timer then make it equal to null to reset it. Mine is a little different and I handle it in onBackPressed()
#Override
public void onBackPressed()
{
super.onBackPressed();
timer.cancel(); // timer is a reference to my inner CountDownTimer class
timer = null;
secs = 10;
}
I think this is what you are looking for. It works for me.
I have an animation in my Android app that flashes a TextView different colors. I've used a TimerTask, Timer, and Runnable method to implement this. What I need to do is stop the thread when a user leaves the app during this animation in onPause(), and resume the thread when the user returns to the app in onResume(). The following is the code I've implemented, but it's not working (the onPause(), and onResume() pieces), and I don't understand why. I've read a few other posts on similar matters, but they haven't helped me figure out what to do in my situation. I've read that TimerTasks are outdated, and I should probably use an ExecutorService method; it is unclear to me as how to implement this function.
...timerStep5 = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (b5) {
cashButton2SignalText.setBackgroundColor(Color.RED);
cashButton2SignalText.setTextColor(Color.WHITE);
b5=false;
} else {
cashButton2SignalText.setBackgroundColor(Color.WHITE);
cashButton2SignalText.setTextColor(Color.RED);
b5=true;
}
}
});
}
};
timer5.schedule(timerStep5,250,250);
}
public void onPause(){
super.onPause();
timerStep5.cancel();
}
public void onResume(){
super.onResume();
timerStep5.run();
}
After a TimerTask is canceled, it cannot run again, you have to create a new instance.
Read details here:
https://stackoverflow.com/a/2098678/727768
ScheduledThreadPoolExecutor is recommended for newer code, it handles the cases like exceptions and task taking longer time than the scheduled interval.
But for your task, TimerTask should be enough.
Here's how I did it. Add pauseTimer boolean where ever the pause takes place (button listener perhaps) and don't count timer if true.
private void timer (){
Timer timer = new Timer();
tv_timer = (TextView) findViewById(R.id.tv_locationTimer);
countTimer = 0;
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
String s_time = String.format("%02d:%02d:%02d",
countTimer / 3600,
(countTimer % 3600) / 60,
countTimer % 60);
tv_timer.setText(s_time);
if (!pauseTimer) countTimer++;
}
});
}
}, 1000, 1000);
}
Timer timer1;
private boolean timerStartFlag = false;
private boolean hiddenVisibleFrg = false;
int timerSize = 0;
int videoTime = 0;
#Override
public void onPause() {
super.onPause();
Log.e("keshav", "onPause timer1 " +timer1);
if (timerSize >0 &&hiddenVisibleFrg){
timerStartFlag =true;
}
if (timer1 != null) {
this.timer1.cancel();
}
}
#Override
public void onResume() {
super.onResume();
if (timerSize >0 && timerStartFlag && hiddenVisibleFrg) {
callTimerTask(timerSize);
timerStartFlag = false;
}
}
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (!hidden) {
Log.e("keshav", "HomeFragment visible ");
if (timerSize >0 && timerStartFlag) {
callTimerTask(timerSize);
timerStartFlag=false;
}
hiddenVisibleFrg=true;
} else {
Log.e("keshav", "HomeFragment in visible " +timer1);
if (timer1 != null) {
this.timer1.cancel();
}
if (timerSize >0){
timerStartFlag =true;
}
hiddenVisibleFrg=false;
}
}
private void callTimerTask(int size) {
// TODO Timer for auto sliding
printLog("callTimerTask size " + size);
timer1 = new Timer();
timer1.schedule(new TimerTask() {
#Override
public void run() {
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
if (getActivity() == null) {
return;
}
if (count1 < size - 1) {
//TODO ADD ME kk
count1++;
} else {
count1 = 0;
}
if (intro_images != null) {
intro_images.setCurrentItem(count1);
}
videoTime++;
Log.e("KeshavTimer", "callTimerTask videoTime " + videoTime);
}
});
} else {
printLog("callTimerTask getActivity is null ");
}
}
}, 1000, 1000);
// TODO 1000, 3000;
}
For Kotlin user, checkout this
How to use:
// 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()
I have used Countdown Timer like this
new CountDownTimer(15000, 15) {
public void onTick(long millisUntilFinished) {
long seconds=millisUntilFinished/1000;
long min=millisUntilFinished%100;
timeleft=(int) (seconds*1000+min);
if(millisUntilFinished>=10000)
{
changeText.setTextColor(Color.GREEN);
}
else if(millisUntilFinished>=5000)
{
changeText.setTextColor(Color.MAGENTA);
}
else
{
changeText.setTextColor(Color.RED);
}
changeText.setText(String.format("%02d", seconds )+ "."+String.format("%02d", min )+" sec");
}
public void onFinish() {
timeleft=0;
missed++;
nametext.setTextColor(Color.RED);
nametext.setText("Time Up!");
bottombutton.setVisibility(View.INVISIBLE);
globalflag=13;
changeText.setTextColor(Color.RED);
changeText.setText("0.00 Sec");
Handler myHandler = new Handler();
myHandler.postDelayed(mMyRunnablecif, 3000);
}
}.start();
On a button click I have called cancel() but it stops counting for a while and then calls onFinish(). I need not to call onFinish() after calling cancel(). Is there any solution for this. Any help will be highly appreciated.
Inside your onClick set a Boolean (buttonPressed for example) to true.
In your onFinish check this Boolean:
if (buttonPressed == true)
{
//do nothing
}
else
{
//run code
}
You could use Timer instead and do something like this:
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
// do your updates here
mUpdateTimeHandler.postDelayed(this, 1000);
}
};
Handler mUpdateTimeHandler = new Handler();
mUpdateTimeHandler.postDelayed(mUpdateTimeTask, 100);
When cancelling the task:
mUpdateTimeHandler.removeCallbacks(mUpdateTimeTask);
I also don't understand why the onFinish gets called despite calling cancel(). I guess the only workaround for this is to just null check your calls in onFinish to prevent any NPE's.
public void onFinish() {
if (nametext == null || bottombutton == null || changeText == null || ... ) {
return;
}
...