跳至主要內容
版本:6.x

React Native 標籤檢視

React Native 標籤檢視是一個跨平台標籤檢視元件,適用於使用 react-native-pager-view 在 Android 和 iOS 上及 PanResponder 在 Web、macOS 和 Windows 上,實作的 React Native。

它預設遵循材質設計指南,但你也可以使用自己的自訂標籤列或將標籤列放置在底部。

React Native Tab View Demo

此套件未整合 React 導航。如果你想將標籤檢視與 React 導航的導航系統整合,例如:在標籤列中顯示畫面,並能夠使用 navigation.navigate 等方式在它們之間導航,請改用 材質頂端 tab 導航器

安裝

若要使用此套件,請在專案根目錄中開啟終端機,並執行

npm install react-native-tab-view

接下來,如果你打算支援 iOS 和 Android,請安裝 react-native-pager-view

如果你使用的是 Expo,請執行以下操作,以確保取得相容的函式庫版本

expo install react-native-pager-view

如果你未在使用 Expo,請執行下列操作

npm install react-native-pager-view

完成了!現在你可以在裝置/模擬器上建構和執行應用程式。

快速入門

import * as React from 'react';
import { View, useWindowDimensions } from 'react-native';
import { TabView, SceneMap } from 'react-native-tab-view';

const FirstRoute = () => (
<View style={{ flex: 1, backgroundColor: '#ff4081' }} />
);

const SecondRoute = () => (
<View style={{ flex: 1, backgroundColor: '#673ab7' }} />
);

const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});

export default function TabViewExample() {
const layout = useWindowDimensions();

const [index, setIndex] = React.useState(0);
const [routes] = React.useState([
{ key: 'first', title: 'First' },
{ key: 'second', title: 'Second' },
]);

return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={{ width: layout.width }}
/>
);
}

在 Snack 上嘗試此範例

在 Snack 上的更多範例

API 參考

此套件中有一個 TabView 元件,可讓您用於呈現標籤檢視,以及一個 TabBar 元件,是預設的標籤列實作。

TabView

負責呈現和管理標籤的容器元件。預設遵循 Material Design 的樣式。

基本用法的範例如下

<TabView
navigationState={{ index, routes }}
onIndexChange={setIndex}
renderScene={SceneMap({
first: FirstRoute,
second: SecondRoute,
})}
/>

TabView 屬性

標籤檢視的狀態。狀態應包含下列屬性

  • index:代表 routes 陣列中 active 路由索引的數字
  • routes:包含路徑物件清單的陣列,可用於呈現標籤

各個路徑物件應包含下列屬性

  • key:用於識別路由的唯一金鑰 (必填)
  • title:在標籤列中顯示的路徑標題
  • icon:在標籤列中顯示的路徑圖示
  • accessibilityLabel:標籤按鈕的輔助技術標籤
  • testID:標籤按鈕的測試識別碼

範例

{
index: 1,
routes: [
{ key: 'music', title: 'Music' },
{ key: 'albums', title: 'Albums' },
{ key: 'recents', title: 'Recents' },
{ key: 'purchased', title: 'Purchased' },
]
}

TabView 是個受控元件,也就是說 index 需透過 onIndexChange 回呼更新。

onIndexChange (必填)

標籤變更時會呼叫的回呼,並接收新標籤索引作為引數。呼叫後必須更新瀏覽狀態,否則會中斷變更。

renderScene (必填)

回呼,傳回一個 react 元素,作為標籤的頁面呈現。接收一個物件,其中包含作為引數的路徑

const renderScene = ({ route, jumpTo }) => {
switch (route.key) {
case 'music':
return <MusicRoute jumpTo={jumpTo} />;
case 'albums':
return <AlbumsRoute jumpTo={jumpTo} />;
}
};

您需確保個別路由實作 shouldComponentUpdate 才能提升效能。您可以使用 SceneMap 輔助函式輕鬆指定元件。

