Search code examples
javaandroidandroid-buttoncountdowntimer

How do I make a CountDown show/start at the same moment my Button gets enabled?


"How do I make a CountDown show at the same moment my Button gets enabled ?"

Additional info regarding the button: the Buttons Job is it to click 5 times through a stringarray displayed in a Textview to then get disabled for 5 seconds to do the same task again.

so ..I would like a CountDown to visually show those 5 seconds(the time the button is enabled) count down for the User to see.

sadly I dont have an idea how to connect my Button with an CountDown to let it know its supposed to count down at that particular time the Button is enabled.

Also I would like for the CountDown to start everytime the Button gets enabled.

I looked into https://developer.android.com/reference/android/os/CountDownTimer but it doesnt seem to have a solution for that particular case.

thats my Code for the Button as of now :

next_button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            if (currentnumber == list.length) {
                currentnumber = 0;
            }
            if (Curclicks == mod - 1) {
                next_button.setEnabled(false);
                display.setText(list[currentnumber]);
                currentnumber++;


                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //the button will unlock after the delay specified
                        next_button.setEnabled(true);
                        Curclicks = 0;

                    }
                }, delay);

            } else {
                display.setText(list[currentnumber]);
                currentnumber++;

            }
            Curclicks++;

        }

    });

UI Thread code can solve that ? :

  private void runThread() {

            new Thread() {
                public void run() {
                    while (delay == 5000) { //delay = 5000 ( 5 secs)
                        try {
                            runOnUiThread(new Runnable() {

                                @Override
                                public void run() {
                                    timer.setText("" + delay);//timer=TxtView
                                }
                            });
                            Thread.sleep(300);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();

Solution

  • Here is an example of how you can use the postDelayed() method of the Handler to create a count down. I have purposefully left the code a bit verbose so you can go through it step-by-step in order to see what is happening.

    Create a few class variables and constants.

    private static final long COUNT_DOWN_TICKS = 100l;
    private static final long COUNT_DOWN_FINISH = 5000l;
    private long countdownElapsed = 0l;
    private Handler mCountDownHandler = new Handler();
    

    COUNT_DOWN_FINISH is set to 5000 --> 5 sec. But can be changed to anything you need. Also I use COUNT_DOWN_TICKS set to 100 --> 0.1 sec, just in case you want to display a more precise count down.

    From your OnClick() method just call startCountDown() to get the count down started.

    private void startCountDown() {
        try {
            countdownElapsed = 0l;
            next_button.setEnabled(false);
            displayCountDown();
            mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
        }
        catch (Exception ex){
            Log.e(TAG, ex.getMessage());
        }
    }
    
    private Runnable mCountDownRunnable = new Runnable() {
        @Override
        public void run() {
            countdownElapsed = countdownElapsed + COUNT_DOWN_TICKS;
    
            if(countdownElapsed >= COUNT_DOWN_FINISH){
                releaseCountDownHandler();
                next_button.setEnabled(true);
            }
            else{
                mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
            }
    
            long secFull = countdownElapsed % 1000;
            if(secFull == 0){
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        displayCountDown();
                    }
                });
            }
        }
    };
    
    private void releaseCountDownHandler() {
        try {
            if(mCountDownRunnable != null) {
                mCountDownHandler.removeCallbacks(mCountDownRunnable);
            }
        }
        catch (Exception ex){
            Log.e(TAG, ex.getMessage());
        }
    }
    
    
    private void displayCountDown(){
        long t = (COUNT_DOWN_FINISH - countdownElapsed)/1000;
        String myTime = String.valueOf(t);
        timer.setText(myTime);
    }
    

    In order to dispose of the Runnable properly you will want to call releaseCountDownHandler() from the onPause() method. This is just a short running Thread, but it should still not be ignored.

    I prefer the Handler with the postDelay() method to the Thread.sleep() method--something about putting any thread to sleep is disconcerting. Also note that it is a good idea to get accustom to checking the elapsed time condition with ">=" RATHER than "==" depending on the implementation (e.g. you use SystemClock.elapsedRealtime() instead) the condition just might miss the exact value!


    EDIT


    Somewhere under the definition of your Activity class (for this example I will call it MainActivity) you will need to declare a few variables. Since they are being defined inside the class and NOT inside a method the are referred to as "class variables" and they have a scope of entire class when defined "private".

    public class MainActivity extends AppCompatActivity {
    
        //class variables
        private static final long COUNT_DOWN_TICKS = 100l;
        private static final long COUNT_DOWN_FINISH = 5000l;
        private long countdownElapsed = 0l;
        private Handler mCountDownHandler = new Handler();
        private Button next_button;
        private TextView timer;
    
    
    ....    
    
    }
    

    You probably have declared the onClick() method inside the onCreate() method of the MainActivity class. So just add the following code:

    next_button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        startCountDown();
    }
    

    Everything else I provide are method inside the MainActivity class...NOT inside any other method. So below the onCreate() method add all methods that I previously posted.

    It will look something like this:

    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = MainActivity.class.getSimpleName();
    
        //class variables
        private static final long COUNT_DOWN_TICKS = 100l;
        private static final long COUNT_DOWN_FINISH = 5000l;
        private long countdownElapsed = 0l;
        private Handler mCountDownHandler = new Handler();
        private Button next_button;
        private TextView timer;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // you must compensate for the actual layout for your activity
            setContentView(R.layout.activity_main);
    
    
            // you must compensate for the actual id of the TextView
            timer = findViewById(R.id.tvTimer);
    
            // you must compensate for the actual id of the Button
            next_button = findViewById(R.id.btnNext);
            next_button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    startCountDown();
                }
            });
        }
    
        private void startCountDown() {
            try {
                countdownElapsed = 0l;
                next_button.setEnabled(false);
                displayCountDown();
                mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
            }
            catch (Exception ex){
                Log.e(TAG, ex.getMessage());
            }
        }
    
        private Runnable mCountDownRunnable = new Runnable() {
            @Override
            public void run() {
                countdownElapsed = countdownElapsed + COUNT_DOWN_TICKS;
    
                if(countdownElapsed >= COUNT_DOWN_FINISH){
                    releaseCountDownHandler();
                    next_button.setEnabled(true);
                }
                else{
                    mCountDownHandler.postDelayed(mCountDownRunnable, COUNT_DOWN_TICKS);
                }
    
                long secFull = countdownElapsed % 1000;
                if(secFull == 0){
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            displayCountDown();
                        }
                    });
                }
            }
        };
    
        private void releaseCountDownHandler() {
            try {
                if(mCountDownRunnable != null) {
                    mCountDownHandler.removeCallbacks(mCountDownRunnable);
                }
            }
            catch (Exception ex){
                Log.e(TAG, ex.getMessage());
            }
        }
    
    
        private void displayCountDown(){
            long t = (COUNT_DOWN_FINISH - countdownElapsed)/1000;
            String myTime = String.valueOf(t);
            timer.setText(myTime); 
        }   
    
    }