摘要:在本指南中,我將向你演示如何創建自定義標簽欄以并與一起使用。我們將導入并使用創建默認選項卡導航器。接下來,我們將添加實際的自定義標簽欄組件。例如,當前的實現假設選項卡導航器中總會有個,聚光燈顏色在選項卡欄組件中是寫死。
如果你覺得 React Navigation 默認 Tab 組件看起來太平淡,或者想創造一些更現代的東西,那么你想法就和我一樣。 在本指南中,我將向你演示如何創建自定義標簽欄以并與 React Navigation 一起使用。
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你!
源碼已發布到 github,如果有需要,請點擊這里。
這是最終完成的樣子:
首先,讓我們初始化一個新項目并安裝幾個依賴項。在終端運行如下命令:
$ react-native init CustomTabBar $ cd CustomTabBar $ npm install react-navigation react-native-gesture-handler react-native-pose
React Navigation 從 V3 開始需要依賴 react-native-gesture-handler 庫,react-native-pose 是一個很棒的庫,我們將用它來制作非常簡單的動畫。
react-native-gesture-handler 需要通過 link 命令將一些配置自動關聯到原生中。
react-native link react-native-gesture-handler
現在我們可以啟動應用程序了。
首先——我們創建如下一個目錄結構,方便代碼管理:
/android /ios ... /src /AppEntry.js /router /router.js /index.js /components /screens /index.js
首先,我們將創建一個 src 目錄,將我們的代碼與項目根目錄中的其他文件(package.json,app.json,.gitignore 等)分開。 screens,components 和 router 目錄是知名其意的。
我們從項目的根目錄中刪除默認的App.js文件,并在 index.js 中寫入import /src/AppEntry.js
/* /index.js */ import { AppRegistry } from "react-native"; import App from "./src/AppEntry"; import { name as appName } from "./app.json"; AppRegistry.registerComponent(appName, () => App);
現在我們想要使用 react-navigation 創建路由器,但是首先我們需要創建一些 screen(就是頁面)。我們將創建一個通用的 Screen 組件,它接受一個名稱并顯示它來模擬多個 Screen。
在 /src/screens/index.js 添加如下內容:
/* /src/screens/index.js */ import React from "react" import Screen from "./Screen" export const HomeScreen = () =>export const SearchScreen = () => export const FavoritesScreen = () => export const ProfileScreen = () => ;
現在我們創建 Screen 組件。
/* /src/screens/Screen.js */ import React from "react"; import { Text, View, StyleSheet } from "react-native"; const S = StyleSheet.create({ container: { flex: 1, backgroundColor: "#bbbbbb", justifyContent: "center", alignItems: "center" }, text: { fontSize: 28, color: "#222222", textAlign: "center" } }); const Screen = ({ name }) => (); export default Screen; This is the "{name}" screen
接著創建路由,首先在 /src/router/index.js 在添加如下內容:
/* /src/router/index.js */ export { default as Router } from "./router";
現在讓我們在 router.js 中創建基本的 BottomTabNavigator。 我們將導入 screens 并使用createBottomTabNavigator 創建默認選項卡導航器。
/* /src/router/router.js */ import { createAppContainer, createBottomTabNavigator } from "react-navigation"; import { HomeScreen, SearchScreen, FavoritesScreen, ProfileScreen } from "../screens"; const TabNavigator = createBottomTabNavigator({ HomeScreen, SearchScreen, FavoritesScreen, ProfileScreen }); export default createAppContainer(TabNavigator);
現在我們在 AppEntry.js 中渲染路由:
/* /src/AppEntry.js */ import React from "react"; import { Router } from "./router"; export default () =>;
當我們重新加載應用程序時,應該會如下內容:
默認標簽欄支持圖標,我們將在本教程中使用 ascii 字符,當然在實際應用中可以使用 react-native-vector-icons 或自定義圖標字體。
讓我們創建一個 Icon 組件,接受參數為 name 和 color 并返回圖標。
/* /src/components/index.js */ export { default as Icon } from "./Icon";
/ /src/components/Icon.js /
import React from "react"; import { Text } from "react-native"; const iconMap = { home: "?", search: "?", favorites: "?", profile: "?" }; const Icon = ({ name, color, style, ...props }) => { const icon = iconMap[name]; return{icon} ; }; export default Icon;
現在我們可以在路由器中使用這個組件。我們在 router.js 中更改 screens ,以接受帶有navigationOptions 配置的對象。默認選項卡欄將 tintColor 傳遞給圖標組件,因此我們使用它來設置圖標顏色。
/* /src/router/router.js */ import { createAppContainer, createBottomTabNavigator } from "react-navigation"; import React from "react"; import { HomeScreen, SearchScreen, FavoritesScreen, ProfileScreen } from "../screens"; import {Icon} from "../components" const TabNavigator = createBottomTabNavigator({ HomeScreen: { screen: HomeScreen, navigationOptions: { tabBarIcon: ({ tintColor }) =>} }, SearchScreen: { screen: SearchScreen, navigationOptions: { tabBarIcon: ({ tintColor }) => } }, FavoritesScreen: { screen: FavoritesScreen, navigationOptions: { tabBarIcon: ({ tintColor }) => } }, ProfileScreen: { screen: ProfileScreen, navigationOptions: { tabBarIcon: ({ tintColor }) => } } }); export default createAppContainer(TabNavigator);
運行效果如下:
現在我們的標簽欄看起來好一點,但它仍然是 react-navigation 的默認標簽欄。 接下來,我們將添加實際的自定義標簽欄組件。
讓我們從創建一個自定義 TabBar 組件開始,該組件只渲染一些文本并打印傳遞過來的 props ,這樣我們就可以看到我們從導航器中得到了什么 props。
/* /src/components/index.js */ export { default as Icon } from "./Icon"; export { default as TabBar } from "./TabBar";
/* /src/components/TabBar.js */ import React from "react"; import { Text } from "react-native"; const TabBar = props => { console.log("Props", props); returnCustom Tab Bar ; }; export default TabBar;
使用自定義標簽欄需要配置 createBottomTabNavigator 第二個參數, 我們可以添加以下配置作為createBottomTabNavigator 的第二個參數。
如果我們查看標簽欄打印了什么,我們會看到導航欄中有 navigation.state狀態,其中也包含路由。還有 renderIcon 函數,onTabPress 和很多我們可能需要的東西。此外,我們還注意到我們在路由器配置中 tabBarOptions 是如何被注入到組件中的。
現在重新編寫 TabBar 組件。首先,讓我們嘗試重新創建默認選項卡欄。我們將在容器上設置一些樣式,以便將選項卡按鈕排成一行,并為每個路由呈現一個選項卡按鈕。我們可以使用 renderIcon 函數來渲染正確的圖標——通過查看源代碼,該函數需要傳入一個對象參數: { route, focused, tintColor }。我們添加了onPress 處理程序、易訪問性標簽,這樣就有了默認的選項卡欄。
/* /src/components/TabBar.js */ import React from "react"; import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; const S = StyleSheet.create({ container: { flexDirection: "row", height: 52, elevation: 2 }, tabButton: { flex: 1, justifyContent: "center", alignItems: "center" } }); const TabBar = props => { const { renderIcon, getLabelText, activeTintColor, inactiveTintColor, onTabPress, onTabLongPress, getAccessibilityLabel, navigation } = props; const { routes, index: activeRouteIndex } = navigation.state; return ({routes.map((route, routeIndex) => { const isRouteActive = routeIndex === activeRouteIndex; const tintColor = isRouteActive ? activeTintColor : inactiveTintColor; return ( ); }; export default TabBar;{ onTabPress({ route }); }} onLongPress={() => { onTabLongPress({ route }); }} accessibilityLabel={getAccessibilityLabel({ route })} > {renderIcon({ route, focused: isRouteActive, tintColor })} ); })}{getLabelText({ route })}
運行后,效果如下:
現在我們知道我們可以靈活地創建自己的標簽欄,因此我們可以開始實際擴展它。 我們將使用 react-native-pose 創建一個動畫視圖,該視圖將突出顯示活動路徑 - 我們將此視圖稱為聚光燈。
首先我們可以去掉標簽。然后我們在標簽欄后面添加一個絕對視圖,它將顯示聚光燈效果。我們使用Dimensions API 計算聚光燈的偏移量。
/* /src/components/TabBar.js */ import React from "react"; import { View, Text, StyleSheet, TouchableOpacity, Dimensions } from "react-native"; import posed from "react-native-pose"; const windowWidth = Dimensions.get("window").width; const tabWidth = windowWidth / 4; const SpotLight = posed.View({ route0: { x: 0 }, route1: { x: tabWidth }, route2: { x: tabWidth * 2 }, route3: { x: tabWidth * 3 } }); const S = StyleSheet.create({ container: { flexDirection: "row", height: 52, elevation: 2 }, tabButton: { flex: 1, justifyContent: "center", alignItems: "center" }, spotLight: { width: tabWidth, height: "100%", backgroundColor: "rgba(128,128,255,0.2)", borderRadius: 8 } }); const TabBar = props => { const { renderIcon, getLabelText, activeTintColor, inactiveTintColor, onTabPress, onTabLongPress, getAccessibilityLabel, navigation } = props; const { routes, index: activeRouteIndex } = navigation.state; return (); }; export default TabBar; {routes.map((route, routeIndex) => { const isRouteActive = routeIndex === activeRouteIndex; const tintColor = isRouteActive ? activeTintColor : inactiveTintColor; return ( { onTabPress({ route }); }} onLongPress={() => { onTabLongPress({ route }); }} accessibilityLabel={getAccessibilityLabel({ route })} > {renderIcon({ route, focused: isRouteActive, tintColor })} ); })}{getLabelText({ route })}
運行效果如下:
請注意,我們從未指定動畫的持續時間和行為, Pos e負責使用合理的默認值。
現在我們將為選中圖標添加一些縮放:
/* /src/components/TabBar.js */ ... const Scaler = posed.View({ active: { scale: 1.25 }, inactive: { scale: 1 } }); ...
現在我們可以像這樣將圖標包裝在 Scaler 組件中。
/* /src/components/TabBar.js */{renderIcon({ route, focused: isRouteActive, tintColor })}
運行效果如下:
我們的標簽欄開始看起來很不錯。 剩下要做的就是稍微改善一下,改變配色方案,調整我們的聚光燈,我們的組件就完成了。
現在,我們可以在這里改進一些事情。 例如,當前的實現假設選項卡導航器中總會有 4 個 Screen,聚光燈顏色在選項卡欄組件中是寫死。樣式應該通過路由器上的 tabBarOptions 配置進行動態編寫的,這邊不會講這些,大家自己動手做做。
TabBar 組件的完整代碼:
/* /src/components/TabBar.js */ import React from "react"; import { View, Text, StyleSheet, TouchableOpacity, Dimensions } from "react-native"; import posed from "react-native-pose"; const windowWidth = Dimensions.get("window").width; const tabWidth = windowWidth / 4; const SpotLight = posed.View({ route0: { x: 0 }, route1: { x: tabWidth }, route2: { x: tabWidth * 2 }, route3: { x: tabWidth * 3 } }); const Scaler = posed.View({ active: { scale: 1.25 }, inactive: { scale: 1 } }); const S = StyleSheet.create({ container: { flexDirection: "row", height: 52, elevation: 2, alignItems: "center" }, tabButton: { flex: 1 }, spotLight: { width: tabWidth, height: "100%", justifyContent: "center", alignItems: "center" }, spotLightInner: { width: 48, height: 48, backgroundColor: "#ee0000", borderRadius: 24 }, scaler: { flex: 1, alignItems: "center", justifyContent: "center" } }); const TabBar = props => { const { renderIcon, activeTintColor, inactiveTintColor, onTabPress, onTabLongPress, getAccessibilityLabel, navigation } = props; const { routes, index: activeRouteIndex } = navigation.state; return (); }; export default TabBar; {routes.map((route, routeIndex) => { const isRouteActive = routeIndex === activeRouteIndex; const tintColor = isRouteActive ? activeTintColor : inactiveTintColor; return ( { onTabPress({ route }); }} onLongPress={() => { onTabLongPress({ route }); }} accessibilityLabel={getAccessibilityLabel({ route })} > ); })}{renderIcon({ route, focused: isRouteActive, tintColor })}
路由器配置如下:
/ /src/router/router.js /
... const TabNavigator = createBottomTabNavigator( /* screen config ommited */, { tabBarComponent: TabBar, tabBarOptions: { activeTintColor: "#eeeeee", inactiveTintColor: "#222222" } } );
你的點贊是我持續分享好東西的動力,歡迎點贊!
歡迎加入前端大家庭,里面會經常分享一些技術資源。文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103561.html
閱讀 3289·2023-04-26 00:57
閱讀 605·2021-10-08 10:05
閱讀 1353·2021-09-08 09:36
閱讀 4164·2021-08-12 13:31
閱讀 2549·2019-08-30 15:55
閱讀 2241·2019-08-30 15:55
閱讀 1020·2019-08-30 15:55
閱讀 2689·2019-08-29 13:17