如何在React Native应用程序中使用反应导航关闭键盘
在移动应用程序中,显示和关闭键盘似乎是一件微不足道的事情,但是当它与react-navigation
和模式呈现一起使用时,在自动关闭它方面会很棘手。 至少那是根据我最初的假设。 本文旨在详细介绍我从键盘处理中学到的知识,以及在处理TextInput
时如何避免多余的敲击。由于所有库都是开源的,因此还将有很多代码拼写。 在撰写本文时,我正在使用的React Native版本是0.57.5
内置的TextInput组件
React Native带有许多基本组件,其中一个是TextInput,用于通过键盘将文本输入到应用中。
从'react'导入React,{组件};
从'react-native'导入{AppRegistry,TextInput};
导出默认类UselessTextInput扩展Component {
构造函数(道具){
超级(道具);
this.state = {text:'Useless Placeholder'};
}
render(){
返回(
<TextInput
样式= {{高度:40,borderColor:'灰色',borderWidth:1}}
onChangeText = {(文本)=> this.setState({text})}
值= {this.state.text}
/>
);
}
}
就是这样,每当我们单击文本输入时,就会出现键盘,允许我们输入值。 要通过按屏幕上的任意位置来关闭键盘,简单的解决方法是将TouchableWithoutFeedback
与Keyboard
一起使用。 这类似于在iOS UIView
拥有UITapGestureRecognizer
并调用view.endEditing
import { Keyboard } from 'react-native'
Keyboard.dismiss()
ScrollView中的TextInput
通常,在React Native(主要是ScrollView
的滚动组件中,我们应该有一些文本输入,以便能够处理较长的内容列表并避免使用键盘。 如果TextInput
在ScrollView
则关闭键盘的行为会有所不同,并取决于keyboardShouldPersistTaps
确定敲击后键盘何时保持可见状态。
-
'never'
(默认),在键盘向上弹起时在聚焦文本输入之外点击会关闭键盘。 发生这种情况时,孩子们将不会收到水龙头。 -
'always'
('always'
,键盘不会自动关闭,并且滚动视图也不会捕获敲击,但是滚动视图的子级可以捕获敲击。 -
'handled'
,当水龙头被儿童操纵(或被祖先捕捉)时,键盘不会自动关闭。
在大多数情况下, never
模式应该是理想的行为,在聚焦文本输入之外的任何地方单击都可以关闭键盘。
在我的应用程序中,有一些文本输入和一个操作按钮。 场景是用户输入一些信息,然后按该按钮注册数据。 在“ never
模式下,我们必须按两次按钮,一次是关闭键盘,两次是onPress
。 因此解决方案是使用always
模式。 这样, Button
总是首先获得按下事件。
<ScrollView keyboardShouldPersistTaps='always' />
ScrollView关心键盘
可以响应本机ScrollView
本机RCTScrollView
类具有处理关闭模式的代码
RCT_SET_AND_PRESERVE_OFFSET(setKeyboardDismissMode,keyboardDismissMode,UIScrollViewKeyboardDismissMode)
它选择的选项是keyboardDismissMode
属性的UIScrollViewKeyboardDismissMode
在滚动视图中开始拖动时关闭键盘的方式。
如您所见,可能的模式是onDrag
和interactive
。 并通过keyboardShouldPersistTaps
对本机做出反应以显示自定义点keyboardShouldPersistTaps
case none
键盘不会因拖动而消失。
case onDrag
开始拖动时,将关闭键盘。
case interactive
键盘跟随拖动触摸屏外移动,可以再次向上拉以取消关闭。
模态内的ScrollView
但是,当ScrollView
在Modal
内部时,这不起作用。 所谓Modal
就是我在React Native中的Modal组件。 我唯一使用的库是react-navigation
,它也支持打开全屏模式,但是它们以方式让我们在react-navigation
声明模态看起来像是堆栈,而且很混乱,所以我宁愿不使用它。 我在react-native
使用Modal
,效果很好。
因此,如果在Modal
内部的ScrollView
具有TextInput
则keyboardShouldPersistTaps
无法正常工作。 Modal
似乎知道父级ScrollView
因此我们必须在每个父级ScrollView
上声明keyboardShouldPersistTaps='always'
。 在React Native中, FlatList
和SectionList
在SectionList
使用ScrollView
,因此我们需要了解所有这些ScrollView
组件。
摸索反应导航
由于我的应用程序严重依赖于react-navigation
,因此最好对它的组件有深入的了解,以便我们确定问题出在哪里。 我在下面写了一些关于反应导航结构的文章。
在React Native应用中使用react-navigation 3.0
react-navigation可能是我在React Native应用程序中使用的唯一依赖项。 到目前为止,我对此感到满意,然后是3.0版… codeburst.io
像每个传统的移动应用程序一样,我的应用程序由选项卡导航器中的许多堆栈导航器组成。 在iOS中,这意味着UITabbarController
许多UINavigationViewController
。 在react-navigation
我在createBottomTabNavigator
使用createMaterialTopTabNavigator
从“反应导航”导入{createMaterialTopTabNavigator}
从'react-navigation-tabs'导入{createBottomTabNavigator,BottomTabBar}
我遇到键盘问题的屏幕是一个堆栈导航器中从第二个屏幕显示的Modal
,因此让我们检查层次结构中每个可能的ScrollView
。 这个过程涉及大量的代码阅读,这就是我爱开源的方式。
首先让我们从createBottomTabNavigator开始,它使用createTabNavigator及其自己的TabNavigationView
TabNavigationView类扩展了React.PureComponent
导出默认的createTabNavigator(TabNavigationView);
标签导航器在ScreenContainer
具有标签栏视图,该标签栏视图用于包含视图。 ScreenContainer
来自react-native-screens“该项目旨在将本机导航容器组件公开给React Native”。 以下是标签导航器的工作方式。
render(){
const {navigation,renderScene,lazy} = this.props;
const {routes} = navigation.state;
const {已加载} = this.state
返回(
{routes.map((route,index)=> {
如果(懒惰&&!loaded.includes(index)){
//如果从未浏览过屏幕,请勿渲染
返回null;
const isFocused = navigation.state.index ===索引
返回(
<ResourceSavingScene
键= {route.key}
style = {StyleSheet.absoluteFill}
isVisible = {isFocused}
>
{renderScene({route})}
);
})}
{this._renderTabBar()}
);
}
使用_renderTabBar
函数中的_renderTabBar
渲染选项卡栏。 查看代码,整个选项卡导航器与ScrollView
无关。
因此,可疑列表上仅剩下createMaterialTopTabNavigator。 我在带有swipeEnabled: true
的应用程序中使用它。 通过查看导入,顶部标签导航器具有
从'../views/MaterialTopTabBar'导入MaterialTopTabBar,{类型TabBarOptions,};
MaterialTopTabBar已从react-native-tab-view导入
从'react-native-tab-view'导入{TabBar};
有ScrollView
<Animated.ScrollView
水平的
keyboardShouldPersistTaps =“处理”
该属性keyboardShouldPersistTaps
最初设置为always
,然后又设置为handle,以避免键盘打开时无法按下选项卡栏中任何按钮的错误https://github.com/react-native-community/react-native -tab-view /问题/ 375
但是此TabBar
与我们的问题无关,因为它仅用于包含标签栏按钮。
在createMaterialTopTabNavigator中刷卡
再来看一下createMaterialTopTabNavigator,我们从react-native-tab-view
看到了更多导入
从'react-native-tab-view'导入{TabView,PagerPan};
swipeEnabled
传入了swipeEnabled
返回(
<TabView
{...休息}
navigationState = {navigation.state}
animationEnabled = {animationEnabled}
swipeEnabled = {swipeEnabled}
onAnimationEnd = {this._handleAnimationEnd}
onIndexChange = {this._handleIndexChange}
onSwipeStart = {this._handleSwipeStart}
renderPager = {renderPager}
renderTabBar = {this._renderTabBar}
renderScene = {
/ * $ FlowFixMe * /
this._renderScene
}
/>
);
并呈现PagerDefault,而后者又使用iOS版PagerScroll
从'react-native'导入{Platform};
让Pager;
开关(Platform.OS){
案例“ android”:
Pager = require('./ PagerAndroid')。default;
打破;
案例'ios':
Pager = require('./ PagerScroll')。default;
打破;
默认:
分页器= require('./ PagerPan')。default;
打破;
}
导出默认的寻呼机;
因此,PagerScroll使用ScrollView
处理滚动以匹配用户可以在页面之间滚动的材质样式,并且它具有keyboardShouldPersistTaps=”always”
应该正确。
返回(
<ScrollView
水平的
pagesEnabled
directionalLockEnabled
keyboardDismissMode =“拖曳”
keyboardShouldPersistTaps =“总是”
因此,在react-navigation
,没有什么可疑的,这促使我查看项目中的代码。
调试FlatList,SectionList和ScrollView
就像我在本文开头所述,根本问题是我们需要为层次结构中的所有父ScrollView
声明keyboardShouldPersistTaps
。 这意味着要注意任何FlatList
, SectionList
和ScrollView
幸运的是,有react-devtools
可以显示react应用程序中所有渲染组件的树,并且在react native的Debugging部分中也有介绍。
您可以使用独立版本的React Developer Tools来调试React组件层次结构。 要使用它,请全局安装react-devtools
软件包:
npm install -g react-devtools
因此,在搜索之后,我发现层次结构中有一个SectionList,应该具有keyboardShouldPersistTaps='always'
而没有。