Real approach for Avoiding Re-creation of Fragment after Screen Rotate (Official Fragment Developer Guide as example)

元气小坏坏 提交于 2019-12-01 01:56:21

I've had some success with the following method.

This does the right thing based on the fragment's state.

public void activate(FragmentTransaction ft, Fragment f, String tag, int resId) {
    boolean changed =   resId != f.getId();

    if (changed && (f.isAdded() || f.isDetached())) {
        ft.remove(f);
        ft.add(resId, f, tag);
        return;
    }

    // Currently in a detached mode
    if (f.isDetached()) {
        ft.attach(f);
        return;
    }

    // Not in fragment manager add
    if (!f.isAdded() && ! f.isDetached()) {
        ft.add(resId, f, tag);
        return;
    }
}

This handles the specific memoized fragment.

private enum FragmentOp {
    ADD
    ,DETACH
    ,REMOVE
    ;
}

private void ensureFragmentState(FragmentOp op) {
    if (null == iChild) {
        iChild = (FragmentChild) iFM.findFragmentById(R.id.fragment_phonenumber_details);
    }

    FragmentTransaction ft = iFM.beginTransaction();
    switch(op) {
    case ADD:
        if (null == iChild) {
            iChild = new FragmentChild();
        }
        activate(ft, iChild, null, R.id.fragment_phonenumber_details);
        break;
    case DETACH:
        if (null != iChild) {
            iChild.deactivate(ft);
        }
        break;
    case REMOVE:
        if (null != iChild) {
            iChild.remove(ft);
        }
        break;
    }
    // Only if something shows up did we do anything!
    if (null != iChild) {
        ft.commit();
    }
}

And then in the lifecycle methods:

@Override public void onResume() {
    super.onResume();
    if (iDualPane) {
        ensureFragmentState(FragmentOp.ADD);
    }
}

@Override public void onPause() {
    super.onPause();

    recordState();   // Grab what I need from the child fragment

    ensureFragmentState(FragmentOp.DETACH);
}
midnite

Thanks @RogerGarzonNieto very much for spotting the method that disable auto Activity re-creation upon orientation changes. It is very useful. I am sure i will have to use it in some situations in the further.

For just avoiding Fragments re-creation upon screen rotates, i have found a simpler method that we can still allow the Activity re-creates as usual.

In onSaveInstanceState():

@Override
protected void onSaveInstanceState(Bundle outState) {
    if (isPortrait2Landscape()) {
        remove_fragments();
    }
    super.onSaveInstanceState(outState);
}

private boolean isPortrait2Landscape() {
    return isDevicePortrait() && (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
}

and the isDevicePortrait() would be like:

private boolean isDevicePortrait() {
    return (findViewById(R.id.A_View_Only_In_Portrait) != null);
}

*Notice that we cannot use getResources().getConfiguration().orientation to determine if the device is currently literally Portrait. It is because the Resources object is changed RIGHT AFTER the screen rotates - EVEN BEFORE onSaveInstanceState() is called!!

If you do not want to use findViewById() to test orientation (for any reasons, and it's not so neat afterall), keep a global variable private int current_orientation; and initialise it by current_orientation = getResources().getConfiguration().orientation; in onCreate(). This seems neater. But we should be aware not to change it anywhere during the Activity lifecycle.

*Be sure we remove_fragments() before super.onSaveInstanceState().

(Because in my case, i remove the Fragments from the Layout, and from the Activity. If it is after super.onSaveInstanceState(), the Layout will already be saved into the Bundle. Then the Fragments will also be re-created after the Activity re-creates. ###)

### I have proved this phenomenon. But the reason of What to determine a Fragment restore upon Activity re-create? is just by my guess. If you have any ideas about it, please answer my another question. Thanks!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!