组件挂载时如何在 React Native 中自动打开键盘?
Posted
技术标签:
【中文标题】组件挂载时如何在 React Native 中自动打开键盘?【英文标题】:How to open keyboard automatically in React Native when component mounts? 【发布时间】:2018-10-03 23:55:34 【问题描述】:我的页面只有一个TextInput
,我已经传入了autoFocus prop:autoFocus: true
。
<TextInput
style=styles.textInput
placeholder="Quiz Deck Title"
autoFocus=true
value=this.state.title
onChangeText=(title) => this.controlledTextInput(title)
/>
我要避免的是要求用户在键盘弹出之前“单击”TextInput
框。
但我也希望软键盘也自动打开(如果设备没有硬件键盘)。
有没有办法在本机反应中实现这一点?我正在为 ios 和 android 开发。
如果重要,我的页面将通过 TabNavigator 导航到。我之所以提到这一点,是因为对另一个类似 SO 问题的评论(见下文)表明他们在使用 StackNavigator 到达他们的页面时遇到了类似的问题.
关于类似 SO 问题的注意事项:How to open keyboard automatically in React Native? :没有提供解决方案,其他人的 cmets 建议的结果与我相同:输入已聚焦,但键盘不会自动打开。Close/hide the Android Soft Keyboard 和Android: show soft keyboard automatically when focus is on an EditText :正在使用本机android代码(java),而不是反应本机代码(javascript)。
注意:我正在使用 android 模拟器 Nexus 6P 和 android 23(推荐)和 ios 模拟器和 iPhone 6s 进行开发,因为我没有物理设备。
编辑:添加请求的代码
NewDeck.js(我希望键盘自动弹出的视图):
import React from 'react';
import connect from 'react-redux';
import View, Text, TouchableOpacity,
TextInput, KeyboardAvoidingView,
StyleSheet, Platform,
from 'react-native';
import StyledButton from '../components/StyledButton';
import saveDeck from '../store/decks/actionCreators';
import getDeckList from '../store/decks/selectors';
import fetchDecks from '../utils/api';
import saveDeckTitle from '../utils/api';
from '../utils/colors';
import titleCase, stripInvalidChars, makeStringUnique
from '../utils/helpers';
import white, gray, primaryColor, primaryColorDark, primaryColorLight,
class NewDeck extends React.Component
state =
title: '',
canSubmit: false,
componentDidMount ()
this.textInputRef.focus()
controlledTextInput(title)
title = titleCase(stripInvalidChars(title));
const canSubmit = this.isValidInput(title);
this.setState( title, canSubmit );
isValidInput(text)
return text.trim() !== '';
onBlur()
title = this.state.title.trim();
const unique = makeStringUnique(title, this.props.existingTitles);
this.setState( title: unique );
onSubmit()
let title = this.state.title.trim();
title = makeStringUnique(title, this.props.existingTitles)
saveDeckTitle(title)
this.props.navigation.navigate('Home');
render()
return (
<View style=styles.container>
<View style=[styles.cardContainer, flex: 1]>
<Text style=styles.instructionsText
>
Title for your New Quiz Deck
</Text>
<KeyboardAvoidingView ...keyboardAvoidingViewProps>
<TextInput
style=styles.textInput
placeholder="Quiz Deck Title"
value=this.state.title
onChangeText=(title) => this.controlledTextInput(title)
/* autoFocus=true */
ref=ref => this.textInputRef = ref
/>
</KeyboardAvoidingView>
</View>
<KeyboardAvoidingView
...keyboardAvoidingViewProps
style=[styles.buttonsContainer, styles.buttonContainer]
>
<StyledButton
style=[styles.item, style=flex: 2]
onPress=() => this.onSubmit()
disabled=!this.state.canSubmit
>
<Text>
Submit
</Text>
</StyledButton>
</KeyboardAvoidingView>
</View>
);
const keyboardAvoidingViewProps =
behavior: 'padding',
;
// ...styles definition here, - I posted it in a later code block, to minimize
// clutter, in the event that it is irrelevant to this issue
function mapStoreToProps(store)
const decks = getDeckList(store) || null;
// ensure titles are unique (better UX than if just make id unique)
const existingTitles = decks && decks.map(deck =>
return deck.title
) || [];
return
existingTitles,
export default connect(mapStoreToProps)(NewDeck);
TabNavigator 和 StackNavigator 代码(在 App.js 中):
// ... the TabNavigator I'm using:
import TabNavigator, StackNavigator from 'react-navigation';
//... the class's render method, uses StackNavigator (as MainNavigation)
render()
return (
<Provider store=createStore(rootReducer)>
<View style=flex:1>
<AppStatusBar
backgroundColor=primaryColor
barStyle="light-content"
/>
<MainNavigation />
</View>
</Provider>
);
// ...
const Tabs = TabNavigator(
DeckList:
screen: DeckList,
navigationOptions:
tabBarLabel: 'Quiz Decks',
tabBarIcon: ( tintColor ) => // icons only show in ios
<Ionicons name='ios-bookmarks' size=30 color=tintColor />
,
,
NewDeck:
screen: NewDeck,
navigationOptions:
tabBarLabel: 'Create New Deck',
tabBarIcon: ( tintColor ) => // icons only show in ios
<FontAwesome name='plus-square' size=30 color=tintColor />
,
,
,
navigationOptions:
// do-not-display page headers for Tab Navigation
header: null
,
tabBarOptions:
// ios icon and text color; android text color
activeTintColor: Platform.OS === 'ios' ? primaryColor : white,
pressColor: white,
indicatorStyle:
backgroundColor: primaryColorDark,
height: 3,
,
style:
height: 56,
backgroundColor: Platform.OS === 'ios' ? white : primaryColor,
shadowColor: 'rgba(0, 0, 0, 0.24)',
shadowOffset:
width: 0,
height: 3
,
shadowRadius: 6,
shadowOpacity: 1
);
//... StackNavigator uses TabNavigator (as Tabs)
const stackScreenNavigationOptions =
headerTintColor: white,
headerStyle:
backgroundColor: primaryColor,
;
const MainNavigation = StackNavigator(
// RouteConfigs: This is analogous to defining Routes in a web app
Home:
screen: Tabs, // Which also loads the first Tab (DeckList)
,
Deck:
screen: Deck,
navigationOptions: stackScreenNavigationOptions,
,
Quiz:
screen: Quiz,
navigationOptions: stackScreenNavigationOptions,
,
NewDeck:
screen: NewDeck,
navigationOptions: stackScreenNavigationOptions,
,
NewCard:
screen: NewCard,
navigationOptions: stackScreenNavigationOptions,
,
,
);
这是 NewDeck.js
的 styles 定义const styles = StyleSheet.create(
// CONTAINER styles
wrapper:
// this was the previous container style
flex: 1,
backgroundColor: white,
alignItems: 'center',
justifyContent: 'center',
,
container:
flex: 1,
backgroundColor: white,
alignItems: 'center',
justifyContent: 'space-between',
padding: 10,
paddingTop: 30,
paddingBottom: 5,
,
cardContainer:
flex: 1,
justifyContent: 'flex-start',
alignSelf: 'stretch',
backgroundColor: '#fefefe',
padding: 20,
marginLeft: 30,
marginRight: 30,
marginTop: 10,
borderRadius: Platform.OS === 'ios' ? 20 : 10,
shadowRadius: 3,
shadowOpacity: 0.8,
shadowColor: 'rgba(0, 0, 0, 0.24)',
shadowOffset:
width: 0,
height: 3,
,
marginBottom:20,
,
buttonsContainer:
flex: 3,
alignSelf: 'stretch',
justifyContent: 'flex-start',
,
buttonContainer:
justifyContent: 'center',
margin: 10,
,
// TEXT Styles
instructionsText:
flex: 1,
fontSize: 20,
color: gray,
alignSelf: 'center',
textAlign: 'center',
,
// INPUTTEXT styles
textInput:
fontSize: 27,
color: primaryColor,
alignSelf: 'stretch',
flexWrap: 'wrap',
textAlign: 'center',
marginTop: 10,
,
);
StyledButton.js(基本上,TouchableOpacity
具有特定于平台的样式,可在整个应用程序中普遍使用):
import React from 'react';
import Text, TouchableOpacity, StyleSheet, Platform from 'react-native';
import white, gray, primaryColor, primaryColorLight, primaryColorDark from '../utils/colors';
export default function TextButton( children, onPress, customColor, disabled=false )
const disabledColor = disabled ? gray : null;
const backgroundColor = Platform.OS==='ios' ? white : disabledColor || customColor || primaryColorLight;
const borderColor = Platform.OS==='ios' ? disabledColor || customColor || primaryColorDark
const textColor = Platform.OS==='ios' ? disabledColor || customColor || primaryColor : white;
const btnStyle = Platform.OS==='ios' ? styles.iosBtn : styles.androidBtn;
const txtStyle = styles.txtDefault;
const btnColor = backgroundColor, borderColor ;
const txtColor = color: textColor ;
return (
<TouchableOpacity
onPress=onPress
disabled=disabled
style=[btnStyle, btnColor]
>
<Text
style=[styles.txtDefault, txtColor]
>
children
</Text>
</TouchableOpacity>
);
const styles = StyleSheet.create(
txtDefault:
textAlign: 'center',
// because of bleeding of white text to colored background on android,
// enlarge text (or increase fontWeight) for better readability
fontSize: Platform.OS==='ios' ? 15 : 18,
padding: 10,
,
iosBtn:
height: 45,
borderRadius: 7,
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
// ios only settings
borderColor: primaryColorDark,
borderWidth: 1,
borderRadius: 3,
paddingLeft: 25,
paddingRight: 25,
,
androidBtn:
height: 45,
borderRadius: 5,
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
// android- only settings
// (padding accepts clicks, vs. margin === no-click zone)
padding: 20,
paddingLeft: 15,
paddingRight: 15,
,
);
// ios has white buttons with colored outlines and colored text
// android has colored buttons with white text
// Pass in a button color, or it defaults to the App's primary colors
【问题讨论】:
如果您只在模拟器上进行过测试,那么您可能只是默认隐藏了键盘。在ios模拟器中,按cmd + k 开启。 @MattAft - 是的,我确实碰巧找到了。很高兴您为未来的访问者提及它,因为这不是一个明显的要求。 @MattAft 目前在两个平台上,用户都需要在键盘出现之前“单击”文本字段。当我使用这样的应用程序时,我发现这是一个令人讨厌的功能。我更喜欢的是,当页面加载时键盘会自动弹出。 您可以在组件加载时将焦点添加到任何textinput
以使用它的引用显示键盘
@MattAft 我错了:键盘不会在 Android 中自动弹出,但在 iOS 中会弹出(cmd-k 已打开)。
【参考方案1】:
只需将 ref 包裹在 timeout 内
setTimeout(()=>this.textInputRef.focus(),100)
【讨论】:
你知道为什么需要这样做吗? 我猜这个小小的延迟给了在调用 focus() 之前实际分配 ref 的时间。【参考方案2】:老问题,但如果有人在这里搜索答案..
如果您卡在动画中(例如,如果来自另一个屏幕),键盘似乎不会出现。
您可以使用ref
专注于您的输入,但必须包含在InteractionManager.runAfterInteractions
中才能正常工作。
我就是这样解决的:
export const CustomInput: FunctionComponent<Props & TextInputProps> = (
error,
...props
) =>
const inputRef = useRef<TextInput>(null);
useEffect(() =>
// Must run after animations for keyboard to automatically open
InteractionManager.runAfterInteractions(() =>
if (inputRef?.current)
inputRef.current.focus();
);
, [inputRef]);
return (
<View>
<TextInput
ref=inputRef
...props
/>
error && <ErrorText>error</ErrorText>
</View>
);
;```
【讨论】:
【参考方案3】:您始终可以在任何TextInput
上使用.focus
当组件加载时,如果您想避免autoFocus
,则显示键盘。
componentDidMount ()
this.textInputRef.focus()
<TextInput
style=styles.textInput
ref=ref => this.textInputRef = ref
placeholder="Quiz Deck Title"
autoFocus=true
value=this.state.title
onChangeText=(title) => this.controlledTextInput(title)
/>
如docs中所述
通过本机元素公开的两个方法是
.focus()
和.blur()
,它们将以编程方式聚焦或模糊 TextInput。
【讨论】:
不幸的是,这给了我同样的行为:用户必须在键盘弹出之前“点击”输入框。 好的,当你加载同一个组件时,textInput
是焦点还是 unfocused
?
嗯..好问题。光标未显示在 TextInput 框内(直到我在 TextInput 内单击),所以我怀疑它实际上不是“聚焦”。这对于您的代码和我发布的代码都是一样的。
可能有一些overlay
或其他东西正在使用ref focus
。
当你使用自动对焦时,它基本上会在组件安装后调用焦点,所以它是完全相同的东西。如果您键入并且它显示在 textinput 上,那么您就会知道它已获得焦点。【参考方案4】:
我现在只需要“autoFocus”道具就可以做到这一点。
https://reactnative.dev/docs/textinput#autofocus
【讨论】:
以上是关于组件挂载时如何在 React Native 中自动打开键盘?的主要内容,如果未能解决你的问题,请参考以下文章
(React、MaterialUI、Context)MUI 自动完成/TextField 未在挂载时呈现其值
如何使用 redux 让根组件在 react-native 中重新渲染(开源项目)