ReactNative【实战系列教程】我的小红书 3 -- 自定义底栏Tab导航(含图片选择 expo-image-picker 的使用)

最终效果

技术要点

自定义 tab

需从 "expo-router/ui" 中导入 TabList, Tabs, TabSlot, TabTrigger 实现

  • Tabs 表示含底栏的页面容器
  • TabList 为整个底栏的容器
  • TabSlot 渲染 tab 路由对应的页面
  • TabTrigger 触发 tab 底栏的路由导航
    • name 属性对应页面文件
    • href 属性对应页面的路由
c 复制代码
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

  1. 获取当前路由
c 复制代码
import { usePathname } from "expo-router";
c 复制代码
const active_href = usePathname();
  1. 根据当前路由,渲染高亮样式
c 复制代码
const active_tab_color = "red";
ts 复制代码
<Text
  style={{
    color:
      tab.href === active_href ? active_tab_color : "black",
  }}
>
  {tab.label}
</Text>

选择图片

安装依赖

c 复制代码
npx expo install expo-image-picker

utils/imagePicker.ts

c 复制代码
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;
  }
};

页面使用

c 复制代码
import { pickImage } from "@/utils/imagePicker";
c 复制代码
  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

因暂无内容,放下方初始模板即可。

c 复制代码
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

c 复制代码
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 (
              <TouchableOpacity
                key={tab.name}
                style={{
                  flex: 1,
                  justifyContent: "center",
                  alignItems: "center",
                  marginHorizontal: 20,
                }}
                onPress={onPublishPress}
              >
                <Image
                  style={{
                    width: 58,
                    height: 42,
                    resizeMode: "contain",
                  }}
                  source={icon_tab_publish}
                />
              </TouchableOpacity>
            );
          } else {
            return (
              <TabTrigger
                key={tab.name}
                name={tab.name}
                href={tab.href as "/"}
                style={{
                  flex: 1,
                  alignItems: "center",
                  justifyContent: "center",
                  padding: 10,
                }}
              >
                <MaterialIcons
                  name={
                    tab.icon as "home" | "shopping-cart" | "message" | "person"
                  }
                  size={24}
                  color={tab.href === active_href ? active_tab_color : "black"}
                />
                <Text
                  style={{
                    color:
                      tab.href === active_href ? active_tab_color : "black",
                  }}
                >
                  {tab.label}
                </Text>
              </TabTrigger>
            );
          }
        })}
      </TabList>
    </Tabs>
  );
}

图片素材

assets/images/icon_tab_publish.png

相关推荐
wen's16 小时前
React Native 0.79.4 中 [RCTView setColor:] 崩溃问题完整解决方案
javascript·react native·react.js
冰冷的bin1 天前
【React Native】自定义倒计时组件CountdownView
react native
朝阳3911 天前
React Native【实用教程】(含图标方案,常用第三库,动画,内置组件,内置Hooks,内置API,自定义组件,创建项目等)
react native
朝阳3912 天前
React Native【实战范例】同步跟随滚动
react native
朝阳3913 天前
React Native【详解】动画
react native
朝阳3914 天前
React Native【详解】内置 API
react native
xx240614 天前
React Native学习笔记
笔记·学习·react native
朝阳3914 天前
React Native【实战范例】弹跳动画菜单导航
react native
草明15 天前
解决: React Native iOS webview 空白页
react native·react.js·ios