Неверное начальное значение хронометра

Я пишу простое приложение для шахматных часов на основе android.widget.Chronometer. Я использую это сsetCountDown(true)флаг и вся логика работает хорошо. Каждый хронометр создается в отдельномFragment. Но иногда возникают проблемы с начальным значением хронометра. Например, я устанавливаю начальное значение на 20 секунд, но на самом деле у меня есть следующее: И при перезапуске приложения это различение может быть на случайных часах. Я устанавливаю начальное значение таким образом в моем классе, расширяющемandroid.widget.Chronometer, а mTimeLimit каждый раз равно 20000:

private void setTimeLimit() {
    mStartTime = SystemClock.elapsedRealtime() + mTimeLimit;
    setBase(mStartTime);
}

Я думаю, что проблема может быть во времени создания фрагментов или так далее. Кто знает, в чем я ошибаюсь?

2 ответа

Решение

Я нахожу одно решение, добавляя 100 миллисекунд к начальному значению:

mStartTime = SystemClock.elapsedRealtime() + mTimeLimit + 100;

Кажется, что это решает проблему, но я думаю, что это не лучшее решение, может, кто-то знает подход получше.

Проблема возникает потому, что класс Chronometer вычисляет секунды, усекая деление миллисекунд, тогда как он должен округлять результат.

Это не проблема при прямом счете, но проблема возникает при обратном счете.

Я открыл проблему в системе отслеживания ошибок, чтобы запросить изменение расчета в классе Chronometer:

https://issuetracker.google.com/issues/297733248

Добавление произвольного количества миллисекунд к базе хронометра может работать на некоторых устройствах, но на медленных устройствах проблема все равно может возникнуть.

Мое решение (пока проблема решается в системе отслеживания проблем) заключалось в создании собственного класса MyChronometer на основе класса Chronometer платформы, но с правильным расчетом (см. расчет в методе updateText):

      @SuppressLint("AppCompatCustomView")
@RemoteView
public class MyChronometer extends TextView {

    public interface OnChronometerTickListener {
        void onChronometerTick(MyChronometer chronometer);
    }

    private long mBase;
    private boolean mVisible;
    private boolean mStarted;
    private boolean mRunning;
    private final StringBuilder mRecycle = new StringBuilder(8);
    private OnChronometerTickListener mOnChronometerTickListener;
    private boolean mCountDown;

    public MyChronometer(Context context) {
        this(context, null, 0);
    }

    public MyChronometer(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyChronometer(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

    public MyChronometer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        mBase = SystemClock.elapsedRealtime();
        updateText(mBase);
    }

    public void setCountDown(boolean countDown) {
        mCountDown = countDown;
        updateText(SystemClock.elapsedRealtime());
    }

    public void setBase(long base) {
        mBase = base;
        dispatchChronometerTick();
        updateText(SystemClock.elapsedRealtime());
    }

    public long getBase() {
        return mBase;
    }

    public void setOnChronometerTickListener(OnChronometerTickListener listener) {
        mOnChronometerTickListener = listener;
    }

    public OnChronometerTickListener getOnChronometerTickListener() {
        return mOnChronometerTickListener;
    }

    public void start() {
        mStarted = true;
        updateRunning();
    }

    public void stop() {
        mStarted = false;
        updateRunning();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mVisible = false;
        updateRunning();
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mVisible = visibility == VISIBLE;
        updateRunning();
    }

    @Override
    protected void onVisibilityChanged(View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        updateRunning();
    }

    private synchronized void updateText(long now) {

        long diffMillis = mCountDown ? mBase - now : now - mBase;
        int seconds = Math.round(diffMillis / 1000f);

        String text;

        if (seconds < 0) {
            text = "-" + DateUtils.formatElapsedTime(mRecycle, -seconds);
        } else {
            text = DateUtils.formatElapsedTime(mRecycle, seconds);
        }

        setText(text);

    }

    private void updateRunning() {

        boolean running = mVisible && mStarted && isShown();

        if (running != mRunning) {

            if (running) {
                updateText(SystemClock.elapsedRealtime());
                dispatchChronometerTick();
                postDelayed(mTickRunnable, 1000);
            } else {
                removeCallbacks(mTickRunnable);
            }

            mRunning = running;

        }

    }

    private final Runnable mTickRunnable = new Runnable() {
        @Override
        public void run() {
            if (mRunning) {
                updateText(SystemClock.elapsedRealtime());
                dispatchChronometerTick();
                postDelayed(mTickRunnable, 1000);
            }
        }
    };

    void dispatchChronometerTick() {
        if (mOnChronometerTickListener != null) {
            mOnChronometerTickListener.onChronometerTick(this);
        }
    }

    @Override
    public CharSequence getAccessibilityClassName() {
        return MyChronometer.class.getName();
    }

}
Другие вопросы по тегам