Let ScrollView Know About Keyboard Opened Before Mount
Summary: ScrollView has special behavior when the keyboard is open, but starts listening to keyboard events on mount. This means a ScrollView mounted after the keyboard is already up (e.g. for a typeahead) is not initialized to the keyboard being up. This change adds `Keyboard.isVisible()` and `Keyboard.metrics()` APIs to allow seeding initial keyboard metrics. Changelog: [General][Fixed] - Inform ScrollView of Keyboard Events Before Mount Reviewed By: JoshuaGross, yungsters Differential Revision: D38701976 fbshipit-source-id: 42b354718fbf5001ca4b90de0442eeab0be91e7a
This commit is contained in:
Родитель
9e4114abe1
Коммит
26d148029c
|
@ -24,7 +24,7 @@ export type KeyboardEventEasing =
|
|||
| 'linear'
|
||||
| 'keyboard';
|
||||
|
||||
export type KeyboardEventCoordinates = $ReadOnly<{|
|
||||
export type KeyboardMetrics = $ReadOnly<{|
|
||||
screenX: number,
|
||||
screenY: number,
|
||||
width: number,
|
||||
|
@ -36,7 +36,7 @@ export type KeyboardEvent = AndroidKeyboardEvent | IOSKeyboardEvent;
|
|||
type BaseKeyboardEvent = {|
|
||||
duration: number,
|
||||
easing: KeyboardEventEasing,
|
||||
endCoordinates: KeyboardEventCoordinates,
|
||||
endCoordinates: KeyboardMetrics,
|
||||
|};
|
||||
|
||||
export type AndroidKeyboardEvent = $ReadOnly<{|
|
||||
|
@ -47,7 +47,7 @@ export type AndroidKeyboardEvent = $ReadOnly<{|
|
|||
|
||||
export type IOSKeyboardEvent = $ReadOnly<{|
|
||||
...BaseKeyboardEvent,
|
||||
startCoordinates: KeyboardEventCoordinates,
|
||||
startCoordinates: KeyboardMetrics,
|
||||
isEventFromThisApp: boolean,
|
||||
|}>;
|
||||
|
||||
|
@ -103,6 +103,8 @@ type KeyboardEventDefinitions = {
|
|||
*/
|
||||
|
||||
class Keyboard {
|
||||
_currentlyShowing: ?KeyboardEvent;
|
||||
|
||||
_emitter: NativeEventEmitter<KeyboardEventDefinitions> =
|
||||
new NativeEventEmitter(
|
||||
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
||||
|
@ -110,6 +112,15 @@ class Keyboard {
|
|||
Platform.OS !== 'ios' ? null : NativeKeyboardObserver,
|
||||
);
|
||||
|
||||
constructor() {
|
||||
this.addListener('keyboardDidShow', ev => {
|
||||
this._currentlyShowing = ev;
|
||||
});
|
||||
this.addListener('keyboardDidHide', _ev => {
|
||||
this._currentlyShowing = null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The `addListener` function connects a JavaScript function to an identified native
|
||||
* keyboard notification event.
|
||||
|
@ -158,6 +169,20 @@ class Keyboard {
|
|||
dismissKeyboard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the keyboard is last known to be visible.
|
||||
*/
|
||||
isVisible(): boolean {
|
||||
return !!this._currentlyShowing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the metrics of the soft-keyboard if visible.
|
||||
*/
|
||||
metrics(): ?KeyboardMetrics {
|
||||
return this._currentlyShowing?.endCoordinates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for syncing TextInput (or other keyboard accessory view) size of
|
||||
* position changes with keyboard movements.
|
||||
|
|
|
@ -22,7 +22,7 @@ import type {
|
|||
ViewLayout,
|
||||
ViewLayoutEvent,
|
||||
} from '../View/ViewPropTypes';
|
||||
import type {KeyboardEvent, KeyboardEventCoordinates} from './Keyboard';
|
||||
import type {KeyboardEvent, KeyboardMetrics} from './Keyboard';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
...ViewProps,
|
||||
|
@ -71,7 +71,7 @@ class KeyboardAvoidingView extends React.Component<Props, State> {
|
|||
this.viewRef = React.createRef();
|
||||
}
|
||||
|
||||
_relativeKeyboardHeight(keyboardFrame: KeyboardEventCoordinates): number {
|
||||
_relativeKeyboardHeight(keyboardFrame: KeyboardMetrics): number {
|
||||
const frame = this._frame;
|
||||
if (!frame || !keyboardFrame) {
|
||||
return 0;
|
||||
|
|
|
@ -42,7 +42,7 @@ import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes';
|
|||
import type {ViewProps} from '../View/ViewPropTypes';
|
||||
import ScrollViewContext, {HORIZONTAL, VERTICAL} from './ScrollViewContext';
|
||||
import type {Props as ScrollViewStickyHeaderProps} from './ScrollViewStickyHeader';
|
||||
import type {KeyboardEvent} from '../Keyboard/Keyboard';
|
||||
import type {KeyboardEvent, KeyboardMetrics} from '../Keyboard/Keyboard';
|
||||
import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
|
||||
|
||||
import Commands from './ScrollViewCommands';
|
||||
|
@ -731,7 +731,7 @@ class ScrollView extends React.Component<Props, State> {
|
|||
new Map();
|
||||
_headerLayoutYs: Map<string, number> = new Map();
|
||||
|
||||
_keyboardWillOpenTo: ?KeyboardEvent = null;
|
||||
_keyboardMetrics: ?KeyboardMetrics = null;
|
||||
_additionalScrollOffset: number = 0;
|
||||
_isTouching: boolean = false;
|
||||
_lastMomentumScrollBeginTime: number = 0;
|
||||
|
@ -769,7 +769,7 @@ class ScrollView extends React.Component<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
this._keyboardWillOpenTo = null;
|
||||
this._keyboardMetrics = Keyboard.metrics();
|
||||
this._additionalScrollOffset = 0;
|
||||
|
||||
this._subscriptionKeyboardWillShow = Keyboard.addListener(
|
||||
|
@ -1075,8 +1075,8 @@ class ScrollView extends React.Component<Props, State> {
|
|||
let keyboardScreenY = Dimensions.get('window').height;
|
||||
|
||||
const scrollTextInputIntoVisibleRect = () => {
|
||||
if (this._keyboardWillOpenTo != null) {
|
||||
keyboardScreenY = this._keyboardWillOpenTo.endCoordinates.screenY;
|
||||
if (this._keyboardMetrics != null) {
|
||||
keyboardScreenY = this._keyboardMetrics.screenY;
|
||||
}
|
||||
let scrollOffsetY =
|
||||
top - keyboardScreenY + height + this._additionalScrollOffset;
|
||||
|
@ -1094,8 +1094,8 @@ class ScrollView extends React.Component<Props, State> {
|
|||
this._preventNegativeScrollOffset = false;
|
||||
};
|
||||
|
||||
if (this._keyboardWillOpenTo == null) {
|
||||
// `_keyboardWillOpenTo` is set inside `scrollResponderKeyboardWillShow` which
|
||||
if (this._keyboardMetrics == null) {
|
||||
// `_keyboardMetrics` is set inside `scrollResponderKeyboardWillShow` which
|
||||
// is not guaranteed to be called before `_inputMeasureAndScrollToKeyboard` but native has already scheduled it.
|
||||
// In case it was not called before `_inputMeasureAndScrollToKeyboard`, we postpone scrolling to
|
||||
// text input.
|
||||
|
@ -1243,32 +1243,28 @@ class ScrollView extends React.Component<Props, State> {
|
|||
scrollResponderKeyboardWillShow: (e: KeyboardEvent) => void = (
|
||||
e: KeyboardEvent,
|
||||
) => {
|
||||
this._keyboardWillOpenTo = e;
|
||||
this._keyboardMetrics = e.endCoordinates;
|
||||
this.props.onKeyboardWillShow && this.props.onKeyboardWillShow(e);
|
||||
};
|
||||
|
||||
scrollResponderKeyboardWillHide: (e: KeyboardEvent) => void = (
|
||||
e: KeyboardEvent,
|
||||
) => {
|
||||
this._keyboardWillOpenTo = null;
|
||||
this._keyboardMetrics = null;
|
||||
this.props.onKeyboardWillHide && this.props.onKeyboardWillHide(e);
|
||||
};
|
||||
|
||||
scrollResponderKeyboardDidShow: (e: KeyboardEvent) => void = (
|
||||
e: KeyboardEvent,
|
||||
) => {
|
||||
// TODO(7693961): The event for DidShow is not available on iOS yet.
|
||||
// Use the one from WillShow and do not assign.
|
||||
if (e) {
|
||||
this._keyboardWillOpenTo = e;
|
||||
}
|
||||
this._keyboardMetrics = e.endCoordinates;
|
||||
this.props.onKeyboardDidShow && this.props.onKeyboardDidShow(e);
|
||||
};
|
||||
|
||||
scrollResponderKeyboardDidHide: (e: KeyboardEvent) => void = (
|
||||
e: KeyboardEvent,
|
||||
) => {
|
||||
this._keyboardWillOpenTo = null;
|
||||
this._keyboardMetrics = null;
|
||||
this.props.onKeyboardDidHide && this.props.onKeyboardDidHide(e);
|
||||
};
|
||||
|
||||
|
@ -1547,7 +1543,7 @@ class ScrollView extends React.Component<Props, State> {
|
|||
// keyboard, except on Android where setting windowSoftInputMode to
|
||||
// adjustNone leads to missing keyboard events.
|
||||
const softKeyboardMayBeOpen =
|
||||
this._keyboardWillOpenTo != null || Platform.OS === 'android';
|
||||
this._keyboardMetrics != null || Platform.OS === 'android';
|
||||
|
||||
return hasFocusedTextInput && softKeyboardMayBeOpen;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче