Home > Enterprise >  CountDownTimer flickers between seconds
CountDownTimer flickers between seconds

Time:12-08

I have a CountDownTimer that runs fine most times. However, it at times begins to flicker in the TextView displaying the countdown. I have it displayed like so:

HH:MM:SS

As it's counting down the last second will jump from for example 9 to 7 to 8 in one second. And then the next second it will quickly flicker from 8 to 6 to 7.

I have tried passing the variable millisUntilFinished directly to the method updating the textView, but the issue persists. Note that I'm saving the countdown and continuing it during onStop and onStart methods.

private fun startVisibleCountdown() {

    visibleCountdownRunning = true

    object : CountDownTimer(timeLeftInMillisecondsVisibleCounter, 1000) {

        override fun onTick(millisUntilFinished: Long) {
            timeLeftInMillisecondsVisibleCounter = millisUntilFinished
            updateCountDownTextVisible()
        }

        override fun onFinish() {
            //not relevant here
        }
    }.start()
}

fun updateCountDownTextVisible() {
    var seconds = (timeLeftInMillisecondsVisibleCounter / 1000).toInt()
    val hours = seconds / (60 * 60)
    val tempMint = seconds - hours * 60 * 60
    val minutes = tempMint / 60
    seconds = tempMint - (minutes * 60);

    textViewTimer.text = (String.format("d", hours)
              ":"   String.format("d", minutes)
              ":"   String.format("d", seconds))
}

Saving and returning to countdown when app is closed:

override fun onStop() {
    super.onStop()

    if (visibleCountdownRunning) {
        timeLeftInMillisecondsVisibleCounter  = System.currentTimeMillis()
    }

    val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) ?: return
    with (sharedPref.edit()) {
        putLong(timeLeftVisibleCounterKey, timeLeftInMillisecondsVisibleCounter)
        putBoolean(visibleCountdownRunningKey, visibleCountdownRunning)
        apply()
    }
}

override fun onStart() {
    super.onStart()

    val sharedPref = activity?.getPreferences(Context.MODE_PRIVATE) ?: return

    timeLeftInMillisecondsVisibleCounter = sharedPref.getLong(timeLeftVisibleCounterKey, 43200000)

    visibleCountdownRunning = sharedPref.getBoolean(visibleCountdownRunningKey, false)

    if (visibleCountdownRunning) {
        timeLeftInMillisecondsVisibleCounter -= System.currentTimeMillis()

        if (timeLeftInMillisecondsVisibleCounter > 0) {
            startVisibleCountdown()
        } else {
            timeLeftInMillisecondsVisibleCounter = 43200000

            visibleCountdownRunning = false
        }
    }
}

CodePudding user response:

Try this, its working with me but its java, convert it to Kotlin and it will work with you

    long time = 20 *60;
       new CountDownTimer( time * 1000, 1000) {

        @SuppressLint("SetTextI18n")
        public void onTick(long millisUntilFinished) {
            String timeremaining = TimetoString(millisUntilFinished);
            // update your textview here 
        }

        public void onFinish() {
        // here when the counter finish 
      
        }
    }.start();

CodePudding user response:

Since you are calling your startVisibleCountdown() function both from onStart() and when a button is clicked, it is likely that you have 2 CountDownTimer objects running and calling your updateCountDownTextVisible() code at the same time. This could explain the flickering and skipping seconds in a weird way.

Keep in mind, that even if you comment out the call to startVisibleCountdown() inside your onStart() function, Android sometimes does not prevent multi clicks from going through (depends on what type of button you are using and how did you wire up your onClickListener), so you might also be starting multiple countdown timers when clicking the button.

Add some log output (Log.d(...)) in you startVisibleCountdown(), just to make sure that it is not being called multiple times.

CodePudding user response:

You've got a few problems with your code - I don't know which part is causing the specific behaviour you're seeing, but I can point to some things that might contribute to it!

First up, here's your onStart:

override fun onStart() {
    ...
    timeLeftInMillisecondsVisibleCounter = sharedPref.getLong(timeLeftVisibleCounterKey, 43200000)

    visibleCountdownRunning = sharedPref.getBoolean(visibleCountdownRunningKey, false)

    if (visibleCountdownRunning) {
        timeLeftInMillisecondsVisibleCounter -= System.currentTimeMillis()
        if (timeLeftInMillisecondsVisibleCounter > 0) {
            startVisibleCountdown()
        } else {
            timeLeftInMillisecondsVisibleCounter = 43200000
            visibleCountdownRunning = false
        }
    }
}

You're fetching your "time remaining" value that you stored, and setting it on the top-level timeLeftInMillisecondsVisibleCounter variable. I'm just going to call it timeLeft, because it's gonna come up a lot.

Then, if the countdown is running, you subtract currentTimeMillis from timeLeft. That's an extremely large number, it's the number of milliseconds since the start of 1970 - timeLeft is always going to be extremely negative.

Then you check if timeLeft is positive, which it never is, so you basically always end up resetting it to your default remaining time of 43200000, and setting your running flag to false. I'm assuming this isn't meant to happen when you're restoring a running timer state!


But there's something else going on - here's your timer's onTick code:

override fun onTick(millisUntilFinished: Long) {
    timeLeftInMillisecondsVisibleCounter = millisUntilFinished
    updateCountDownTextVisible()
}

and here's the display update code:

fun updateCountDownTextVisible() {
    var seconds = (timeLeftInMillisecondsVisibleCounter / 1000).toInt()
    ...
}

Both of those are referencing timeLeft - the tick is setting it, the displayer is using it to calculate the number of seconds to display. You're starting that timer, but never stopping it, so it's always running, and resetting timeLeft to however many millis are left on the timer. So here's how the value is changing:

  • onStart sets it to the stored "time remaining" value
  • onStart sets it to a hugely negative value
  • onStart sets it to the default value
  • onTick sets it to the time left according to the timer

and the display code works with whatever the current value is. Like I said, I'm not sure exactly why you're getting glitches - I'd assume multiple timers running, which shouldn't happen according to your code (that timeLeft > 0 check should never succeed) but you haven't said how you start it in the first place. If you're pressing a button more than once, you'll get multiple timers, and then you have a potential for a race condition with them setting different values

  • Related