【问题标题】:sequential countdowntimer with handler wont update textView correctly带有处理程序的顺序倒数计时器不会正确更新 textView
【发布时间】: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 部分,我将倒计时剩余的时间发送到我的MainActivityhandler,然后应该更新一个文本视图。

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


【解决方案1】:

在这种情况下使用Handler 会使事情变得过于复杂。

CountDownTimers onTick()onFinish() 都在 UI Thread 上运行,因此从任一方法更新 TextViews 和其他 Views 只需将 View 的引用传递给即可轻松完成类的构造函数,就像你已经在做的那样。然后你只需在所需的方法中更新它。

// could create a member variable for the TextView with your other member variables
...
mTV;

然后在你的构造函数中分配它 // 删除了对 Handler 的引用——你已经在这里引用了 TextView 公共ExerciseMeCountDownTimer(ArrayList elist, 按钮 startStopButton, TextView countdownText) { mTV = 倒计时文字;

然后在需要的任何方法中更新

public void onTick(long millisUntilFinished) {

    String text = String.format(Locale.GERMANY, "%02d:%02d:%03d",
            millisUntilFinished / 100000, millisUntilFinished / 1000,
            millisUntilFinished % 1000);
    mTV.setText(text);  // set the text here


}

public void onFinish() {
    if (!eq.isEmpty()) {
        this.start();
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-13
    相关资源
    最近更新 更多