【发布时间】:2014-08-06 18:09:48
【问题描述】:
我试图建立某种顺序倒计时。意思是,我建立了一个“练习”队列,每个练习都包含一个特定的持续时间,即倒计时时间。在自定义倒计时课程中,我将这些练习从队列中弹出,并将持续时间用作倒计时。
我希望这些倒计时一个接一个地运行。为此,我基于抽象类CountDownTimer 的代码基础构建了一个Countdown 类。
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Locale;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.widget.Button;
import android.widget.TextView;
public class ExerciseMeCountDownTimer {
private static final int MSG_COUNTDOWN = 100;
private static final int MSG_FINISH = 99;
private ArrayDeque<Exercise> eq;
private long mMillisInFuture;
private int mCountdownInterval;
private String name;
private long mStopTimeInFuture;
CountdownHandler cHandler;
public ExerciseMeCountDownTimer(ArrayList<Exercise> elist,
Button startStopButton, TextView countdownText,
CountdownHandler cHandler) {
this.cHandler = cHandler;
eq = new ArrayDeque<Exercise>(elist);
this.start();
}
public final void cancel() {
mHandler.removeMessages(MSG);
}
private synchronized final ExerciseMeCountDownTimer start() {
if (!eq.isEmpty()) {
Exercise e = eq.pop();
this.mMillisInFuture = Long.parseLong(e.getDuration());
this.mCountdownInterval = 30;
this.name = e.getName();
} else {
onFinish();
return this;
}
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
public void onTick(long millisUntilFinished) {
Message msg = cHandler.obtainMessage(MSG_COUNTDOWN);
Bundle data = new Bundle();
String text = String.format(Locale.GERMANY, "%02d:%02d:%03d",
millisUntilFinished / 100000, millisUntilFinished / 1000,
millisUntilFinished % 1000);
data.putString("countdown", text);
msg.setData(data);
cHandler.sendMessage(msg);
}
public void onFinish() {
if (!eq.isEmpty()) {
this.start();
}
Message msg = cHandler.obtainMessage(MSG_FINISH);
Bundle data = new Bundle();
String text = String.format(Locale.GERMANY, "00:00:000");
data.putString("finish", text);
msg.setData(data);
cHandler.sendMessage(msg);
}
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
final long millisLeft = mStopTimeInFuture
- SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to
// execute
long delay = lastTickStart + mCountdownInterval
- SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval
// to
// complete, skip to next interval
while (delay < 0)
delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
};
@Override
public String toString() {
return this.name;
}
}
重要的部分是sendMessage 部分,我将倒计时剩余的时间发送到我的MainActivity 的handler,然后应该更新一个文本视图。
import android.os.Handler;
import android.os.Message;
class CountdownHandler extends Handler {
private static final int MSG_COUNTDOWN = 100;
private static final int MSG_FINISH = 99;
private MainActivity mActivity;
CountdownHandler(MainActivity activity) {
this.mActivity = activity;
}
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_COUNTDOWN) {
String text = msg.getData().getString("countdown");
this.mActivity.sayLog(text);
}
if (msg.what == MSG_FINISH) {
String text = msg.getData().getString("finish");
this.mActivity.sayLog(text);
}
}
最后更新MainActivty中的textView
public void sayLog(String text) {
countdown.setText(text);
}
ExerciseMeCountDownTimer 被调用
new ExerciseMeCountDownTimer(elist, cHandler);
在一些 onClick() 上。
问题是,有时(实际上大部分时间)textView 没有正确更新。它会在 00:05:211 等随机时间停止更新。 有人介意告诉我为什么会一直这样吗?也许还添加一个解决方案或至少一些我应该阅读以理解问题的文献(也许指出一些部分)?我也对替代方法持开放态度,因为我对 android 中的这个“处理程序”、“线程”东西不熟悉。
编辑
- 文本视图正在更新,但文本视图是可点击的。每当我单击 textview 时,它就会停止更新!
- 正如接受的答案所示,我决定使用直接方法在
onTick()方法中更新适当的文本视图。
【问题讨论】:
-
您知道
onTick()在UI线程上运行,对吧?如果没有Handler,您是否有理由无法在那里更新它 -
是的,我知道。我已经尝试在那里更新 textview,但 android 并不喜欢那样,抱怨视图层次结构只能由创建 textview 的活动修改。但是从那以后我改变了很多,让我再试一次。
-
确保你是从主线程调用
ExerciseMeCountDownTimer -
好吧,你说得对,我实际上可以使用
onTick()... 奇怪,这在以前不起作用。我正在将 textView 传递给ExerciseMeCountDownTimer(elist, countdownTextview, cHandler)问题,textView 冻结仍然存在。当我在文本视图上单击(鼠标,在模拟器中)时,就会发生这种情况...... -
嗯,我明白这一点,但如果它正在被其他东西更新,你为什么希望它是可点击的。使其不可点击
标签: java android multithreading countdowntimer android-handler