ReactNative【实战系列教程】我的小红书 3 -- 自定义底栏Tab导航(含图片选择 expo-image-picker 的使用)
最终效果
技术要点
自定义 tab
需从 “expo-router/ui” 中导入 TabList, Tabs, TabSlot, TabTrigger 实现
- Tabs 表示含底栏的页面容器
- TabList 为整个底栏的容器
- TabSlot 渲染 tab 路由对应的页面
- TabTrigger 触发 tab 底栏的路由导航
- name 属性对应页面文件
- href 属性对应页面的路由
import { Tabs, TabList, TabTrigger, TabSlot } from 'expo-router/ui';
import { Text } from 'react-native';// Defining the layout of the custom tab navigator
export default function Layout() {return (<Tabs><TabSlot /><TabList><TabTrigger name="home" href="/"><Text>Home</Text></TabTrigger><TabTrigger name="article" href="/article"><Text>Article</Text></TabTrigger></TabList></Tabs>);
}
在 TabTrigger 内自由设计每个 tab 项的元素和样式。
更多详情可参考官网
高亮选中的 tab
- 获取当前路由
import { usePathname } from "expo-router";
const active_href = usePathname();
- 根据当前路由,渲染高亮样式
const active_tab_color = "red";
<Textstyle={{color:tab.href === active_href ? active_tab_color : "black",}}
>{tab.label}
</Text>
选择图片
安装依赖
npx expo install expo-image-picker
utils/imagePicker.ts
import * as ImagePicker from "expo-image-picker";
import { Alert } from "react-native";
// 请求相机胶卷权限
export const requestGalleryPermission = async (): Promise<boolean> => {const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();if (status !== "granted") {Alert.alert("权限拒绝", "需要相机胶卷权限才能选择图片");return false;}return true;
};
// 从相机胶卷选择图片
export const pickImage = async (): Promise<string | undefined> => {const hasPermission = await requestGalleryPermission();if (!hasPermission) return;const result = await ImagePicker.launchImageLibraryAsync({mediaTypes: "images",allowsEditing: true,aspect: [4, 3],quality: 1,});if (!result.canceled && result.assets?.length > 0) {return result.assets[0].uri;}
};
// 请求相机权限
export const requestCameraPermission = async (): Promise<boolean> => {const { status } = await ImagePicker.requestCameraPermissionsAsync();if (status !== "granted") {Alert.alert("权限拒绝", "需要相机权限才能拍照");return false;}return true;
};
// 使用相机拍照
export const takePhoto = async (): Promise<string | undefined> => {const hasPermission = await requestCameraPermission();if (!hasPermission) return;const result = await ImagePicker.launchCameraAsync({mediaTypes: "images",allowsEditing: true,aspect: [4, 3],quality: 1,});if (!result.canceled && result.assets?.length > 0) {return result.assets[0].uri;}
};
页面使用
import { pickImage } from "@/utils/imagePicker";
const onPublishPress = async () => {const imageUri = await pickImage();if (imageUri) {console.log("选择的图片的URI:", imageUri);}};
代码实现
创建各 tab 对应的页面
- app/(tabs)/index.tsx
- app/(tabs)/message.tsx
- app/(tabs)/mine.tsx
- app/(tabs)/shop.tsx
因暂无内容,放下方初始模板即可。
import { StyleSheet, Text, View } from "react-native";
export default function IndexScreen() {return (<View style={styles.page}><Text>首页</Text></View>);
}
const styles = StyleSheet.create({page: {},
});
app/(tabs)/_layout.tsx
import icon_tab_publish from "@/assets/images/icon_tab_publish.png";
import { pickImage } from "@/utils/imagePicker";
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import { usePathname } from "expo-router";
import { TabList, Tabs, TabSlot, TabTrigger } from "expo-router/ui";
import React from "react";
import { Image, Text, TouchableOpacity } from "react-native";
export default function TabLayout() {const active_href = usePathname();const active_tab_color = "red";const tabs = [{href: "/",name: "index",label: "首页",icon: "home",},{href: "/shop",name: "shop",label: "购物",icon: "shopping-cart",},{href: "/publish",name: "publish",label: "发布",icon: "publish",},{href: "/message",name: "message",label: "消息",icon: "message",},{href: "/mine",name: "mine",label: "我",icon: "person",},];const onPublishPress = async () => {const imageUri = await pickImage();if (imageUri) {console.log("Selected image URI:", imageUri);}};return (<Tabs><TabSlot /><TabList>{tabs.map((tab, index: number) => {if (index === 2) {return (<TouchableOpacitykey={tab.name}style={{flex: 1,justifyContent: "center",alignItems: "center",marginHorizontal: 20,}}onPress={onPublishPress}><Imagestyle={{width: 58,height: 42,resizeMode: "contain",}}source={icon_tab_publish}/></TouchableOpacity>);} else {return (<TabTriggerkey={tab.name}name={tab.name}href={tab.href as "/"}style={{flex: 1,alignItems: "center",justifyContent: "center",padding: 10,}}><MaterialIconsname={tab.icon as "home" | "shopping-cart" | "message" | "person"}size={24}color={tab.href === active_href ? active_tab_color : "black"}/><Textstyle={{color:tab.href === active_href ? active_tab_color : "black",}}>{tab.label}</Text></TabTrigger>);}})}</TabList></Tabs>);
}
图片素材
assets/images/icon_tab_publish.png