React Native требует двух нажатий для изменения фокуса ввода в режиме прокрутки
Представьте себе простой ScrollView с несколькими TextInputs, как
<ScrollView style={styles.container}>
<TextInput style={styles.input} />
<TextInput style={styles.input} />
</ScrollView>
Когда я ввожу первый ввод, клавиатура открывается, и я могу печатать текст. Когда я хочу переключиться на второй ввод, мне нужно дважды нажать - первый тип закрывает клавиатуру, и только второе нажатие открывает клавиатуру для второго ввода.
Одним из решений является использование keyboardShouldPersistTaps={true}
- переключение работает нормально, однако тогда клавиатура совсем не закрывается, и клавиатура может закрывать некоторые из последующих входов (или кнопок). Я также могу использовать keyboardDismissMode
Однако это просто закрыть клавиатуру при перетаскивании.
Мой вопрос заключается в том, как объединить эти два поведения - в ИМХО для лучшего пользовательского опыта - когда я щелкаю по другому входу, фокус меняется немедленно, не открывая клавиатуру, и когда я нажимаю где-то еще, клавиатура закрывается?
Я использую RN0.22, и образец приложения доступен по адресу https://rnplay.org/apps/kagpGw
ОБНОВЛЕНИЕ - Эта проблема могла быть решена в RN 0,40 - см. https://github.com/facebook/react-native/commit/552c60192172f6ec503181c060c08bbc5cbcc5a4
7 ответов
Я решил свою проблему с этим кодом
<ScrollView
keyboardDismissMode='on-drag'
keyboardShouldPersistTaps={true}
>
В конце я нашел решение, которое не является оптимальным (с точки зрения кодирования), но работает с точки зрения пользователя. Я сделал небольшой компонент, который можно использовать вместо ScrollView
:
export class InputScrollView extends React.Component {
constructor(props, ctx) {
super(props, ctx);
this.handleCapture = this.handleCapture.bind(this);
}
render() {
return (
<ScrollView keyboardShouldPersistTaps={true} keyboardDismissMode='on-drag'>
<View onStartShouldSetResponderCapture={this.handleCapture}>
{this.props.children}
</View>
</ScrollView>
);
}
handleCapture(e) {
const focusField = TextInputState.currentlyFocusedField();
const target = e.nativeEvent.target;
if (focusField != null && target != focusField){
const inputs = this.props.inputs;
if (inputs && inputs.indexOf(target) === -1) {
dismissKeyboard();
}
}
}
}
InputScrollView.propTypes = {
inputs : React.PropTypes.array,
}
Единственным недостатком является то, что мне нужно собирать дескрипторы узла (как возвращено React.findNodeHandle
) всех текстовых вводов "вручную" и передать его компоненту в виде массива.
Этот SO-ответ не совсем то, что вы просите, но автоматически выдвинет окно из-за клавиатуры, когда TextInput имеет фокус; Решение вашей клавиатуры может покрыть некоторые из последующих проблем ввода (или кнопок).
Просматривая https://facebook.github.io/react-native/docs/scrollview.html, я обнаружил метод, который может сделать это:
- После ввода в TextInput1, если вы нажмете Input2, клавиатура сохранится.
- Если щелкнуть где-нибудь пусто, клавиатура автоматически исчезнет.
Код выглядит следующим образом:
<ScrollView
keyboardDismissMode="none"
keyboardShouldPersistTaps="handled"
>
{content}
</ScrollView>
В новых версиях реакции-натива вы можете сохранить событие нажатия на прокрутку, как показано ниже:
<ScrollView
keyboardShouldPersistTaps='always'
keyboardDismissMode={'interactive'}></ScrollView>
Главный трюк keyboardShouldPersistTaps='always'
Я обновил приведенный выше ответ, потому что клавиатура ShouldPersistTaps={true} устарела и теперь используется клавиатура ShouldPersistTaps='всегда'
export class InputScrollView extends React.Component {
constructor(props, ctx) {
super(props, ctx);
this.handleCapture = this.handleCapture.bind(this);
}
render() {
return (
<ScrollView keyboardShouldPersistTaps='always' keyboardDismissMode='on-drag'>
<View onStartShouldSetResponderCapture={this.handleCapture}>
{this.props.children}
</View>
</ScrollView>
);
}
handleCapture(e) {
const focusField = TextInputState.currentlyFocusedField();
const target = e.nativeEvent.target;
if (focusField != null && target != focusField){
const inputs = this.props.inputs;
if (inputs && inputs.indexOf(target) === -1) {
dismissKeyboard();
}
}
}
}
InputScrollView.propTypes = {
inputs : React.PropTypes.array,
}
Если
keyboardShouldPersistTaps="always"
не работает для вас, проверьте свою кнопку и действие, которое она выполняет.
Наличие чего-то вроде приведенного ниже кода может привести к проблеме с двумя касаниями независимо от
keyboardShouldPersistTaps
ценить:
const submit = () => {
const _valid = some_validator();
setValid(_valid);
if (valid) {
// expected action
// may not fire on first press due to the state update call above
api.someAction();
} else {
// some error thing
}
};
Вместо этого вы можете использовать
useEffect
чтобы разделить вызов состояния кнопки и результирующее действие, чтобы событие щелчка имело единственную цель
useEffect(()=>{
if(valid) {
// expected action
api.someAction();
}
}, [valid])