mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-03-06 17:34:59 +08:00
Fix stack nesting on Android. (#234)
This change fixes two issues related to stack nesting on Android. First one being the fact that root and nested fragments were relying on the same fragment manager as opposed to using nested fragment manager structure (via getChildFragmentManager). This resulted in an unprodictable behavior when displaying the top screen. This commit changes this behavior by correctly returning child fragment manager or root fragment manager depending on the containers hierarchy. Second issue was related to back stack handling and resulted in always the root fragment manager interacting. Instead we want the bottommost active stack to handle back presses. This has been addressed by adding `setPrimaryNavigationFragment` when creating back stack entries.
This commit is contained in:
committed by
GitHub
parent
124e8acb2d
commit
31192250e1
@@ -23,6 +23,7 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
protected final ArrayList<T> mScreenFragments = new ArrayList<>();
|
||||
private final Set<ScreenFragment> mActiveScreenFragments = new HashSet<>();
|
||||
|
||||
private @Nullable FragmentManager mFragmentManager;
|
||||
private @Nullable FragmentTransaction mCurrentTransaction;
|
||||
private boolean mNeedUpdate;
|
||||
private boolean mIsAttached;
|
||||
@@ -108,11 +109,18 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
return mScreenFragments.get(index).getScreen();
|
||||
}
|
||||
|
||||
protected final FragmentActivity findRootFragmentActivity() {
|
||||
private FragmentManager findFragmentManager() {
|
||||
ViewParent parent = this;
|
||||
while (!(parent instanceof ReactRootView) && parent.getParent() != null) {
|
||||
// We traverse view hierarchy up until we find screen parent or a root view
|
||||
while (!(parent instanceof ReactRootView || parent instanceof Screen) && parent.getParent() != null) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
// If parent is of type Screen it means we are inside a nested fragment structure.
|
||||
// Otherwise we expect to connect directly with root view and get root fragment manager
|
||||
if (parent instanceof Screen) {
|
||||
return ((Screen) parent).getFragment().getChildFragmentManager();
|
||||
}
|
||||
|
||||
// we expect top level view to be of type ReactRootView, this isn't really necessary but in order
|
||||
// to find root view we test if parent is null. This could potentially happen also when the view
|
||||
// is detached from the hierarchy and that test would not correctly indicate the root view. So
|
||||
@@ -131,11 +139,14 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
throw new IllegalStateException(
|
||||
"In order to use RNScreens components your app's activity need to extend ReactFragmentActivity or ReactCompatActivity");
|
||||
}
|
||||
return (FragmentActivity) context;
|
||||
return ((FragmentActivity) context).getSupportFragmentManager();
|
||||
}
|
||||
|
||||
protected FragmentManager getFragmentManager() {
|
||||
return findRootFragmentActivity().getSupportFragmentManager();
|
||||
protected final FragmentManager getFragmentManager() {
|
||||
if (mFragmentManager == null) {
|
||||
mFragmentManager = findFragmentManager();
|
||||
}
|
||||
return mFragmentManager;
|
||||
}
|
||||
|
||||
protected FragmentTransaction getOrCreateTransaction() {
|
||||
|
||||
@@ -61,8 +61,16 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
getFragmentManager().removeOnBackStackChangedListener(mBackStackListener);
|
||||
getFragmentManager().popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
FragmentManager fm = getFragmentManager();
|
||||
if (!fm.isStateSaved()) {
|
||||
// state save means that the container where fragment manager was installed has been unmounted.
|
||||
// This could happen as a result of dismissing nested stack. In such a case we don't need to
|
||||
// reset back stack as it'd result in a crash caused by the fact the fragment manager is no
|
||||
// longer attached.
|
||||
fm.removeOnBackStackChangedListener(mBackStackListener);
|
||||
fm.popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,7 +96,11 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
getOrCreateTransaction().remove(screen);
|
||||
}
|
||||
}
|
||||
ScreenStackFragment newTop = null;
|
||||
|
||||
// When going back from a nested stack with a single screen on it, we may hit an edge case
|
||||
// when all screens are dismissed and no screen is to be displayed on top. We need to gracefully
|
||||
// handle the case of newTop being NULL, which happens in several places below
|
||||
ScreenStackFragment newTop = null; // newTop is nullable, see the above comment ^
|
||||
ScreenStackFragment belowTop = null; // this is only set if newTop has TRANSPARENT_MODAL presentation mode
|
||||
|
||||
for (int i = mScreenFragments.size() - 1; i >= 0; i--) {
|
||||
@@ -106,7 +118,6 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (ScreenStackFragment screen : mScreenFragments) {
|
||||
// add all new views that weren't on stack before
|
||||
if (!mStack.contains(screen) && !mDismissed.contains(screen)) {
|
||||
@@ -127,7 +138,10 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
}
|
||||
});
|
||||
}
|
||||
getOrCreateTransaction().show(newTop);
|
||||
|
||||
if (newTop != null) {
|
||||
getOrCreateTransaction().show(newTop);
|
||||
}
|
||||
|
||||
if (!mStack.contains(newTop)) {
|
||||
// if new top screen wasn't on stack we do "open animation" so long it is not the very first screen on stack
|
||||
@@ -165,7 +179,9 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
|
||||
tryCommitTransaction();
|
||||
|
||||
setupBackHandlerIfNeeded(mTopScreen);
|
||||
if (mTopScreen != null) {
|
||||
setupBackHandlerIfNeeded(mTopScreen);
|
||||
}
|
||||
|
||||
for (ScreenStackFragment screen : mStack) {
|
||||
screen.onStackUpdate();
|
||||
@@ -208,6 +224,7 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
.hide(topScreen)
|
||||
.show(topScreen)
|
||||
.addToBackStack(BACK_STACK_TAG)
|
||||
.setPrimaryNavigationFragment(topScreen)
|
||||
.commitAllowingStateLoss();
|
||||
getFragmentManager().addOnBackStackChangedListener(mBackStackListener);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user