Android native stack bugfixes. (#190)

A few bugs fixed with android native stack in this commit:
 - fixed a problem with views not resizing when keyboard appears, the bug was due to onMeasure forwardin getHeight which was the old height of the container instead of the changed one
 - fixed a problem with back button behavior not being consistent. Now back button is controlled by 'gestureEnabled' to emulate iOS behavior where there is no hw back button but you can swipe back instead.
 - added compatibility for "contained" modal styles - for now we always render "containted" fragments on Android, plan to add a way to render in dialogs in the future
This commit is contained in:
Krzysztof Magiera
2019-10-22 23:22:48 +02:00
committed by GitHub
parent 4749405d64
commit d4636d3130
6 changed files with 67 additions and 23 deletions

View File

@@ -2,7 +2,6 @@ package com.swmansion.rnscreens;
import android.content.Context;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
@@ -13,7 +12,6 @@ import androidx.fragment.app.Fragment;
import com.facebook.react.bridge.GuardedRunnable;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.MeasureSpecAssertions;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.ReactPointerEventsView;
import com.facebook.react.uimanager.UIManagerModule;

View File

@@ -25,6 +25,10 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
onUpdate();
}
public Screen getTopScreen() {
return mTopScreen.getScreen();
}
public Screen getRootScreen() {
for (int i = 0, size = getScreenCount(); i < size; i++) {
Screen screen = getScreenAt(i);
@@ -51,9 +55,7 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for (int i = 0, size = getChildCount(); i < size; i++) {
getChildAt(i).measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
}
@@ -169,5 +171,9 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
mStack.addAll(mScreenFragments);
tryCommitTransaction();
for (ScreenStackFragment screen : mStack) {
screen.onStackUpdate();
}
}
}

View File

@@ -52,6 +52,13 @@ public class ScreenStackFragment extends ScreenFragment {
}
}
public void onStackUpdate() {
View child = mScreenView.getChildAt(0);
if (child instanceof ScreenStackHeaderConfig) {
((ScreenStackHeaderConfig) child).onUpdate();
}
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,

View File

@@ -1,7 +1,6 @@
package com.swmansion.rnscreens;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
@@ -29,17 +28,26 @@ public class ScreenStackHeaderConfig extends ViewGroup {
private int mTitleFontSize;
private int mBackgroundColor;
private boolean mIsHidden;
private boolean mGestureEnabled = true;
private boolean mIsBackButtonHidden;
private boolean mIsShadowHidden;
private int mTintColor;
private final Toolbar mToolbar;
private boolean mIsAttachedToWindow = false;
private OnBackPressedCallback mBackCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
getScreenStack().dismiss(getScreenFragment());
ScreenStack stack = getScreenStack();
Screen current = getScreen();
if (stack.getTopScreen() == current) {
stack.dismiss(getScreenFragment());
}
mBackCallback.remove();
}
};
private OnClickListener mBackClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
@@ -68,7 +76,14 @@ public class ScreenStackHeaderConfig extends ViewGroup {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
update();
mIsAttachedToWindow = true;
onUpdate();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mIsAttachedToWindow = false;
}
private Screen getScreen() {
@@ -101,14 +116,27 @@ public class ScreenStackHeaderConfig extends ViewGroup {
return null;
}
private void installBackCallback() {
mBackCallback.remove();
Fragment fragment = getScreenFragment();
fragment.requireActivity().getOnBackPressedDispatcher().addCallback(fragment, mBackCallback);
}
private void update() {
public void onUpdate() {
Screen parent = (Screen) getParent();
final ScreenStack stack = getScreenStack();
boolean isRoot = stack == null ? true : stack.getRootScreen() == parent;
boolean isTop = stack == null ? true : stack.getTopScreen() == parent;
// we need to clean up back handler especially in the case given screen is no longer on top
// because we don't want it to capture back event if it is not responsible for handling it
// as that would block other handlers from running
mBackCallback.remove();
if (!mIsAttachedToWindow || !isTop) {
return;
}
if (!isRoot && isTop && mGestureEnabled) {
Fragment fragment = getScreenFragment();
fragment.requireActivity().getOnBackPressedDispatcher().addCallback(fragment, mBackCallback);
mBackCallback.setEnabled(true);
}
if (mIsHidden) {
if (mToolbar.getParent() != null) {
getScreenFragment().removeToolbar();
@@ -125,13 +153,7 @@ public class ScreenStackHeaderConfig extends ViewGroup {
ActionBar actionBar = activity.getSupportActionBar();
// hide back button
final ScreenStack stack = getScreenStack();
boolean isRoot = stack == null ? true : stack.getRootScreen() == parent;
actionBar.setDisplayHomeAsUpEnabled(isRoot ? false : !mIsBackButtonHidden);
if (!isRoot) {
installBackCallback();
}
mBackCallback.setEnabled(!isRoot);
// when setSupportActionBar is called a toolbar wrapper gets initialized that overwrites
// navigation click listener. The default behavior set in the wrapper is to call into
@@ -268,6 +290,10 @@ public class ScreenStackHeaderConfig extends ViewGroup {
mIsShadowHidden = hideShadow;
}
public void setGestureEnabled(boolean gestureEnabled) {
mGestureEnabled = gestureEnabled;
}
public void setHideBackButton(boolean hideBackButton) {
mIsBackButtonHidden = hideBackButton;
}

View File

@@ -82,6 +82,11 @@ public class ScreenStackHeaderConfigViewManager extends ViewGroupManager<ScreenS
config.setHideShadow(hideShadow);
}
@ReactProp(name = "gestureEnabled", defaultBoolean = true)
public void setGestureEnabled(ScreenStackHeaderConfig config, boolean gestureEnabled) {
config.setGestureEnabled(gestureEnabled);
}
@ReactProp(name = "hideBackButton")
public void setHideBackButton(ScreenStackHeaderConfig config, boolean hideBackButton) {
config.setHideBackButton(hideBackButton);

View File

@@ -35,9 +35,11 @@ public class ScreenViewManager extends ViewGroupManager<Screen> {
public void setStackPresentation(Screen view, String presentation) {
if ("push".equals(presentation)) {
view.setStackPresentation(Screen.StackPresentation.PUSH);
} else if ("modal".equals(presentation)) {
} else if ("modal".equals(presentation) || "containedModal".equals(presentation)) {
// at the moment Android implementation does not handle contained vs regular modals
view.setStackPresentation(Screen.StackPresentation.MODAL);
} else if ("transparentModal".equals(presentation)) {
} else if ("transparentModal".equals(presentation) || "containedTransparentModal".equals((presentation))) {
// at the moment Android implementation does not handle contained vs regular modals
view.setStackPresentation(Screen.StackPresentation.TRANSPARENT_MODAL);
} else {
throw new JSApplicationIllegalArgumentException("Unknown presentation type " + presentation);