Files
react-navigation/android/src/main/java/com/swmansion/rnscreens/Screen.java
Michał Osadnik 69a23f1c9f Remove blocking touch interactions if more than one active scree… (#296)
We believe that it might be better to manage the logic of blocking interactions from JS with setNativeProps and pointerEvents.

The current logic leads to some issues we're facing within React Navigation. There might be a state when more that one screen can be active. E.g. in modals we want to have two screens visible on the stack and have enabled interaction on the bottom one.

New code should not give any issues, because even though we need to handle activation of each screen from JS and this will just introduce an additional native call for enabling / disabling pointer events.

On iOS, we're not using React Native Screens in React Navigation for now and we'd like to focus on it later because right now we're not observing that crucial performance issues.
2020-01-27 11:07:07 +01:00

177 lines
5.0 KiB
Java

package com.swmansion.rnscreens;
import android.content.Context;
import android.graphics.Paint;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.facebook.react.bridge.GuardedRunnable;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.ReactPointerEventsView;
import com.facebook.react.uimanager.UIManagerModule;
public class Screen extends ViewGroup {
public enum StackPresentation {
PUSH,
MODAL,
TRANSPARENT_MODAL
}
public enum StackAnimation {
DEFAULT,
NONE,
FADE
}
private static OnAttachStateChangeListener sShowSoftKeyboardOnAttach = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
InputMethodManager inputMethodManager =
(InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(view, 0);
view.removeOnAttachStateChangeListener(sShowSoftKeyboardOnAttach);
}
@Override
public void onViewDetachedFromWindow(View view) {
}
};
private @Nullable Fragment mFragment;
private @Nullable ScreenContainer mContainer;
private boolean mActive;
private boolean mTransitioning;
private StackPresentation mStackPresentation = StackPresentation.PUSH;
private StackAnimation mStackAnimation = StackAnimation.DEFAULT;
private boolean mGestureEnabled = true;
public Screen(ReactContext context) {
super(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
final int width = r - l;
final int height = b - t;
final ReactContext reactContext = (ReactContext) getContext();
reactContext.runOnNativeModulesQueueThread(
new GuardedRunnable(reactContext) {
@Override
public void runGuarded() {
reactContext.getNativeModule(UIManagerModule.class)
.updateNodeSize(getId(), width, height);
}
});
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
clearDisappearingChildren();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// This method implements a workaround for RN's autoFocus functionality. Because of the way
// autoFocus is implemented it sometimes gets triggered before native text view is mounted. As
// a result Android ignores calls for opening soft keyboard and here we trigger it manually
// again after the screen is attached.
View view = getFocusedChild();
if (view != null) {
while (view instanceof ViewGroup) {
view = ((ViewGroup) view).getFocusedChild();
}
if (view instanceof TextView) {
TextView textView = (TextView) view;
if (textView.getShowSoftInputOnFocus()) {
textView.addOnAttachStateChangeListener(sShowSoftKeyboardOnAttach);
}
}
}
}
/**
* While transitioning this property allows to optimize rendering behavior on Android and provide
* a correct blending options for the animated screen. It is turned on automatically by the container
* when transitioning is detected and turned off immediately after
*/
public void setTransitioning(boolean transitioning) {
if (mTransitioning == transitioning) {
return;
}
mTransitioning = transitioning;
super.setLayerType(transitioning ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null);
}
public void setStackPresentation(StackPresentation stackPresentation) {
mStackPresentation = stackPresentation;
}
public void setStackAnimation(StackAnimation stackAnimation) {
mStackAnimation = stackAnimation;
}
public void setGestureEnabled(boolean gestureEnabled) {
mGestureEnabled = gestureEnabled;
}
public StackAnimation getStackAnimation() {
return mStackAnimation;
}
public StackPresentation getStackPresentation() {
return mStackPresentation;
}
@Override
public void setLayerType(int layerType, @Nullable Paint paint) {
// ignore - layer type is controlled by `transitioning` prop
}
protected void setContainer(@Nullable ScreenContainer container) {
mContainer = container;
}
protected void setFragment(Fragment fragment) {
mFragment = fragment;
}
protected @Nullable Fragment getFragment() {
return mFragment;
}
protected @Nullable ScreenContainer getContainer() {
return mContainer;
}
public void setActive(boolean active) {
if (active == mActive) {
return;
}
mActive = active;
if (mContainer != null) {
mContainer.notifyChildUpdate();
}
}
public boolean isActive() {
return mActive;
}
public boolean isGestureEnabled() {
return mGestureEnabled;
}
}