Szymon
Szymon

Reputation: 31

Custom view's state is saved, but never restored

I have used the advices from How to prevent custom views from losing state across screen orientation changes but even when my View saves it's state properly, onRestoreInstanceState is never called. Activity's restoring method is called to make things stranger.

import java.text.DecimalFormat;
import java.text.NumberFormat;
import com.szyk.myheart.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Scroller;

public class NumberScroller extends View implements OnTouchListener{

    private final float VIEW_FILL_PERCENTAGE = 80;
    private final int MIN_VELOCITY_Y = 100;
    private final int VELOCITY_UNIT = 1000;
    private final String TAG = "NumberScroller";
    private final int ALIGNING_DURATION = 1000;

    private boolean isCustomizing = true;
    private boolean isDragged = false;
    private boolean isNewVelocityTracker = false;

    private Scroller scroller;
    private VelocityTracker velocityTracker;
    private float lastY;
    private int absoleteY;
    private float velocityY;
    private float ascent;
    private Integer maxScrollerNumber;
    private Integer minScrollerNumber;

    private Paint paintNumber;
    private Paint paintNumberActivated;
    private Paint paintBorder;
    private float textOffsetX;
    private Drawable foregroundBorder;
    private Drawable foregroundAlpha;
    private NumberFormat numberFormatter;


    /**
     * Initializes attributes and variables.
     * @param attrs - AttributeSet for Number Scroller
     * @param context in which scroller exists.
     */
    public NumberScroller(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG, "Creating NumberScroller.");
        initAttrs(attrs, context);
        scroller = new Scroller(getContext());
        setOnTouchListener(this);

        foregroundAlpha = getResources().getDrawable(R.drawable.foreground_alpha);

        numberFormatter = new DecimalFormat();
        numberFormatter.setMinimumIntegerDigits(maxScrollerNumber.toString()
                .length());
        numberFormatter.setMinimumFractionDigits(0);

        setSaveEnabled(true);

    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
    }
    /**
     * (non-Javadoc)
     * @see android.view.View#onMeasure(int, int)
     */

/**
     * Initializes attributes from xml description.
     * @param attrs - AttributeSet for Number Scroller
     * @param context in which scroller exists.
     */
    private void initAttrs(AttributeSet attrs, Context context) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.NumberScroller);
        maxScrollerNumber = typedArray.getInteger(
                R.styleable.NumberScroller_maximum, 99);
        minScrollerNumber = typedArray.getInteger(
                R.styleable.NumberScroller_minimum, 0);
        if (maxScrollerNumber < minScrollerNumber) {
            int tmp = minScrollerNumber;
            minScrollerNumber = maxScrollerNumber;
            maxScrollerNumber = tmp;
        }
        typedArray.recycle();
    }

V1:

@Override
  public Parcelable onSaveInstanceState() {
    Log.i(TAG,"SAve");
    //begin boilerplate code that allows parent classes to save state
    Parcelable superState = super.onSaveInstanceState();

    SavedState ss = new SavedState(superState);
    //end

    ss.stateToSave = absoleteY;

    return ss;
  }

  @Override
  public void onRestoreInstanceState(Parcelable state) {
      Log.i(TAG,"REstore");
    //begin boilerplate code so parent classes can restore state
    if(!(state instanceof SavedState)) {
      super.onRestoreInstanceState(state);
      return;
    }
    SavedState ss = (SavedState)state;
    super.onRestoreInstanceState(ss.getSuperState());
    //end

    absoleteY = ss.stateToSave;
  }

  public static class SavedState extends BaseSavedState {
   int stateToSave;

    SavedState(Parcelable superState) {
      super(superState);
    }

    private SavedState(Parcel in) {
      super(in);
      this.stateToSave = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
      super.writeToParcel(out, flags);
      out.writeInt(this.stateToSave);
    }
    //required field that makes Parcelables from a Parcel
    public static final Parcelable.Creator<SavedState> CREATOR =
        new Parcelable.Creator<SavedState>() {
          public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
          }
          public SavedState[] newArray(int size) {
            return new SavedState[size];
          }
    };
  }

V2:

@Override
protected Parcelable onSaveInstanceState() {
    Log.i(TAG, "Saving state.");
    Bundle bundle = new Bundle();
    bundle.putParcelable("state", super.onSaveInstanceState());
    bundle.putInt("absoleteY", absoleteY);
    return bundle;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    Log.i(TAG, "Restoring state");

    if(state instanceof Bundle){

        Bundle bundle = (Bundle) state;
        absoleteY = bundle.getInt("absoleteY");
        super.onRestoreInstanceState(bundle.getParcelable("state"));
        return;
    }

    super.onRestoreInstanceState(state); 
}

In logs I never see "Restoring state".

It appears that if I am not using my custom view inside android.support.v4.view.ViewPager, then onRestoreInstanceState is called. Does anyone know the reason for that and do I have to call save/restore from within the activity?

Upvotes: 3

Views: 1820

Answers (1)

Miguel Beltran
Miguel Beltran

Reputation: 2202

This question is very old but I stumbled upon it while Googling a solution, then after I figured it out.

The problem may be that the custom views are created programatically inside the root Activity's onCreate method.

The solution is to call to onSaveInstanceState and onRestoreInstanceState from the root Activity containing the ViewPager and store the custom view's state programatically as well.

Inside your root Activity implement the following:

Save the state of your custom view

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putParcelable("STATE_COLLECTION", collectionFrameLayout.onSaveInstanceState());
    super.onSaveInstanceState(outState);
}

And inside the onCreate() restore it after you create your custom views.

 if (savedInstanceState != null) {
     Parcelable state = savedInstanceState.getParcelable("STATE_COLLECTION");
     if (state != null) {
         collectionFrameLayout.onRestoreInstanceState(state);
     }
 }

Upvotes: 1

Related Questions