This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
在 Android 或 iOS 设备上处理常见键盘交互的指南。
键盘处理会影响 Expo 应用的用户体验。React Native 提供了 Keyboard 和 KeyboardAvoidingView,它们通常用于处理键盘事件。对于更复杂或自定义的键盘交互,可以考虑使用 react-native-keyboard-controller,这是一个提供高级键盘处理功能的库。
本指南介绍了常见的键盘交互以及如何有效地管理它们。
React Native 应用的键盘处理教程在这个 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 上使用底部标签栏导航器时,你可能会注意到,当聚焦输入框时,底部标签会被推到键盘上方。要解决这个问题,请在 应用配置 中为你的 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 之间提供一致性,并带来用户所期待的原生体验。
1.
开发构建
Keyboard Controller 库不包含在 Expo Go 中。有关更多信息,请参阅创建开发构建。
2.
已安装 react-native-reanimated
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,
},
});
其他资源
react-native-keyboard-controller有关 Keyboard Controller 库的更多详情,请参阅文档。