布局文件-底部tabBar
内容配置
export default function Layout() {return (<Tabs />);
}
默认会将布局文件是将与它在同一个目录的所有文件,包括下级目录的文件,全都配置成Tab
了。:
这样做显然不对,正确的做法是
- 在
app
目录里新建一个(tabs)
文件夹,注意了,名字上有一对小括号。 - 里面新建一个
_layout.js
布局文件,这里就专门放TabBar
配置。 - 然后将
index.js
,挪动到(tabs)
里面。 - 在
(tabs)
里,再新建一个videos.js
和users.js
文件。
app/_layout.js
import { Stack } from 'expo-router';export default function Layout() {return (<StackscreenOptions={{title: '', // 默认标题为空headerTitleAlign: 'center', // 安卓标题栏居中animation: 'slide_from_right', // 安卓使用左右切屏headerTintColor: '#1f99b0', // 导航栏中文字、按钮、图标的颜色headerTitleStyle: { // 标题组件的样式fontWeight: '400',color: '#2A2929',fontSize: 16,},headerBackButtonDisplayMode: 'minimal', // 设置返回按钮只显示箭头,不显示文字}}>{/* Tabs */}<Stack.Screen name="(tabs)" options={{ headerShown: false }} />{/* Cards */}<Stack.Screen name="articles/index" options={{ title: '通知' }} /><Stack.Screen name="settings/index" options={{ title: '设置' }} /><Stack.Screen name="courses/[id]" options={{ title: '课程详情' }} /><Stack.Screen name="search/index" options={{ title: '搜索' }} /></Stack>);
}
-
TabBar
的配置:- 注意这里有个
headerShown
,这是因为TabBar
也会自带一个导航栏。 - 如果不隐藏,它会和
Stack
的导航栏同时出现,这就会出来两个导航栏了。
- 注意这里有个
-
底下给各个页面都添加上了
title
。 -
在
Stack
里页面是有两种形式的:- 这种页面左右滑动跳转的就叫
Cards
。 - 另一种页面从屏幕底部弹出的,叫做
模态(Modal)
。
- 这种页面左右滑动跳转的就叫
app/(tabs)/_layout.js
import { Link, Tabs } from 'expo-router'
import { Image } from 'expo-image'
import { SimpleLineIcons } from '@expo/vector-icons'
import { StyleSheet, TouchableOpacity } from 'react-native'/*** 导航栏 Logo 组件*/
function LogoTitle() {return <Image style={style.logo} contentFit="contain" source={require('../../assets/logo-light.png')}/>
}/*** 导航栏按钮组件* @param props*/
function HeaderButton({ href, ...rest }) {return (<Link href={href} asChild><TouchableOpacity><SimpleLineIcons size={20} color="#1f99b0" {...rest} /></TouchableOpacity></Link>)
}export default function TabsLayout() {return (<TabsscreenOptions={{headerTitleAlign: 'center', // 安卓标题栏居中headerTitle: props => <LogoTitle {...props} />,headerLeft: () => <HeaderButton name="bell" href="/articles" style={style.headerLeft} />,headerRight: () => (<><HeaderButton name="magnifier" href="/search" style={style.headerRight} /><HeaderButton name="options" href="/settings" style={style.headerRight} /></>),}}><Tabs.Screenname="index"options={{ title: '首页' }}/><Tabs.Screenname="videos"options={{ title: '视频课程' }}/><Tabs.Screenname="users"options={{ title: '我的' }}/></Tabs>);
}const style = StyleSheet.create({logo: {width: 130,height: 30,},headerLeft: {marginLeft: 15,},headerRight: {marginRight: 15,},
});
(tabs)
目录,专门用来放各个Tab
页。名字上的这个小括号
,叫做路由分组
:
-
利用它,将一些相关的文件,放在一起。
-
这种带
小括号
的目录名,在URL
里不计算路径!index.js
的URL
,依然还是/index
,就像还在app
目录里一样,它依然还是首页
。videos.js
文件的URL
,其实是/videos
,而不是/(tabs)/videos
,同理。
-
底下的和刚才不同,刚才的布局文件里使用的是
Stack
、Stack.Screen
。这里要用Tabs
和Tabs.Screen
。 -
然后将顶部的按钮,配置到了最外层的
screenOptions
里,这样所有的Tab
页在导航栏上,都会有Logo
和按钮。 -
从
Tab
页,是可以随意跳转到非Tab
页的。从哪里都能跳过去,它就属于共享路由。 -
一个项目里,也可以有多个布局文件,布局文件只对和它同级或下级文件生效。
图标和样式
在上面的基础上增加图标和样式:
/*** TabBar 图标组件* @param props*/function TabBarIcon(props) {return <SimpleLineIcons size={25} {...props} />;}<Tabs . Screenname="users"options={{title: "我的",tabBarIcon: ({ color }) => <TabBarIcon name="user" color={color} />,}}/><TabsscreenOptions={{headerTitleAlign: "center", // 安卓标题栏居中headerTitle: (props) => <LogoTitle {...props} />,headerLeft: () => (<HeaderButton name="bell" href="/articles" style={style.headerLeft} />),headerRight: () => (<><HeaderButtonname="magnifier"href="/search"style={style.headerRight}/><HeaderButtonname="options"href="/settings"style={style.headerRight}/></>),tabBarActiveTintColor: "#1f99b0", // 设置 TabBar 选中项的颜色tabBarStyle: {height: 80, // 设置 TabBar 的高度},tabBarLabelStyle: {marginTop: 4, // 设置 TabBar 文字与图标之间的间距},}}>
完整代码:
import { Link, Tabs } from "expo-router";
import { Image } from "expo-image";
import { SimpleLineIcons } from "@expo/vector-icons";
import { StyleSheet, TouchableOpacity } from "react-native";/*** 导航栏 Logo 组件*/
function LogoTitle() {return (<Imagestyle={style.logo}contentFit="contain"source={require("../../assets/logo-light.png")}/>);
}/*** TabBar 图标组件* @param props*/
function TabBarIcon(props) {return <SimpleLineIcons size={25} {...props} />;
}/*** 导航栏按钮组件* @param props*/
function HeaderButton({ href, ...rest }) {return (<Link href={href} asChild><TouchableOpacity><SimpleLineIcons size={20} color="#1f99b0" {...rest} /></TouchableOpacity></Link>);
}export default function TabsLayout() {return (<TabsscreenOptions={{headerTitleAlign: "center", // 安卓标题栏居中headerTitle: (props) => <LogoTitle {...props} />,headerLeft: () => (<HeaderButton name="bell" href="/articles" style={style.headerLeft} />),headerRight: () => (<><HeaderButtonname="magnifier"href="/search"style={style.headerRight}/><HeaderButtonname="options"href="/settings"style={style.headerRight}/></>),tabBarActiveTintColor: "#1f99b0", // 设置 TabBar 选中项的颜色tabBarStyle: {height: 80, // 设置 TabBar 的高度},tabBarLabelStyle: {marginTop: 4, // 设置 TabBar 文字与图标之间的间距},}}><Tabs.Screenname="index"options={{title: "发现",tabBarIcon: ({ color }) => (<TabBarIcon name="compass" color={color} />),}}/><Tabs.Screenname="videos"options={{title: "视频课程",tabBarIcon: ({ color }) => (<TabBarIcon name="camrecorder" color={color} />),}}/><Tabs.Screenname="users"options={{title: "我的",tabBarIcon: ({ color }) => <TabBarIcon name="user" color={color} />,}}/></Tabs>);
}const style = StyleSheet.create({logo: {width: 130,height: 30,},headerLeft: {marginLeft: 15,},headerRight: {marginRight: 15,},
});