SceneMap 會將物件取用 route.key 對應的 React 元件,並傳回一個函式以搭配 renderScene 屬性使用。

import { SceneMap } from 'react-native-tab-view';

...

const renderScene = SceneMap({
music: MusicRoute,
albums: AlbumsRoute,
});

這個方式可以更輕鬆地指定元件,並負責實作 shouldComponentUpdate 方法。

各場景接收下列屬性

  • route:元件呈現的目前路由
  • jumpTo:跳至其他標籤的方法,並將 route.key 作為引數
  • position:表示目前位置的動畫節點

jumpTo 方法可程式化地導覽至其他分頁

props.jumpTo('albums');

SceneMap 呈現的所有場景都使用 React.PureComponent 來最佳化,且在父項的道具或狀態變更時不會重新產生。如果你想進一步控制場景的更新方式(例如:就算 navigationState 沒有變更也要觸發重新產生),請直接使用 renderScene,而不要透過 SceneMap

重要提示:傳遞內嵌函式給 SceneMap,例如請不要執行下列操作

SceneMap({
first: () => <FirstRoute foo={props.foo} />,
second: SecondRoute,
});

請始終在檔案的頂層定義你的元件。如果你傳遞內嵌函式,則會在每次產生時重新建立元件,這將導致整個路由在每次變更時都解除安裝接著重新安裝。這對效能很不好,同時也會造成任何區域狀態遺失。

如果你需要傳遞其他道具,請使用自訂的 renderScene 函式

const renderScene = ({ route }) => {
switch (route.key) {
case 'first':
return <FirstRoute foo={this.props.foo} />;
case 'second':
return <SecondRoute />;
default:
return null;
}
};
renderTabBar

傳回要作為分頁列使用的自訂 React 元素的呼叫回函

import { TabBar } from 'react-native-tab-view';

...

<TabView
renderTabBar={props => <TabBar {...props} />}
...
/>

如果這裡未指定,則會產生預設的頁籤列。你可以傳遞這些道具來自訂預設的頁籤列、提供你自己的頁籤列,或徹底停用頁籤列。

<TabView
renderTabBar={() => null}
...
/>
tabBarPosition

在分頁檢視中的分頁列位置。可能的值是 'top''bottom'。預設值為 'top'

lazy

取得包含目前路由的物件,並傳回布林值以指出是否延遲產生場景的函式。

預設會產生所有場景,以提供更流暢的滑動體驗。不過你可能會想延後處理未聚焦的場景,直到使用者看到它們為止。如要為特定場景啟用延遲產生,請針對該 routegetLazy 傳回 true

<TabView
lazy={({ route }) => route.name === 'Albums'}
...
/>

當你為畫面啟用延遲產生時,當它變成焦點時通常需要一些時間才能產生。你可以使用 renderLazyPlaceholder 道具自訂使用者在這段短時間內看到什麼。

你也可以傳遞一個布林值來為所有場景啟用延遲產生

<TabView lazy />
lazyPreloadDistance

當啟用 lazy 時,你可以藉由這個道具指定預載多少相鄰的路由。這個值的預設值為 0,表示延遲載入的頁面會在它們進入視窗時載入。

renderLazyPlaceholder

呼叫會傳回自訂 React 元素,以產生尚未呈現的路由。會收到包含路由的物件作為引數。還需要啟用 [`lazy`](/docs/lazy-loading-routes) 屬性。

此畫面通常只顯示一小段時間。請維持小巧。

預設會產生 `null`。

keyboardDismissMode

字串,表示是否在拖曳手勢下關閉鍵盤。可能的數值為

  • 'auto' (預設值):在索引變更時關閉鍵盤。
  • 'on-drag':在拖曳開始時關閉鍵盤。
  • 'none':拖曳不關閉鍵盤。
swipeEnabled

布林值,表示是否啟用滑動手勢。滑動手勢預設為啟用。傳遞 `false` 會停用滑動手勢,但使用者仍可透過按下分頁欄切換分頁。

