Android 4.2: back stack behaviour with nested fragments

后端 未结 17 1803
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-29 16:01

With Android 4.2, the support library got support for nested fragments see here. I\'ve played around with it and found an interesting behaviour / bug regarding back stack an

17条回答
  •  一生所求
    2020-11-29 16:41

    More than 5 years and this issue is still relevant. If you do not want to use the fragmentManager.getFragments() due to its restriction. Extend and use the below classes:

    NestedFragmentActivity.java

    abstract public class NestedFragmentActivity extends AppCompatActivity {
    
        private final Stack mActiveFragmentIdStack = new Stack<>();
        private final Stack mActiveFragmentTagStack = new Stack<>();
    
        @Override
        public void onBackPressed() {
            if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {
    
                // Find by id
                int lastFragmentId = mActiveFragmentIdStack.lastElement();
                NestedFragment nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentById(lastFragmentId);
    
                // If cannot find by id, find by tag
                if (nestedFragment == null) {
                    String lastFragmentTag = mActiveFragmentTagStack.lastElement();
                    nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentByTag(lastFragmentTag);
                }
    
                if (nestedFragment != null) {
                    nestedFragment.onBackPressed();
                }
    
                // If cannot find by tag, then simply pop
                mActiveFragmentTagStack.pop();
                mActiveFragmentIdStack.pop();
    
            } else {
                super.onBackPressed();
            }
        }
    
        public void addToBackStack(int fragmentId, String fragmentTag) {
            mActiveFragmentIdStack.add(fragmentId);
            mActiveFragmentTagStack.add(fragmentTag);
        }
    }
    

    NestedFragment.java

    abstract public class NestedFragment extends Fragment {
    
        private final Stack mActiveFragmentIdStack = new Stack<>();
        private final Stack mActiveFragmentTagStack = new Stack<>();
    
        private NestedFragmentActivity mParentActivity;
        private NestedFragment mParentFragment;
    
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
    
            if (getParentFragment() == null) {
                try {
                    mParentActivity = (NestedFragmentActivity) context;
                } catch (ClassCastException e) {
                    throw new ClassCastException(context.toString()
                            + " must implement " + NestedFragmentActivity.class.getName());
                }
            } else {
                try {
                    mParentFragment = (NestedFragment) getParentFragment();
                } catch (ClassCastException e) {
                    throw new ClassCastException(getParentFragment().getClass().toString()
                            + " must implement " + NestedFragment.class.getName());
                }
            }
        }
    
        public void onBackPressed() {
    
            if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {
    
                // Find by id
                int lastFragmentId = mActiveFragmentIdStack.lastElement();
                NestedFragment nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentById(lastFragmentId);
    
                // If cannot find by id, find by tag
                if (nestedFragment == null) {
                    String lastFragmentTag = mActiveFragmentTagStack.lastElement();
                    nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentByTag(lastFragmentTag);
                }
    
                if (nestedFragment != null) {
                    nestedFragment.onBackPressed();
                }
    
                // If cannot find by tag, then simply pop
                mActiveFragmentIdStack.pop();
                mActiveFragmentTagStack.pop();
    
            } else {
                getChildFragmentManager().popBackStack();
            }
        }
    
        private void addToBackStack(int fragmentId, String fragmentTag) {
            mActiveFragmentIdStack.add(fragmentId);
            mActiveFragmentTagStack.add(fragmentTag);
        }
    
        public void addToParentBackStack() {
            if (mParentFragment != null) {
                mParentFragment.addToBackStack(getId(), getTag());
            } else if (mParentActivity != null) {
                mParentActivity.addToBackStack(getId(), getTag());
            }
        }
    }
    

    Explanation:

    Each activity and fragment extended from the above classes manages their own back stack for each of their children, and children's children, and so on. The backstack is simply a record of "active fragment" tags/ids. So the caveat is to always provide a tag and/or id for your fragment.

    When adding to the backstack in a childFragmentManager, you will need to also call "addToParentBackStack()". This ensures that the fragment's tag/id is added in the parent fragment/activity for later pops.

    Example:

        getChildFragmentManager().beginTransaction().replace(
                R.id.fragment,
                fragment,
                fragment.getTag()
        ).addToBackStack(null).commit();
        addToParentBackStack();
    

提交回复
热议问题