我正在使用倒数计时器进行音频通知......从一开始就不准确......
使用初始参数
private final long startCountDown;
private final long intervalCountDown;
...
startCountDown = 180 * 1000; // 3 mns - to be set from Preferences later
intervalCountDown = 60 * 1000; // 1 mns - to be set from Preferences later
...
public void onTick(long millisUntilFinished) {
Log.d(TAG, "notify countDown: " + millisUntilFinished + " msecs");
}
countDownTimer = new SwimCountDownTimer(startCountDown,intervalCountDown);
....
public void startCountDown() {
Log.d(TAG, "start countDown for " + startCountDown + " msecs" );
countDownTimer.start();
}
我可以在日志中看到初始倒计时正确设置为180000但下一个应该是120000并且它设置为119945 !!!
04-27 14:50:42.146: I/SWIMMER(8670): notify countDown: 180000 msecs
04-27 14:51:42.206: I/SWIMMER(8670): notify countDown: 119945 msecs
这非常令人讨厌,因为音频通知器预计只会说 2分钟"而不是" 1分59秒" ...;为什么间隔不对...? 我可以自己设置文本到语音字符串...但有没有办法获得正确的数据?
感谢您的建议
答案 0 :(得分:10)
我知道这是一个古老的问题 - 但我也遇到过这个问题,并且认为我会分享我的解决方案。
显然CountDownTimer不是很准确,所以我决定使用java.util.Timer来实现更精确的倒数计时器:
public abstract class PreciseCountdown extends Timer {
private long totalTime, interval, delay;
private TimerTask task;
private long startTime = -1;
private boolean restart = false, wasCancelled = false, wasStarted = false;
public PreciseCountdown(long totalTime, long interval) {
this(totalTime, interval, 0);
}
public PreciseCountdown(long totalTime, long interval, long delay) {
super("PreciseCountdown", true);
this.delay = delay;
this.interval = interval;
this.totalTime = totalTime;
this.task = getTask(totalTime);
}
public void start() {
wasStarted = true;
this.scheduleAtFixedRate(task, delay, interval);
}
public void restart() {
if(!wasStarted) {
start();
}
else if(wasCancelled) {
wasCancelled = false;
this.task = getTask(totalTime);
start();
}
else{
this.restart = true;
}
}
public void stop() {
this.wasCancelled = true;
this.task.cancel();
}
// Call this when there's no further use for this timer
public void dispose(){
cancel();
purge();
}
private TimerTask getTask(final long totalTime) {
return new TimerTask() {
@Override
public void run() {
long timeLeft;
if (startTime < 0 || restart) {
startTime = scheduledExecutionTime();
timeLeft = totalTime;
restart = false;
} else {
timeLeft = totalTime - (scheduledExecutionTime() - startTime);
if (timeLeft <= 0) {
this.cancel();
startTime = -1;
onFinished();
return;
}
}
onTick(timeLeft);
}
};
}
public abstract void onTick(long timeLeft);
public abstract void onFinished();
}
用法示例如下:
this.countDown = new PreciseCountdown(totalTime, interval, delay) {
@Override
public void onTick(long timeLeft) {
// update..
// note that this runs on a different thread, so to update any GUI components you need to use Activity.runOnUiThread()
}
@Override
public void onFinished() {
onTick(0); // when the timer finishes onTick isn't called
// count down is finished
}
};
开始倒计时,只需调用countDown.start()。 countDown.stop()停止countDown,可以使用countDown.restart()重启。
希望这对任何人来说都有帮助。
答案 1 :(得分:0)
您可以使用变量来保留剩余时间,而不必使用millisUntilFinished
,而在每个onTick
中,将变量减去间隔即可。这样,remainingTime
总是准确的。
private class MyTimer(
countDownTime: Long,
interval: Long
) : CountDownTimer(countDownTime, interval) {
private var remainingTime = countDownTime
override fun onFinish() {
}
override fun onTick(millisUntilFinished: Long) {
// consume remainingTime here and then minus interval
remainingTime -= interval
}
}
答案 2 :(得分:0)
这是对 Noam Gal 发布的内容的扩展。我添加了额外的功能,您可以在其中暂停和恢复计时器。这对我来说非常有帮助。
public abstract class PreciseCountdownTimer extends Timer {
private long totalTime, interval, delay;
private TimerTask task;
private long startTime = -1;
private long timeLeft;
private boolean restart = false;
private boolean wasCancelled = false;
private boolean wasStarted = false;
public PreciseCountdownTimer(long totalTime, long interval) {
this(totalTime, interval, 0);
}
public PreciseCountdownTimer(long totalTime, long interval, long delay ) {
super("PreciseCountdownTimer", true);
this.delay = delay;
this.interval = interval;
this.totalTime = totalTime;
this.task = buildTask(totalTime);
}
private TimerTask buildTask(final long totalTime) {
return new TimerTask() {
@Override
public void run() {
if (startTime < 0 || restart) {
startTime = scheduledExecutionTime();
timeLeft = totalTime;
restart = false;
} else {
timeLeft = totalTime - (scheduledExecutionTime() - startTime);
if (timeLeft <= 0) {
this.cancel();
wasCancelled = true;
startTime = -1;
onFinished();
return;
}
}
onTick(timeLeft);
}
};
}
public void start() {
wasStarted = true;
this.scheduleAtFixedRate(task, delay, interval);
}
public void stop() {
this.wasCancelled = true;
this.task.cancel();
}
public void restart() {
if (!wasStarted) {
start();
} else if (wasCancelled) {
wasCancelled = false;
this.task = buildTask(totalTime);
start();
} else {
this.restart = true;
}
}
public void pause(){
wasCancelled = true;
this.task.cancel();
onPaused();
}
public void resume(){
wasCancelled = false;
this.task = buildTask(timeLeft);
this.startTime = - 1;
start();
onResumed();
}
// Call this when there's no further use for this timer
public void dispose() {
this.cancel();
this.purge();
}
public abstract void onTick(long timeLeft);
public abstract void onFinished();
public abstract void onPaused();
public abstract void onResumed();
}
用法示例几乎完全相同:
this.timer = new PreciseCountdownTimer(totalTime, interval, delay) {
@Override
public void onTick(long timeLeft) {
// note that this runs on a different thread, so to update any GUI components you need to use Activity.runOnUiThread()
}
@Override
public void onFinished() {
onTick(0); // when the timer finishes onTick isn't called
// count down is finished
}
@Override
public void onPaused() {
// runs after the timer has been paused
}
@Override
public void onResumed() {
// runs after the timer has been resumed
}
};
享受并玩得开心:D