animationEnabled

在變更分頁時啟用動畫。預設為 true。

onSwipeStart

在滑動手勢開始時呼叫的呼叫回,即使用者觸碰並移動過螢幕時。

onSwipeEnd

在滑動手勢結束時呼叫的呼叫回,即在使用者於滑動手勢後放開螢幕時。

initialLayout

包含各個畫面的初始高度及寬度的物件。傳遞此物件會改善初始呈現效能。對於大多數 app 來說,這是個不錯的預設值。

<TabView
initialLayout={{ width: Dimensions.get('window').width }}
...
/>
overScrollMode

用於覆寫分頁器的 overScroll 模式預設值。可為 `auto`、`always` 或 `never` (僅限 Android)。

sceneContainerStyle

套用到包裝每個畫面的檢視的樣式。可以傳遞此樣式來覆寫一些預設樣式,例如溢位裁剪

pagerStyle

套用到包裝所有場景的分頁器檢視的樣式。

style

套用至分頁檢視容器的樣式。

TabBar

遵循素材設計的主題標籤欄。若要自訂標籤欄,您需要使用 TabViewrenderTabBar 屬性來渲染 TabBar 並傳遞額外的屬性。

例如,若要自訂指示器顏色和標籤欄背景顏色,您可以分別傳遞 indicatorStylestyle 屬性給 TabBar

const renderTabBar = props => (
<TabBar
{...props}
indicatorStyle={{ backgroundColor: 'white' }}
style={{ backgroundColor: 'pink' }}
/>
);

//...


return (
<TabView
renderTabBar={renderTabBar}
...
/>
);

TabBar 屬性

getLabelText

傳遞具有目前路由的物件的函數,並傳回標籤的標籤文字。預設使用 route.title

<TabBar
getLabelText={({ route }) => route.title}
...
/>
getAccessible

傳遞具有目前路由的物件的函數,並傳回一個布林值以指示是否將標籤標示為 可存取。預設為 true

getAccessibilityLabel

傳遞具有目前路由的物件的函數,並傳回標籤按鈕的可存取性標籤。預設使用 route.accessibilityLabel(如果已指定),否則使用路由標題。

<TabBar
getAccessibilityLabel={({ route }) => route.accessibilityLabel}
...
/>
getTestID

傳遞具有目前路由的物件的函數,並傳回標籤按鈕的測試 ID,以在各項測試中找到此標籤按鈕。預設使用 route.testID

<TabBar
getTestID={({ route }) => route.testID}
...
/>
renderIcon

傳遞具有目前路由、焦點狀態和顏色的物件的函數,並傳回作為圖示要使用的自訂 React 元素。

<TabBar
renderIcon={({ route, focused, color }) => (
<Icon
name={focused ? 'albums' : 'albums-outlined'}
color={color}
/>
)}
...
/>
renderLabel

傳遞具有目前路由、焦點狀態和顏色的物件的函數,並傳回作為標籤要使用的自訂 React 元素。

<TabBar
renderLabel={({ route, focused, color }) => (
<Text style={{ color, margin: 8 }}>
{route.title}
</Text>
)}
...
/>
renderTabBarItem

傳遞 TabBarItemProps 物件的函數,並傳回作為標籤按鈕要使用的自訂 React 元素。

renderIndicator

傳遞具有目前路由的物件的函數,並傳回作為標籤指示器要使用的自訂 React 元素。

renderBadge

傳遞具有目前路由的物件的函數,並傳回作為徽章要使用的自訂 React 元素。

onTabPress

在標籤按下時執行的函數。它接收按下之標籤的場景,可用於執行類似捲動至頂端的動作。

預設情況下,按下標籤也會切換標籤。若要避免這個行為,可以呼叫 preventDefault

<TabBar
onTabPress={({ route, preventDefault }) => {
if (route.key === 'home') {
preventDefault();

// Do something else
}
}}
...
/>
onTabLongPress

