布局文件-底部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,},
});