Android: How to restore the state of a stopped chr

2019-02-27 14:54发布

问题:

I'm doing this way to save the state and restore it after rotation when the chronometer is running. Android_Chronometer pause

When I stop the timer at Xsec and then after Y seconds I change the orientation the chronometer marks X+Y seconds. I'd like to leave the time of a stopped chronometer as it was before rotation, no matter how much time has passed. How should I do?

回答1:

I had a similar question about the Chronometer class and how to survive orientation changes. While there are several useful posts and examples, none that I found addressed the total question.

This was a helpful post here Android_Chronometer pause, which helped with demonstrating the need to save the elapsedTime in order to resume timing.

However, this did not discuss how to make the Chronometer survive Android life cycle orientation changes. What you do with the elapsed time is slightly different between when the timer is running vs. when it is paused.

Here is that I did to put it all together - pausing, resuming, resetting, in a nice class, and surviving orientation:

 public class ChronometerWithPause extends Chronometer {
    private long timeWhenStopped = 0;
    private boolean isRunning = false;

    private final String getTimeKey() {
        return "KEY_TIMER_TIME" + getId();
    }
    private final String getIsRunningKey() {
        return "KEY_TIMER_RUNNING" + getId();
    }

    public ChronometerWithPause(Context context) {
        super(context);
    }

    public ChronometerWithPause(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ChronometerWithPause(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void start() {
        setBase(SystemClock.elapsedRealtime() - timeWhenStopped);
        isRunning = true;
        super.start();
    }

    @Override
    public void stop() {
        isRunning = false;
        timeWhenStopped = SystemClock.elapsedRealtime() - getBase();
        super.stop();
    }

    public void reset() {
        stop();
        isRunning = false;
        setBase(SystemClock.elapsedRealtime());
        timeWhenStopped = 0;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public long getCurrentTime() {
        return timeWhenStopped;
    }

    public void setCurrentTime(long time) {
        timeWhenStopped = time;
        setBase(SystemClock.elapsedRealtime() - timeWhenStopped);
    }

    public void saveInstanceState(Bundle outState) {
        if (isRunning) {
            timeWhenStopped = SystemClock.elapsedRealtime() - getBase();
        }
        outState.putLong(getTimeKey(), getCurrentTime());
        outState.putBoolean(getIsRunningKey(), isRunning());
    }

    public void restoreInstanceState(Bundle inState) {
        isRunning = inState.getBoolean(getIsRunningKey());
        setCurrentTime(inState.getLong(getTimeKey()));
        timeWhenStopped = SystemClock.elapsedRealtime() - getBase();
        if (isRunning) {
            super.start();
        }
    }
}

Note that you can use this in onSaveInstanceState() and onCreate() like this:

protected void onSaveInstanceState(Bundle outState) {
           ...
           timer.saveInstanceState(outState);
           ...

and then in onCreate you can restore the timer function with:

if (savedInstanceState != null) {
             // . . .
                timer.restoreInstanceState(savedInstanceState);
             // . . .
}


回答2:

If you are into slightly hack-y solutions, you can also do the following: Save the value of the ChronometerInstance.getText() into the Bundle and then re-set it with ChronometerInstance.setText(). It's not very friendly, but it works and does not involve calculating offsets and storing long values until you actually need them.

It completely messes with the ability to re-start the Chronometer, too, but since resuming the Chronometer behaves unexpectedly on resume anyway (continues with the same base, so it'll jump to whatever time it would've had without being stopped), that isn't too big of a loss, in my opinion.

Still, if you will ever need the old base time, go with the solution from the comments of the other solution: Save the value of

long diff = SystemClock.elapsedRealtime() - chronometerInstance.getBase();

Then, when you want to restore it, set

ChronometerInstance.setBase(SystemClick.elapsedRealtime() - diff);

If it does not display the value afterwards, you will probably have to force a re-draw. The easiest way is probably to just do a

ChronometerInstance.start();
ChronometerInstance.stop();

(as I have no idea if .invalidate() will do the trick)



回答3:

Thanks for the clarification in the comments.

To solve your problem you can easily save in

onSaveInstanceState()

a variable related to the current status of your cronometer and then retrieve it in onCreate or in

onRestoreInstanceState()

(The second method is better) and set your cronometer status accordingly.

EDIT: To retrieve the elapsed time from the chronometer call:

SystemClock.elapsedRealtime() - chronometerInstance.getBase();