在標籤長時間按下時執行的函數,可作為顯示包含更多選項之功能表等用途

activeColor

在作用中標籤中圖示與標籤的客製化顏色。

inactiveColor

在非作用中標籤中圖示與標籤的客製化顏色。

pressColor

材質漣漪的顏色 (僅針對 Android >= 5.0)。

pressOpacity

按下的標籤的不透明度 (僅針對 iOS 和 Android < 5.0)。

scrollEnabled

布林值,指示是否要讓標籤列可捲動。

如果您設定 scrollEnabledtrue,您也應該在 tabStyle 中指定 width,以提升初始呈現。

bounces

布林值,指示捲動時標籤列是否會反彈。

tabStyle

套用至標籤列中個別標籤項目之樣式。

預設情況下,所有標籤項目會使用相同、根據容器寬度事先計算的寬度。如果您希望讓它們使用原始寬度,您可以在 tabStyle 中指定 width: 'auto'

indicatorStyle

套用至作用中指示器的樣式。

indicatorContainerStyle

套用至指示器之容器檢視的樣式。

labelStyle

套用至標籤項目標籤的樣式。

contentContainerStyle

套用至標籤內部容器的樣式。

樣式 (TabBar)

套用至分頁工具列容器的樣式。

gap

定義分頁間隔。

testID

分頁工具列的測試 ID。可於測試中用於捲動分頁工具列

最佳化秘訣

避免不必要的再繪製

renderScene 函數會在每次索引變更時呼叫。如果您的 renderScene 函數負擔很重,務必將每個路由移至個別組件中(前提是它們不依賴於索引),並在路由組件中使用 shouldComponentUpdateReact.memo 以避免不必要的再繪製。

例如,取代

const renderScene = ({ route }) => {
switch (route.key) {
case 'home':
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
default:
return null;
}
};

採用下列做法

const renderScene = ({ route }) => {
switch (route.key) {
case 'home':
return <HomeComponent />;
default:
return null;
}
};

其中 <HomeComponent /> 是一份 PureComponent(若您使用的是類別組件)

export default class HomeComponent extends React.PureComponent {
render() {
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
}
}

或者是,封裝在 React.memo 中(若您使用的是函式組件)

function HomeComponent() {
return (
<View style={styles.page}>
<Avatar />
<NewsFeed />
</View>
);
}

export default React.memo(HomeComponent);

避免單禎延遲

我們需要測量容器的寬度,因此在畫面中彩現某些元素前需要等待。如果您事先知道最初的寬度,您可以將其傳入,那我們就不需要等待測量了。在大部分情況下,它僅是視窗寬度。

例如,傳遞下列 initialLayoutTabView

const initialLayout = {
height: 0,
width: Dimensions.get('window').width,
};

分頁檢視仍會因應大小變更做出反應,並適當地調整以容納方向變更等情況。

最佳化大量路由

如果您有大量的路由,特別是影像,這可能會顯著減緩動畫速度。您可以改為僅繪製有限數量的路由。

例如,採用下列做法僅在每側繪製 2 個路由

const renderScene = ({ route }) => {
if (Math.abs(index - routes.indexOf(route)) > 2) {
return <View />;
}

return <MySceneComponent route={route} />;
};

避免在 ScrollView 內部繪製 TabView

在垂直 ScrollView 內部嵌套 TabView 會停用 TabView 內部所繪製的 FlatList 組件中的最佳化。因此,請盡可能避免這類做法。

使用 lazyrenderLazyPlaceholder 屬性依需要繪製路由

lazy 選項在預設情況下停用,以提供更順暢的標籤切換體驗,但你可以啟用它,並提供一個佔位元件以獲得更好的延遲載入體驗。啟用 lazy 可以透過在路徑顯示後才渲染路徑,來改善初始載入效能。有關更多詳細資訊,請參閱 prop 參考