How to correctly save instance state of Fragments in back stack?

后端 未结 6 1273
小鲜肉
小鲜肉 2020-11-21 11:57

I have found many instances of a similar question on SO but no answer unfortunately meets my requirements.

I have different layouts for portrait and landscape and I

6条回答
  •  囚心锁ツ
    2020-11-21 12:12

    I just want to give the solution that I came up with that handles all cases presented in this post that I derived from Vasek and devconsole. This solution also handles the special case when the phone is rotated more than once while fragments aren't visible.

    Here is were I store the bundle for later use since onCreate and onSaveInstanceState are the only calls that are made when the fragment isn't visible

    MyObject myObject;
    private Bundle savedState = null;
    private boolean createdStateInDestroyView;
    private static final String SAVED_BUNDLE_TAG = "saved_bundle";
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            savedState = savedInstanceState.getBundle(SAVED_BUNDLE_TAG);
        }
    }
    

    Since destroyView isn't called in the special rotation situation we can be certain that if it creates the state we should use it.

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        savedState = saveState();
        createdStateInDestroyView = true;
        myObject = null;
    }
    

    This part would be the same.

    private Bundle saveState() { 
        Bundle state = new Bundle();
        state.putSerializable(SAVED_BUNDLE_TAG, myObject);
        return state;
    }
    

    Now here is the tricky part. In my onActivityCreated method I instantiate the "myObject" variable but the rotation happens onActivity and onCreateView don't get called. Therefor, myObject will be null in this situation when the orientation rotates more than once. I get around this by reusing the same bundle that was saved in onCreate as the out going bundle.

        @Override
    public void onSaveInstanceState(Bundle outState) {
    
        if (myObject == null) {
            outState.putBundle(SAVED_BUNDLE_TAG, savedState);
        } else {
            outState.putBundle(SAVED_BUNDLE_TAG, createdStateInDestroyView ? savedState : saveState());
        }
        createdStateInDestroyView = false;
        super.onSaveInstanceState(outState);
    }
    

    Now wherever you want to restore the state just use the savedState bundle

      @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ...
        if(savedState != null) {
            myObject = (MyObject) savedState.getSerializable(SAVED_BUNDLE_TAG);
        }
        ...
    }
    

提交回复
热议问题