How to make a simple Countdown Timer in Android ⏲️
Final Output Video
Making a Countdown Timer in Android is very easy and it is also needed to be implemented for many features in day to day basis.
I’ll be showing you how to build one with Kotlin in Android Studio. I’ll be providing the complete project link at the bottom of the blog.
What we will do?
Build a timer that displays the time left between two dates in the following formats
-
‘daysLeft : hoursLeft : minutesLeft: secondsLeft’
-
‘hoursLeft : minutesLeft: secondsLeft’
-
‘minutesLeft: secondsLeft’
-
‘secondsLeft’
Firstly, navigate to app level build.gradle file and enable view binding. like this
buildFeatures{
viewBinding true
}
Our project will have single activity called as MainActivity.class and it’s layout file activity_main.xml
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_timer_days"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:textColor="@color/black"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_timer_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:textColor="@color/black"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_timer_days" />
<TextView
android:id="@+id/tv_timer_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:textColor="@color/black"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_timer_hours" />
<TextView
android:id="@+id/tv_timer_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:textColor="@color/black"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_timer_minutes" />
<Button
android:id="@+id/bt_start_timer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Start Timer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_timer_seconds" />
</androidx.constraintlayout.widget.ConstraintLayout>
It contains 4 textviews and a button which will show the time left in formats as mentioned at the top and a button which will start our timer.
Coming to the main part, let’s start writing code in MainActivity.class .
We will use CountDownTimer class which is provided by the Android Framework and we don’t need any other dependencies for it.
CountDownTimer is an abstract class hence we can’t make an instance of it therefore we need to declare it like below. Inside onCreate() in our MainActivity.class write
val countDownTimer = object : CountDownTimer(param1, param2) {
}
The two parameters are both of type Long, param1 is the time in milliseconds for which you want the timer to be set and param2 denotes the interval in milliseconds for which you want the timer to take for updating the count.
I call param1 as timerTime and set param2 = 1000 (1 second)
The next step is to implement the members
val countDownTimer = object : CountDownTimer(timerTime, 1000) {
override fun onTick(millisUntilFinished: Long) {
// todo
}
override fun onFinish() {
// todo
}
}
onTick() is called everytime after 1 second or 1000 milliseconds is completed. millisUntilFinished is the updated time after 1 second passes in milliseconds
onFinish() will be called when the countdown is completed
timerTime can be obtained by subtracting the received time from the current time. for eg. if we current time is January 1 2022 20:00:00 and the received time is January 2 2022 20:00:00 then the timerTime will be 1 day.
we can do this by
//(January 2 2022 20:00:00 in milliseconds)
val timeReceived = 1641308520000
val timerTime = timeReceived - System.currentTimeMillis()
I recommend using https://currentmillis.com/ for time to milliseconds conversion.
Also we need the timer to start after the button click and few checks such as timeReceived is greater than currentTime. Also what happens after the timer is completed needs to done.
binding.btStartTimer.setOnClickListener {
val timerTime = timeReceived - currentTime
if (timerTime > 0) {
val countDownTimer = object : CountDownTimer(timerTime, 1000) {
override fun onTick(millisUntilFinished: Long) {
// todo
}
override fun onFinish() {
binding.tvTimerDays.text = "Completed"
binding.tvTimerHours.text = "Completed"
binding.tvTimerMinutes.text = "Completed"
binding.tvTimerSeconds.text = "Completed"
}
}
countDownTimer.start()
} else {
Toast.makeText(this, "Invalid Time", Toast.LENGTH_SHORT).show()
}
}
inside onTick() we will display the updated time after every second passes.
It’s important to display the time in an appropriate format, currently it’s available in milliseconds.
Let’s make a function which will give the time in Proper Format.
private fun getFormattedTime(millis: Long) {
val totalSeconds = millis / 1000
val seconds: Int = (totalSeconds % 60).toInt()
val totalMinutes = millis / (1000 * 60)
val minutes: Int = (totalMinutes % 60).toInt()
val totalHours = millis / (1000 * 60 * 60)
val hours: Int = (totalHours % 24).toInt()
/**
* daysLeft : hoursLeft : minutesLeft : secondsLeft
* */
val totalDays = (millis / (1000 * 60 * 60 * 24)).toInt()
binding.tvTimerDays.text = "${totalDays}d : ${hours}h : ${minutes}m : ${seconds}s"
/**
* hoursLeft : minutesLeft : secondsLeft
* */
binding.tvTimerHours.text = "${totalHours}h : ${minutes}m : ${seconds}s"
/**
* minutesLeft : secondsLeft
* */
binding.tvTimerMinutes.text = "${totalMinutes}m : ${seconds}s"
/**
* secondsLeft
* */
binding.tvTimerSeconds.text = "${totalSeconds}s"
}
We have to call this function from onTick() and the updated code looks like this.
binding.btStartTimer.setOnClickListener {
val timerTime = timeReceived - currentTime
if (timerTime > 0) {
val countDownTimer = object : CountDownTimer(timerTime, 1000) {
override fun onTick(millisUntilFinished: Long) {
getFormattedTime(millisUntilFinished)
}
override fun onFinish() {
binding.tvTimerDays.text = "Completed"
binding.tvTimerHours.text = "Completed"
binding.tvTimerMinutes.text = "Completed"
binding.tvTimerSeconds.text = "Completed"
}
}
countDownTimer.start()
} else {
Toast.makeText(this, "Invalid Time", Toast.LENGTH_SHORT).show()
}
}
Run the app with different values of timeReceived to set different duration for your timer.
We have successfully completed our aim.
For the complete code go to