键盘处理
编辑页面
在 Android 或 iOS 设备上处理常见键盘交互的指南。
For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.
键盘处理对于在你的 Expo 应用中创建出色的用户体验至关重要。React Native 提供了 Keyboard 和 KeyboardAvoidingView,它们通常用于处理键盘事件。对于更复杂或自定义的键盘交互,你可以考虑使用 react-native-keyboard-controller,这是一个提供高级键盘处理能力的库。
本指南介绍了常见的键盘交互以及如何有效地管理它们。

在这个 React Native 应用的键盘处理教程中,你将学习如何解决这样一个问题:当你尝试在应用中输入时,键盘遮挡了你的输入框。
键盘处理基础
以下各节将解释如何使用常见 API 处理键盘交互。
键盘避让视图
KeyboardAvoidingView 是一个组件,它会根据键盘高度自动调整视图的高度、位置或底部内边距,以便在键盘显示时保持可见。
Android 和 iOS 对 behavior 属性的交互方式不同。在 iOS 上,padding 通常效果最好;而在 Android 上,仅使用 KeyboardAvoidingView 就能防止输入框被遮挡。这就是下面示例中 Android 使用 undefined 的原因。尝试不同的 behavior 是个好习惯,因为不同的选项可能更适合你的应用。
import { KeyboardAvoidingView, TextInput } from 'react-native'; export default function HomeScreen() { return ( <KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined} style={{ flex: 1 }}> <TextInput placeholder="在这里输入..." /> </KeyboardAvoidingView>; ); }
在上面的示例中,KeyboardAvoidingView 的高度会根据设备键盘的高度自动调整,从而确保输入框始终可见。
在 Android 上使用底部标签栏导航器时,你可能会注意到,当聚焦输入框时,底部标签会被推到键盘上方。要解决这个问题,请在 app 配置 中为你的 Android 配置添加 softwareKeyboardLayoutMode 属性,并将其设置为 pan。
"expo" { "android": { "softwareKeyboardLayoutMode": "pan" } }
添加此属性后,重启开发服务器并重新加载应用以应用更改。
也可以使用 tabBarHideOnKeyboard 在键盘打开时隐藏底部标签栏。这是底部标签栏导航器的一个选项。如果设置为 true,它会在键盘打开时隐藏该栏。
import { Tabs } from 'expo-router'; export default function TabLayout() { return ( <Tabs screenOptions={{ tabBarHideOnKeyboard: true, }}> <Tabs.Screen name="index" /> </Tabs> ); }
键盘事件
React Native 中的 Keyboard 模块允许你监听原生事件、对其做出响应,并对键盘进行操作,例如将其收起。
要监听键盘事件,请使用 Keyboard.addListener 方法。该方法接受一个事件名称和一个回调函数作为参数。当键盘显示或隐藏时,会使用事件数据调用回调函数。
下面的示例展示了添加键盘监听器的一个用例。状态变量 isKeyboardVisible 会在键盘显示或隐藏时切换。基于这个变量,一个按钮只会在键盘处于活动状态时允许用户收起键盘。另请注意,该按钮使用了 Keyboard.dismiss 方法。
import { useEffect, useState } from 'react'; import { Keyboard, View, Button, TextInput } from 'react-native'; export default function HomeScreen() { const [isKeyboardVisible, setIsKeyboardVisible] = useState(false); useEffect(() => { const showSubscription = Keyboard.addListener('keyboardDidShow', handleKeyboardShow); const hideSubscription = Keyboard.addListener('keyboardDidHide', handleKeyboardHide); return () => { showSubscription.remove(); hideSubscription.remove(); }; }, []); const handleKeyboardShow = event => { setIsKeyboardVisible(true); }; const handleKeyboardHide = event => { setIsKeyboardVisible(false); }; return ( <View> {isKeyboardVisible && <Button title="收起键盘" onPress={Keyboard.dismiss} />} <TextInput placeholder="在这里输入..." /> </View> ); }
使用 Keyboard Controller 进行高级键盘处理
对于更复杂的键盘交互,例如带有多个文本输入框的大型可滚动表单,可以考虑使用 react-native-keyboard-controller(Keyboard Controller) 库。它提供了内置 React Native 键盘 API 之外的额外功能,以最少的配置在 Android 和 iOS 之间提供一致性,并带来用户所期待的原生体验。
前提条件
以下步骤使用的是 开发构建,因为 Keyboard Controller 库不包含在 Expo Go 中。更多信息请参见 创建开发构建。
Keyboard Controller 还需要 react-native-reanimated 才能正常工作。要安装它,请按照这些 安装说明 操作。
安装
先在你的 Expo 项目中安装 Keyboard Controller 库:
- npx expo install react-native-keyboard-controller设置提供者
为了完成设置,将 KeyboardProvider 添加到你的应用中。
import { Stack } from 'expo-router'; import { KeyboardProvider } from 'react-native-keyboard-controller'; export default function RootLayout() { return ( <KeyboardProvider> <Stack> <Stack.Screen name="home" /> <Stack.Screen name="chat" /> </Stack> </KeyboardProvider> ); }
处理多个输入框
KeyboardAvoidingView 组件非常适合原型开发,但它需要针对平台的配置,而且可定制性不强。
作为一个更强大的替代方案,你可以使用 KeyboardAwareScrollView 组件。它会自动滚动到当前聚焦的 TextInput,并提供类似原生的性能。对于只有少量元素的简单页面,使用 KeyboardAwareScrollView 是一个很好的方案。
对于包含多个输入框的页面,Keyboard Controller 库还提供了 KeyboardToolbar 组件,可与 KeyboardAwareScrollView 一起使用。结合起来,这些组件可以处理输入导航,并且无需自定义配置即可防止键盘遮挡屏幕:
import { TextInput, View, StyleSheet } from 'react-native'; import { KeyboardAwareScrollView, KeyboardToolbar } from 'react-native-keyboard-controller'; export default function FormScreen() { return ( <> <KeyboardAwareScrollView bottomOffset={62} contentContainerStyle={styles.container}> <View> <TextInput placeholder="输入一条消息..." style={styles.textInput} /> <TextInput placeholder="输入一条消息..." style={styles.textInput} /> </View> <TextInput placeholder="输入一条消息..." style={styles.textInput} /> <View> <TextInput placeholder="输入一条消息..." style={styles.textInput} /> <TextInput placeholder="输入一条消息..." style={styles.textInput} /> <TextInput placeholder="输入一条消息..." style={styles.textInput} /> </View> <TextInput placeholder="输入一条消息..." style={styles.textInput} /> </KeyboardAwareScrollView> <KeyboardToolbar /> </> ); } const styles = StyleSheet.create({ container: { gap: 16, padding: 16, }, listStyle: { padding: 16, gap: 16, }, textInput: { width: 'auto', flexGrow: 1, flexShrink: 1, height: 45, borderWidth: 1, borderRadius: 8, borderColor: '#d8d8d8', backgroundColor: '#fff', padding: 8, marginBottom: 8, }, });
上面的示例使用 KeyboardAwareScrollView 包裹输入框,以防止键盘遮挡它们。KeyboardToolbar 组件会显示导航控件和一个收起按钮。虽然它无需配置即可工作,但如果需要,你也可以自定义工具栏内容。
让视图动画与键盘高度同步
对于更高级且可定制的方式,你可以使用 useKeyboardHandler。它提供对键盘生命周期事件的访问。它允许我们确定键盘何时开始动画,以及在动画的每一帧中它所在的位置。
使用 useKeyboardHandler 钩子,你可以创建一个自定义钩子,在每一帧访问键盘的高度。它使用 reanimated 中的 useSharedValue 返回该高度,如下所示。
import { useKeyboardHandler } from 'react-native-keyboard-controller'; import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; const useGradualAnimation = () => { const height = useSharedValue(0); useKeyboardHandler( { onMove: event => { 'worklet'; height.value = Math.max(event.height, 0); }, }, [] ); return { height }; };
你可以使用 useGradualAnimation 钩子来为视图添加动画,并在键盘处于活动状态或被收起时给予平滑的动画效果,例如在聊天页面组件中(如下例所示)。这个组件从钩子中获取键盘高度。然后,它使用 reanimated 的 useAnimatedStyle 钩子创建一个名为 fakeView 的动画样式。这个样式只包含一个属性:height,其值设置为键盘的高度。
fakeView 动画样式被用于 TextInput 后面的一个动画视图中。这个视图的高度会随着键盘在每一帧的高度变化而动画,从而有效地将内容平滑地推到键盘上方。当键盘被收起时,它的高度也会减小到零。
import { StyleSheet, Platform, FlatList, View, StatusBar, TextInput } from 'react-native'; import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated'; import { useKeyboardHandler } from 'react-native-keyboard-controller'; import MessageItem from '@/components/MessageItem'; import { messages } from '@/messages'; const useGradualAnimation = () => { %%placeholder-start%%// 代码与前一个示例相同 %%placeholder-end%% }; export default function ChatScreen() { const { height } = useGradualAnimation(); const fakeView = useAnimatedStyle(() => { return { height: Math.abs(height.value), }; }, []); return ( <View style={styles.container}> <FlatList data={messages} renderItem={({ item }) => <MessageItem message={item} />} keyExtractor={item => item.createdAt.toString()} contentContainerStyle={styles.listStyle} /> <TextInput placeholder="输入一条消息..." style={styles.textInput} /> <Animated.View style={fakeView} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0, }, listStyle: { padding: 16, gap: 16, }, textInput: { width: '95%', height: 45, borderWidth: 1, borderRadius: 8, borderColor: '#d8d8d8', backgroundColor: '#fff', padding: 8, alignSelf: 'center', marginBottom: 8, }, });
其他资源
在 GitHub 上查看示例项目的源代码。
react-native-keyboard-controller有关 Keyboard Controller 库的更多详情,请参阅文档。