Compare commits

..

8 Commits

Author SHA1 Message Date
Krzysztof Magiera
da9426b4b9 Bump version -> 2.0.0-alpha.14 2019-11-29 11:20:15 +01:00
Krzysztof Magiera
4169fad8b3 Support headerBackTitleVisible and cadrStyle options. (#239) 2019-11-29 11:19:23 +01:00
Krzysztof Magiera
4f8efd2873 Revert "Fix navbar window fitting on Android. (#235)"
This reverts commit 0927e03687.
2019-11-27 22:17:52 +01:00
Krzysztof Magiera
cbc86bb6d8 Avoid changing back stack when fragment manager is not resumed. (#237)
This change fixes the problem when there are changes being made to the stack while the hosting activity is in paused state (e.g. an activity-based dialog from google play services). In such a case it is not allowed to do any changes which can affect fragment manager's state (e.g. updating backstack). For other changes we use `commitAllowingStatLoss` to indicate we are not interested in fragment manager handling their restoration and hence we can perform most operations. Unfortunately installing back handler is not allowed without state change and we need to wait until the fragment host is resumed before we install it.
2019-11-27 22:00:16 +01:00
Krzysztof Magiera
0927e03687 Fix navbar window fitting on Android. (#235)
This change fixes the issue of handling transparent status bar. In such a case the navbar should became slightly bigger in order to fill the status bar space that now belongs to the window. In order for that to happen we use `setFitsSystemWindows` on main screen layout, on app bar layout and on toolbar (the toolbar is the view that gets bigger in the end).

Note that this currently only work if we use Android's native mechanism for changingthe status bar behavior and not using `StatusBar` module from RN. It is because `StausBar` module actually strips top insets from the event that gets delivered to the views. For the time being you can try the following to change status bar:
```
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
```

Note that we are also disabling app bar's scroll behavior. We wasn't utilising this yet, and it cause some weird errors on hot reload because scrolling behavior actually sets `firsSystemWindows` flag on the screen view which ended up consuming insets before appbar would get them.
2019-11-27 12:35:26 +01:00
Janic Duplessis
efaf0cd125 Fix titleFontSize on Android (#227) 2019-11-26 19:08:34 +01:00
Krzysztof Magiera
21e6a9732a Bump version -> 2.0.0-alpha.13 2019-11-26 15:33:58 +01:00
Krzysztof Magiera
31192250e1 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.
2019-11-26 15:32:41 +01:00
6 changed files with 66 additions and 22 deletions

View File

@@ -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() {

View File

@@ -2,6 +2,7 @@ package com.swmansion.rnscreens;
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
@@ -30,6 +31,15 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
}
};
private final FragmentManager.FragmentLifecycleCallbacks mLifecycleCallbacks = new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentResumed(FragmentManager fm, Fragment f) {
if (mTopScreen == f) {
setupBackHandlerIfNeeded(mTopScreen);
}
}
};
public ScreenStack(Context context) {
super(context);
}
@@ -61,16 +71,23 @@ 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();
fm.removeOnBackStackChangedListener(mBackStackListener);
getFragmentManager().unregisterFragmentLifecycleCallbacks(mLifecycleCallbacks);
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.popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mTopScreen != null) {
setupBackHandlerIfNeeded(mTopScreen);
}
getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks, false);
}
@Override
@@ -88,7 +105,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 +127,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 +147,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 +188,9 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
tryCommitTransaction();
setupBackHandlerIfNeeded(mTopScreen);
if (mTopScreen != null) {
setupBackHandlerIfNeeded(mTopScreen);
}
for (ScreenStackFragment screen : mStack) {
screen.onStackUpdate();
@@ -192,6 +217,12 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
* that case we want the parent navigator or activity handler to take over.
*/
private void setupBackHandlerIfNeeded(ScreenStackFragment topScreen) {
if (!mTopScreen.isResumed()) {
// if the top fragment is not in a resumed state, adding back stack transaction would throw.
// In such a case we skip installing back handler and use FragmentLifecycleCallbacks to get
// notified when it gets resumed so that we can install the handler.
return;
}
getFragmentManager().removeOnBackStackChangedListener(mBackStackListener);
getFragmentManager().popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
ScreenStackFragment firstScreen = null;
@@ -208,6 +239,7 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
.hide(topScreen)
.show(topScreen)
.addToBackStack(BACK_STACK_TAG)
.setPrimaryNavigationFragment(topScreen)
.commitAllowingStateLoss();
getFragmentManager().addOnBackStackChangedListener(mBackStackListener);
}

View File

@@ -24,7 +24,7 @@ public class ScreenStackHeaderConfig extends ViewGroup {
private String mTitle;
private int mTitleColor;
private String mTitleFontFamily;
private int mTitleFontSize;
private float mTitleFontSize;
private int mBackgroundColor;
private boolean mIsHidden;
private boolean mGestureEnabled = true;
@@ -258,7 +258,7 @@ public class ScreenStackHeaderConfig extends ViewGroup {
mTitleFontFamily = titleFontFamily;
}
public void setTitleFontSize(int titleFontSize) {
public void setTitleFontSize(float titleFontSize) {
mTitleFontSize = titleFontSize;
}

View File

@@ -69,8 +69,8 @@ public class ScreenStackHeaderConfigViewManager extends ViewGroupManager<ScreenS
}
@ReactProp(name = "titleFontSize")
public void setTitleFontSize(ScreenStackHeaderConfig config, double titleFontSizeSP) {
config.setTitleFontSize((int) PixelUtil.toPixelFromSP(titleFontSizeSP));
public void setTitleFontSize(ScreenStackHeaderConfig config, float titleFontSize) {
config.setTitleFontSize(titleFontSize);
}
@ReactProp(name = "titleColor", customType = "Color")

View File

@@ -48,6 +48,7 @@ class StackView extends React.Component {
headerTitleStyle,
headerBackTitleStyle,
headerBackTitle,
headerBackTitleVisible,
headerTintColor,
gestureEnabled,
largeTitle,
@@ -69,7 +70,7 @@ class StackView extends React.Component {
titleFontFamily: headerTitleStyle && headerTitleStyle.fontFamily,
titleColor: headerTintColor,
titleFontSize: headerTitleStyle && headerTitleStyle.fontSize,
backTitle: headerBackTitle,
backTitle: headerBackTitleVisible === false ? '' : headerBackTitle,
backTitleFontFamily:
headerBackTitleStyle && headerBackTitleStyle.fontFamily,
backTitleFontSize: headerBackTitleStyle && headerBackTitleStyle.fontSize,
@@ -154,7 +155,7 @@ class StackView extends React.Component {
};
_renderScene = (index, route, descriptor) => {
const { navigation, getComponent } = descriptor;
const { navigation, getComponent, options } = descriptor;
const { mode, transparentCard } = this.props.navigationConfig;
const SceneComponent = getComponent();
@@ -167,7 +168,7 @@ class StackView extends React.Component {
return (
<Screen
key={`screen_${route.key}`}
style={StyleSheet.absoluteFill}
style={options.cardStyle}
stackPresentation={stackPresentation}
onDismissed={() => this._removeScene(route)}>
{this._renderHeaderConfig(index, route, descriptor)}

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-screens",
"version": "2.0.0-alpha.12",
"version": "2.0.0-alpha.14",
"description": "First incomplete navigation solution for your react-native app.",
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",