带处理程序的顺序倒计时器不会正确更新textView

时间:2014-06-16 20:36:06

标签: java android multithreading countdowntimer android-handler

我正在尝试构建某种顺序倒计时。意思是,我建立了一个“练习”队列,每个练习都包含一个特定的持续时间,即倒计时时间。在自定义的Countdown类中,我将这些练习从队列中弹出并将持续时间用作倒计时。 我希望这些倒计时一个接一个地运行。为此,我根据抽象类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部分,我将倒计时剩余的时间发送到我handler的{​​{1}},然后应该更新文本视图。

MainActivity

最后更新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); } }

中的textView
MainActivty

调用ExerciseMeCountDownTimer
public void sayLog(String text) {
    countdown.setText(text);
}

on onClick()。

问题是,有时(实际上大部分时间)textView没有正确更新。它会在00:05:211等随机时间停止更新 有人会介意告诉我为什么会这样吗?也许还要添加一个解决方案或至少一些文献(可能指出一些部分)我应该阅读以理解这个问题?我也是替代方法,因为我是这个“处理程序”的新手,是android中的“线程”。

修改

  • textview正在更新,但textview是可点击的。每当我点击textview时它就会停止更新!
  • 如接受的答案所示,我决定使用direkt方法更新new ExerciseMeCountDownTimer(elist, cHandler); 方法中的相应textview。

1 个答案:

答案 0 :(得分:1)

使用Handler和这种情况下的事情使它变得过于复杂。

CountDownTimer onTick()onFinish()都在UI Thread上运行,因此可以从任一方法更新TextViews和其他View很容易就是通过将View的引用传递给类的构造函数,就像您已经在做的那样。然后,您只需在所需的方法中更新它。

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

然后在你的构造函数中分配它     //删除了对Handler的引用 - 您已经在这里引用了TextView     public ExerciseMeCountDownTimer(ArrayList elist,             按钮startStopButton,TextView countdownText){          mTV = countdownText;

然后以任何需要的方法更新

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();
    }