36

I know how to create a simple countdown timer in Java. But I'd like to create this one in Kotlin.

package android.os;

new CountDownTimer(20000, 1000) {
    public void onTick(long millisUntilFinished) {
        mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
    }
    public void onFinish() {
        mTextField.setText("Time's finished!");
    }
}.start();

How can I do it using Kotlin?

Willi Mentzel
  • 21,499
  • 16
  • 88
  • 101
Andy Fedoroff
  • 26,838
  • 8
  • 85
  • 144
  • 1
    Countdown timer can be easy to use but not really accurate. You'll be able to see the timer skipping seconds as the time elapses, if the input time is long enough. Any lag in the system will cause milliseconds of delay on each tick and will eventually cause skipping as the delay accumulates. For more accurate timer implementation, check [this post](https://stackoverflow.com/questions/12762272/android-countdowntimer-additional-milliseconds-delay-between-ticks) – WasabiTea Jan 08 '19 at 23:19

5 Answers5

94

You can use Kotlin objects:

val timer = object: CountDownTimer(20000, 1000) {
    override fun onTick(millisUntilFinished: Long) {...}

    override fun onFinish() {...}
}
timer.start()
Blue Jones
  • 353
  • 1
  • 9
Danail Alexiev
  • 6,536
  • 1
  • 16
  • 27
9

Chronometer can be set to count down and it seems to me the easiest way.

Add the Chronometer view in your layout xml, example

<Chronometer  
 android:id="@+id/view_timer"   
 tools:targetApi="24"  
 android:layout_width="wrap_content"  
 android:layout_height="wrap_content"/>

Then in your activity or fragment:

   view_timer.isCountDown = true
   view_timer.base = SystemClock.elapsedRealtime() + 20000
   view_timer.start()
LiA
  • 346
  • 4
  • 4
  • 9
    Minimum API level for isCountDown method is 24 (Android 7), so not for every project – AFD Sep 19 '19 at 12:17
6

I've solved my problem with timer in Kotlin like this:

class Timer {

    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.Default + job)

    private fun startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: () -> Unit) = scope.launch(Dispatchers.IO) {
        delay(delayMillis)
        if (repeatMillis > 0) {
            while (true) {
                action()
                delay(repeatMillis)
            }
        } else {
            action()
        }
    }

    private val timer: Job = startCoroutineTimer(delayMillis = 0, repeatMillis = 20000) {
        Log.d(TAG, "Background - tick")
        doSomethingBackground()
        scope.launch(Dispatchers.Main) {
            Log.d(TAG, "Main thread - tick")
            doSomethingMainThread()
        }
    }

    fun startTimer() {
        timer.start()
    }

    fun cancelTimer() {
        timer.cancel()
    }
//...
}

I've used Coroutines for a timer.

Dima Kozhevin
  • 3,091
  • 9
  • 33
  • 48
5

If you want to show a countdown with days hours minutes and seconds

private lateinit var countDownTimer:CountDownTimer
.
.
.
    fun printDifferenceDateForHours() {

            val currentTime = Calendar.getInstance().time
            val endDateDay = "03/02/2020 21:00:00"
            val format1 = SimpleDateFormat("dd/MM/yyyy hh:mm:ss",Locale.getDefault())
            val endDate = format1.parse(endDateDay)

            //milliseconds
            var different = endDate.time - currentTime.time
            countDownTimer = object : CountDownTimer(different, 1000) {

                override fun onTick(millisUntilFinished: Long) {
                    var diff = millisUntilFinished
                    val secondsInMilli: Long = 1000
                    val minutesInMilli = secondsInMilli * 60
                    val hoursInMilli = minutesInMilli * 60
                    val daysInMilli = hoursInMilli * 24

                    val elapsedDays = diff / daysInMilli
                    diff %= daysInMilli

                    val elapsedHours = diff / hoursInMilli
                    diff %= hoursInMilli

                    val elapsedMinutes = diff / minutesInMilli
                    diff %= minutesInMilli

                    val elapsedSeconds = diff / secondsInMilli

                    txt_timeleft.text = "$elapsedDays days $elapsedHours hs $elapsedMinutes min $elapsedSeconds sec"
                }

                override fun onFinish() {
                    txt_timeleft.text = "done!"
                }
            }.start()
        }

If you are navigating to another activity/fragment, make sure to cancel the countdown

countDownTimer.cancel()

Code output

51 days 17 hs 56 min 5 sec

Gastón Saillén
  • 9,076
  • 4
  • 39
  • 58
  • 1
    Please don’t teach the young ones to use the long outdated and notoriously troublesome `SimpleDateFormat` class. At least not as the first option. And not without any reservation. Today we have so much better in [`java.time`, the modern Java date and time API,](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter`. Yes, you can use it on Android. For older Android see [How to use ThreeTenABP in Android Project](https://stackoverflow.com/questions/38922754/how-to-use-threetenabp-in-android-project). – Ole V.V. Dec 14 '19 at 11:17
  • 1
    DateTimeFormatter needs minimum api lvl 26 (android 8), with SimpleDateFormat we can use it from api 21 in wich handles more devices, I will update also the answer to work with DateTimeFormatter :) – Gastón Saillén Dec 14 '19 at 18:24
  • 1
    There’s a backport. To use java.time with min SDK 21 (for example), add [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) to your Android project. And make sure to import the date and time classes from the `org.threeten.bp` package with subpackages. Read more in the link at the end of my previous comment. – Ole V.V. Dec 14 '19 at 19:14
3

Try to use objects, like this :

var countDownTimer = object : CountDownTimer(2000, 1000) {
    // override object functions here, do it quicker by setting cursor on object, then type alt + enter ; implement members
}

Try this website : https://try.kotlinlang.org/#/Kotlin%20Koans/Introduction/Java%20to%20Kotlin%20conversion/Task.kt

You have a little button "Convert from Java" on the top right that could be useful to you.

EDIT:

Do not forget to start this object when you need it, by adding .start() at the end of the declaration, or wherever in your activity / fragment :

countDownTimer.start()
Willi Mentzel
  • 21,499
  • 16
  • 88
  • 101
Elynad
  • 835
  • 1
  • 6
  • 26