BREAKING: Fix modal resizing on keyboard show

Summary:
This changes modal behavior to resize when the keyboard appears/disappears.
Previously, the modal would not react in any way, or it would pan above to bring the
TextInput into view. Resizing is the correct behavior for android.

This is not trivial, as in, setting the flag, because of the combination of
react native laying out all views and the system reacting to the keyboard
appearance in a weird way. Namely:
- if `windowTranslucentStatus` is not set, the system will just call
  `onSizeChanged` on the dialog's content view, and everything works nicely
- with `windowTranslucentStatus` set, the system will consider the dialog as a
  full screen view that doesn't resize. In order for it to resize, the base
  view of the layout needs to have
  `setFitsSystemWindows(true)` called on it. This is needed, so that the system
  can call layout on that base view with the new value of `paddingBottom` that
  coincides with the height of the keyboard. Neat.

We fix this by wrapping our existing content view (mHostView) in a simple
FrameLayout that has `setFitsSystemWindows` set. That way, `mHostView` will have
`onSizeChanged` called on itself with the correct new size of the dialog.

This has the fortunate consequence of our layout now also getting `paddingTop` as the size of the
status bar, which means that we can remove the JS `top` hack in Modal, which
was necessary for no view getting drawn under the status bar.

This behavior is set as default, since that is the default correct Android behavior.

Reviewed By: astreet

Differential Revision: D3913784

fbshipit-source-id: 4378ada21f466dc7ac6e357abeca10b88009ca3f
This commit is contained in:
Andrei Coman 2016-09-28 02:53:36 -07:00 коммит произвёл Facebook Github Bot 1
Родитель fc62b00880
Коммит 404b7cc069
2 изменённых файлов: 18 добавлений и 7 удалений

Просмотреть файл

@ -16,10 +16,9 @@ const Platform = require('Platform');
const PropTypes = require('react/lib/ReactPropTypes');
const React = require('React');
const StyleSheet = require('StyleSheet');
const UIManager = require('UIManager');
const View = require('View');
const deprecatedPropType = require('deprecatedPropType');
const deprecatedPropType = require('deprecatedPropType');
const requireNativeComponent = require('requireNativeComponent');
const RCTModalHostView = requireNativeComponent('RCTModalHostView', null);
@ -136,7 +135,6 @@ class Modal extends React.Component {
const containerStyles = {
backgroundColor: this.props.transparent ? 'transparent' : 'white',
top: Platform.OS === 'android' && Platform.Version >= 19 ? UIManager.RCTModalHostView.Constants.StatusBarHeight : 0,
};
let animationType = this.props.animationType;

Просмотреть файл

@ -17,13 +17,13 @@ import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Point;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.R;
@ -204,7 +204,7 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListe
}
mDialog = new Dialog(getContext(), theme);
mDialog.setContentView(mHostView);
mDialog.setContentView(getContentView());
updateProperties();
mDialog.setOnShowListener(mOnShowListener);
@ -236,9 +236,23 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListe
}
});
mDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
mDialog.show();
}
/**
* Returns the view that will be the root view of the dialog. We are wrapping this in a
* FrameLayout because this is the system's way of notifying us that the dialog size has changed.
* This has the pleasant side-effect of us not having to preface all Modals with
* "top: statusBarHeight", since that margin will be included in the FrameLayout.
*/
private View getContentView() {
FrameLayout frameLayout = new FrameLayout(getContext());
frameLayout.addView(mHostView);
frameLayout.setFitsSystemWindows(true);
return frameLayout;
}
/**
* updateProperties will update the properties that do not require us to recreate the dialog
* Properties that do require us to recreate the dialog should set mPropertyRequiresNewDialog to
@ -284,9 +298,8 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListe
new Runnable() {
@Override
public void run() {
Point modalSize = ModalHostHelper.getModalHostSize(getContext());
((ReactContext) getContext()).getNativeModule(UIManagerModule.class)
.updateNodeSize(getChildAt(0).getId(), modalSize.x, modalSize.y);
.updateNodeSize(getChildAt(0).getId(), w, h);
}
});
}