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" valueonStart
sets it to a hugely negative valueonStart
sets it to the default valueonTick
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