标签导航
在移动应用中最常见的导航方式可能是基于标签的导航。标签可以位于屏幕底部或顶部标题下方(甚至可以代替标题)。
本指南涵盖了 createBottomTabNavigator
。您也可以使用 createMaterialBottomTabNavigator
和 createMaterialTopTabNavigator
将标签添加到您的应用程序中。
在继续之前,请先安装 @react-navigation/bottom-tabs
- npm
- Yarn
- pnpm
npm install @react-navigation/bottom-tabs
yarn add @react-navigation/bottom-tabs
pnpm add @react-navigation/bottom-tabs
基于标签的导航的最小示例
import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
function HomeScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
自定义外观
这与您自定义堆栈导航器的方式类似 - 在初始化标签导航器时会设置一些属性,而其他属性可以在 options
中针对每个屏幕进行自定义。
// You can import Ionicons from @expo/vector-icons/Ionicons if you use Expo or
// react-native-vector-icons/Ionicons otherwise.
import Ionicons from 'react-native-vector-icons/Ionicons';
// (...)
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'ios-information-circle'
: 'ios-information-circle-outline';
} else if (route.name === 'Settings') {
iconName = focused ? 'ios-list' : 'ios-list-outline';
}
// You can return any component that you like here!
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: 'tomato',
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
让我们来分析一下
tabBarIcon
是底部标签导航器中支持的选项。因此我们知道可以在屏幕组件的options
属性中使用它,但在这种情况下,我们选择将其放在Tab.Navigator
的screenOptions
属性中,以便为了方便集中图标配置。tabBarIcon
是一个函数,它接收focused
状态、color
和size
参数。如果您进一步查看配置,您将看到tabBarActiveTintColor
和tabBarInactiveTintColor
。这些默认设置为 iOS 平台的默认值,但您可以在此处更改它们。传递给tabBarIcon
的color
是活动或非活动颜色之一,具体取决于focused
状态(focused 表示活动)。size
是标签栏预期的图标大小。- 阅读 完整的 API 参考,以获取有关
createBottomTabNavigator
配置选项的更多信息。
向图标添加徽章
有时我们想向某些图标添加徽章。您可以使用 tabBarBadge
选项 来实现。
<Tab.Screen name="Home" component={HomeScreen} options={{ tabBarBadge: 3 }} />
从 UI 的角度来看,这个组件已经可以使用了,但您仍然需要找到一种方法从其他地方正确传递徽章计数,例如使用 React Context、Redux、MobX 或 事件发射器。
在标签之间跳转
从一个标签切换到另一个标签有一个熟悉的 API - navigation.navigate
。
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home!</Text>
<Button
title="Go to Settings"
onPress={() => navigation.navigate('Settings')}
/>
</View>
);
}
function SettingsScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
</View>
);
}
每个标签的堆栈导航器
标签通常不只显示一个屏幕 - 例如,在您的 Twitter 提要中,您可以点击一条推文,它会将您带到该标签内的一个新屏幕,其中包含所有回复。您可以将其视为每个标签内都有单独的导航堆栈,这正是我们在 React Navigation 中对其进行建模的方式。
import * as React from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
function DetailsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details!</Text>
</View>
);
}
function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
function SettingsScreen({ navigation }) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
const HomeStack = createNativeStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={HomeScreen} />
<HomeStack.Screen name="Details" component={DetailsScreen} />
</HomeStack.Navigator>
);
}
const SettingsStack = createNativeStackNavigator();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Details" component={DetailsScreen} />
</SettingsStack.Navigator>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator screenOptions={{ headerShown: false }}>
<Tab.Screen name="HomeStack" component={HomeStackScreen} />
<Tab.Screen name="SettingsStack" component={SettingsStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
为什么我们需要 TabNavigator 而不是 TabBarIOS 或其他组件?
通常会尝试使用独立的标签栏组件,而无需将其集成到您在应用程序中使用的导航库中。在某些情况下,这可以正常工作!但是,您应该注意,这样做可能会遇到一些令人沮丧的意外问题。
例如,React Navigation 的标签导航器负责为您处理 Android 返回按钮,而独立组件通常不这样做。此外,如果您需要调用两个不同的 API 来执行“跳转到此标签,然后转到此屏幕”等操作,那么您(作为开发人员)执行此类操作会更加困难。最后,移动用户界面具有许多小的设计细节,这些细节要求某些组件了解其他组件的布局或存在 - 例如,如果您有一个半透明的标签栏,内容应该在它下面滚动,并且滚动视图应该在底部有一个缩进,等于标签栏的高度,这样您就可以看到所有内容。双击标签栏应该使活动导航堆栈弹出到堆栈顶部,再次执行此操作应该使该堆栈中活动滚动视图滚动到顶部。虽然并非所有这些行为都已在 React Navigation 中开箱即用地实现,但它们将被实现,而如果您使用独立的标签视图组件,您将无法获得任何这些行为。