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

React Native 选项卡视图

React Native 选项卡视图是一个跨平台的选项卡视图组件,用于 React Native,它使用 react-native-pager-view 在 Android 和 iOS 上实现,并使用 PanResponder 在 Web、macOS 和 Windows 上实现。

它默认遵循 Material Design 指南,但您也可以使用自己的自定义选项卡栏或将选项卡栏放置在底部。

React Native Tab View Demo

此包不与 React Navigation 集成。如果您想将选项卡视图与 React Navigation 的导航系统集成,例如,想要在选项卡栏中显示屏幕并能够使用 navigation.navigate 等在它们之间导航,请使用 Material Top Tab Navigator 代替。

安装

要使用此包,请在项目根目录中打开一个终端并运行

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 数组中活动路由索引的数字
  • routes: 包含用于渲染标签的路由对象列表的数组

每个路由对象应包含以下属性

  • key: 用于标识路由的唯一键(必填)
  • title: 在标签栏中显示的路由标题
  • icon: 在标签栏中显示的路由图标
  • accessibilityLabel: 标签按钮的可访问性标签
  • testID: 标签按钮的测试 ID

示例

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

TabView 是一个受控组件,这意味着需要通过 onIndexChange 回调更新 index

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 进行优化,并且在父级组件的 props 或状态发生变化时不会重新渲染。如果您需要更细粒度地控制场景的更新方式(例如,即使 navigationState 未发生变化也要触发重新渲染),请直接使用 renderScene 而不是使用 SceneMap

重要: 不要 将内联函数传递给 SceneMap,例如,不要执行以下操作

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

始终在文件顶层定义您的组件。如果您传递内联函数,它将在每次渲染时重新创建组件,这会导致整个路由在每次更改时卸载和重新挂载。这会严重影响性能,还会导致任何局部状态丢失。

如果您需要传递其他 props,请使用自定义 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} />}
...
/>

如果未指定,则会渲染默认的选项卡栏。您可以传递这些 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` 属性也需要启用。

此视图通常只显示几秒钟。保持轻量级。

默认情况下,这将渲染 `null`。

keyboardDismissMode

字符串,指示键盘是否会响应拖动手势而消失。可能的取值为

  • 'auto'(默认):当索引更改时,键盘会消失。
  • 'on-drag':当拖动开始时,键盘会消失。
  • 'none':拖动不会使键盘消失。
swipeEnabled

布尔值,指示是否启用滑动手势。默认情况下启用滑动手势。传递 `false` 将禁用滑动手势,但用户仍然可以通过按下标签栏来切换标签。

animationEnabled

在更改标签时启用动画。默认情况下为 true。

onSwipeStart

回调函数,在滑动手势开始时调用,即用户触摸屏幕并移动它。

onSwipeEnd

回调函数,在滑动手势结束时调用,即用户在滑动手势后从屏幕上抬起手指。

initialLayout

包含屏幕初始高度和宽度的对象。传递此对象将提高初始渲染性能。对于大多数应用程序,这是一个很好的默认值。

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

用于覆盖分页器 overScroll 模式默认值。可以是 autoalwaysnever(仅限 Android)。

sceneContainerStyle

应用于包装每个屏幕的视图的样式。您可以传递此样式以覆盖一些默认样式,例如溢出裁剪。

pagerStyle

应用于包装所有场景的分页器视图的样式。

style

应用于选项卡视图容器的样式。

TabBar

Material Design 主题选项卡栏。要自定义选项卡栏,您需要使用 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

一个函数,它接收一个包含当前路由的对象,并返回一个布尔值,以指示是否将选项卡标记为 accessible。默认值为 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

布尔值,指示是否使选项卡栏可滚动。

如果将 scrollEnabled 设置为 true,则还应在 tabStyle 中指定 width 以改善初始渲染。

bounces

布尔值,指示选项卡栏在滚动时是否反弹。

tabStyle

要应用于选项卡栏中各个选项卡项的样式。

默认情况下,所有选项卡项都占用基于容器宽度预先计算的相同宽度。如果希望它们占用原始宽度,可以在 tabStyle 中指定 width: 'auto'

indicatorStyle

要应用于活动指示器的样式。

indicatorContainerStyle

要应用于指示器容器视图的样式。

labelStyle

要应用于选项卡项标签的样式。

contentContainerStyle

要应用于选项卡内部容器的样式。

style (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);

避免一帧延迟

我们需要测量容器的宽度,因此需要等待才能在屏幕上渲染一些元素。如果你事先知道初始宽度,可以将其传入,我们就不需要等待测量它。大多数情况下,它只是窗口宽度。

例如,将以下 initialLayout 传递给 TabView

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

TabView 嵌套在垂直 ScrollView 中会禁用 TabView 内部渲染的 FlatList 组件中的优化。因此,如果可能,请避免这样做。

使用 lazyrenderLazyPlaceholder 属性按需渲染路由

lazy 选项默认情况下处于禁用状态,以提供更流畅的选项卡切换体验,但你可以启用它并提供一个占位符组件以获得更好的延迟加载体验。启用 lazy 可以通过仅在路由进入视图时渲染它们来提高初始加载性能。有关更多详细信息,请参阅 属性参考