How to create count up Timer in Android

How to create count up Timer in Android

Approach 1: Chronometer

In Android, Chronometer is a class that implements a simple timer. Chronometer is a subclass of TextView. This class helps us to add a timer in our app.

You can give start time in the elapsedRealTime() timebase and it start counting from that. If we don’t give base time then it will use the time at which time we call start() method. By default a Chronometer displays the current timer value in the form of MM:SS or H:MM:SS. We can set our own format into an arbitrary string.

Let’s start with the interface:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal">

    <Chronometer
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:id="@+id/cmTimer" />

    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start"
        android:onClick="onClick" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop"
        android:onClick="onClick" />

    <Button
        android:id="@+id/btnReset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Reset"
        android:onClick="onClick" />
</LinearLayout>

Let’s discuss some important methods of Chronometer that may be called in order to manage the chronometer.

  • start(): start function of chronometer is used to start the counting up.
  • stop(): stop function of chronometer is used to stop the counting up.
  • setFormat(String format): set format function of chronometer is used to set the format string used to display. In other words we can say it is used to display text, numbers etc along-with chronometer.
  • setOnChronometerTickListener(Chronometer.OnChronometerTickListener listener). This is a listener event which is automatically called when the chronometer changes.
  • setBase(long base): set base function of chronometer is used to set the time that count up time is in reference to. You can give it a start time in the elapsedRealtime() timebase, and it counts up from that, or if you don’t give it a base time, it will use the time at which you call start(). SystemClock.elapsedRealtime() this returns the time in milliseconds since your device booted included sleep time. This means that if you test your app on an emulator and the emulator opened 30 minutes ago, this method will return 1800000 milliseconds (30 min).

Following is our Activity.

public class MainActivity extends AppCompatActivity {

    Chronometer cmTimer;
    Button btnStart, btnStop, btnReset;
    Boolean resume = false;
    long elapsedTime;
    String TAG = "TAG";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        cmTimer = (Chronometer) findViewById(R.id.cmTimer);
        btnStart = (Button) findViewById(R.id.btnStart);
        btnStop = (Button) findViewById(R.id.btnStop);
        btnReset = (Button) findViewById(R.id.btnReset);

        // example setOnChronometerTickListener
        cmTimer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
            public void onChronometerTick(Chronometer arg0) {
                if (!resume) {
                    long minutes = ((SystemClock.elapsedRealtime() - cmTimer.getBase())/1000) / 60;
                    long seconds = ((SystemClock.elapsedRealtime() - cmTimer.getBase())/1000) % 60;
                    elapsedTime = SystemClock.elapsedRealtime();
                    Log.d(TAG, "onChronometerTick: " + minutes + " : " + seconds);
                } else {
                    long minutes = ((elapsedTime - cmTimer.getBase())/1000) / 60;
                    long seconds = ((elapsedTime - cmTimer.getBase())/1000) % 60;
                    elapsedTime = elapsedTime + 1000;
                    Log.d(TAG, "onChronometerTick: " + minutes + " : " + seconds);
                }
            }
        });

    }

    public void onClick(View v) {
        switch(v.getId()) {
            case R.id.btnStart:
                btnStart.setEnabled(false);
                btnStop.setEnabled(true);

                if (!resume) {
                    cmTimer.setBase(SystemClock.elapsedRealtime());
                    cmTimer.start();
                } else {
                    cmTimer.start();
                }
                break;

            case R.id.btnStop:
                btnStart.setEnabled(true);
                btnStop.setEnabled(false);
                cmTimer.stop();
                cmTimer.setText("");
                resume = true;
                btnStart.setText("Resume");
                break;

            case R.id.btnReset:
                cmTimer.stop();
                cmTimer.setText("00:00");
                resume = false;
                btnStop.setEnabled(false);
                break;
        }
    }
}

The start button can be clicked in two scenarios:

  1. Starting the Chronometer for the first time:
    • We set base of the Chronometer, which is the time in Milliseconds from where the Chronometer starts counting from. We set it to SystemClock.elapsedRealtime(). Which is the time in Milliseconds since the device boot (equivalent to current time).
    • then start the Chronometer
  2. Resuming the chronometer count after start: here we start the Chronometer again to resume counting from the value it had when it was stopped.

The stop button pauses the Chronometer. And sets the resume flag to true to indicate that the next click on the start button will resume counting.

The reset button resets the chronometer so the next click on start button will start counting from zero.

Result

android_timer_countup.png

Approach 2: Handler

In this example we are going to create a simple Android Timer application. We are going to use some very basic ideas and tools, like Handler, that you can use in many cases in your Applications.

Following layout is very simple

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp">

    <TextView
        android:text="00:00:00"
        android:id="@+id/tvTimer"
        android:textSize="40sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvTimer"
        android:layout_alignParentLeft="true"
        android:onClick="start"
        android:text="Start"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tvTimer"
        android:layout_alignParentRight="true"
        android:onClick="stop"
        android:text="Stop"/>

</RelativeLayout>

Following is MainActivity.java

public class SixActivity extends AppCompatActivity {
    TextView tvTimer;
    long startTime, timeInMilliseconds = 0;
    Handler customHandler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_six);

        tvTimer = (TextView) findViewById(R.id.tvTimer);
    }

    public static String getDateFromMillis(long d) {
        SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
        df.setTimeZone(TimeZone.getTimeZone("GMT"));
        return df.format(d);
    }

    public void start(View v) {
        startTime = SystemClock.uptimeMillis();
        customHandler.postDelayed(updateTimerThread, 0);
    }

    public void stop(View v) {
        customHandler.removeCallbacks(updateTimerThread);
    }

    private Runnable updateTimerThread = new Runnable() {
        public void run() {
            timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
            tvTimer.setText(getDateFromMillis(timeInMilliseconds));
            customHandler.postDelayed(this, 1000);
        }
    };
}

The code of this section is pretty much self explanatory. The interesting part is how to update the value of the timer. We are going to use a Handler for that. The Handler takes a Runnable object and it schedules its execution; it places the runnable process as a job in an execution queue to be run after a specified amount of time. The Runnable will be run on the thread to which this handler is attached.

Result

android_timer.png

Approach 3: Reverse of CountDownTimer

We 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(TAG, "Elapsed: " + (totalSeconds * 1000 - millisUntilFinished) / 1000);
    }

    public void onFinish() {
        Log.d(TAG, "Time's up!");
    }
};

To start the timer.

timer.start()

To stop the timer.

timer.cancel()

How to set Chronometer base time from timestamp?

long elapsedRealtimeOffset = System.currentTimeMillis() - SystemClock.elapsedRealtime();
cmTimer.setBase(timestamp - elapsedRealtimeOffset);
cmTimer.start();
comments powered by Disqus