前言
自己本身也是一个刚接触react-native
的新手,甚至对于RN
的书写也是基于函数组件来写搭配react hooks
的钩子函数,在配合react-navigation5.x
的最新版本慢慢熟路整个开发。这样搭配是对我这样的新手来说最舒服的。
写这个文档,也是基于网上很少关于react-navigation5.x
的信息,也是借这个机会和大家分享一下自己的学习经历(欢迎讨论,指错,一起进步)
前期准备
- 你得了解过React(这是基本的),安装好RN的开发环境,并且可以跑起来.
- 安装最新版本的
react-navigation5.x
,这里面有点小坑,这是我之前写的安装过程(juejin.cn/post/684490…),凑乎着看看-。-。 - emm..跑起来
React-navigation5.x的createBottomTabNavigator
React-navigation5.x
有很多导航的方式,今天就介绍一种(我就看了这一种)
官方地址
reactnavigation.org/docs/bottom…
安装(没错还要安装)
npm install @react-navigation/bottom-tabs
引入官方示例
//router.js
//注意引入
import 'react-native-gesture-handler';
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="My" component={SettingsScreen} />
</Tab.Navigator>
);
}
//别忘把你的router.js 在index.js中引入
//只要能跑起来就行,每个人都有自己的写法
import {AppRegistry} from 'react-native';
import App from './router';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
大概这样(下面的文字不一样(Home,My))
Tab.Navigator
属性
- initialRouteName:初始化时(第一次需要显示的页面)路由的名称,没有的话按顺序(第一个作为首先显示的页面)
<Tab.Navigator initialRouteName="Home">
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="My" component={SettingsScreen} />
</Tab.Navigator>
- screenOptions:作用于屏幕的选项(
createStackNavigator
中的这个属性是对自定义标题栏的修改)
To set a custom header for all the screens in the navigator, you can specify this option in the screenOptions prop of the navigator
- backBehavior:后退的按钮处理行为
- initialRoute :返回初始的选项卡
- order:返回上一个选项卡(按照他们的顺序)
- history:返回上次访问的选项卡
- none :无操作行为
<Tab.Navigator backBehavior="none">
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="My" component={SettingsScreen} />
</Tab.Navigator>
- lazy:懒加载(啥时候激活啥时候渲染,默认为true)
- true
- false
- tabBar:自定义导航栏(不满足他们的样式,自己定义样式和事件),他是一个方
法,返回的是React element
,本文会进行梳理此属性(这里不做过多解释)
-
tabBarOptions:对底部导航栏的组件的选项管理
- activeTintColor:图标和文字的选中的颜色
- activeBackgroundColor:当前所选中标签的背景色
- inactiveTintColor:没有选中的图标和文字的颜色
- inactiveBackgroundColor :没有选中的标签背景色
- showLabel :是否显示标签的文字
- showIcon :是否显示icon图标
- style :选项卡的样式对象(可以定义添加样式)
- labelStyle :对标签的文字的样式修改(字体啥的)
- labelPosition :icon图标和文字的位置
- beside-icon:左右排列
- below-icon:上下排列
- tabStyle:单个标签的样式对象(和
style
属性的区别就是一个单个作用范围,一个整个底部的范围) - allowFontScaling :标签文字的是否支持缩放,适应屏幕(默认为true)
- adaptive :标签图标和文字的对齐是否应该根据屏幕大小而改变?iOS 11默认为true。如果是假的,选项卡图标和标签会一直垂直对齐。如果是,则选项卡图标和标签在tablet上水平对齐
- safeAreaInset :安全区域(在使用带有缺口的屏幕时[iPhoneX],保证内容可以显示出来)
Override the forceInset prop for . Defaults to { bottom: 'always', top: 'never' }. Available keys are top | bottom | left | right provided with the values 'always' | 'never'
再附一个地址对SafeAreaView的介绍使用
- keyboardHidesTabBar:是否在键盘弹起时隐藏底部tab栏 (默认false)
Tab.Screen
的options属性- title:通用标题(headerTitle和tabBarLabel的备用)
- tabBarVisible:是否显示此标签(默认为true)
- tabBarIcon:标签的自定义icon,我这边是图片,返回一个
React.Node
参数{focused: boolean, color: string, size: number}
//IndexIcon function IndexIcon(props) { if (props.focused) { return ( <Image source={require('./assets/images/home.png')} style={props.style}></Image> ) } else { return ( <Image source={require('./assets/images/no_home.png')} style={props.style}></Image> ) } }
<Tab.Screen name="Index" component={Index} options={{ tabBarLabel: '主页', tabBarIcon: ({ focused,color, size }) => ( <IndexIcon style={{ width: size, height: size }} focused={focused} /> ) }} title='666' />
- tabBarLabel:标签文字(可以是字符串,也可以是React.node)
- tabBarButton:可以自定义单个标签栏,包括图标和文字,返回的是
React element
- tabBarTestID:标签定位的id
- unmountOnBlur:当切换标签时是否保留当前屏幕的状态(默认为false)
Events
- tabPress:标签栏点击事件监听
React.useEffect(() => {
const unsubscribe = navigation.addListener('tabPress', e => {
// Prevent default behavior
e.preventDefault();
// Do something manually
// ...
});
return unsubscribe;
}, [navigation]);
- tabLongPress:长按事件监听
React.useEffect(() => {
const unsubscribe = navigation.addListener('tabLongPress', e => {
// Do something
});
return unsubscribe;
}, [navigation]);
自定义(定制)底部标签栏
如果对于官方提供的底部标签的UI不满意,可以进行定制化修改,也就是Tab.Navigator
的tabBar
属性
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
//MyTabBar部分
function MyTabBar({ state, descriptors, navigation }) {
//state,descriptors
return (
<View style={{ flexDirection: 'row' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const isFocused = state.index === index;
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const onPress=function(){...}
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityStates={isFocused ? ['selected'] : []}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
style={{ flex: 1, borderWidth: 1, borderColor: 'red' }}
onPress={onPress}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
打印出state的值
{
"stale": false,
"type": "tab",
"key": "tab-n3rBjaO1X",
"index": 0,
"routeNames": [
"Index",
"My"
],
"history": [
{
"type": "route",
"key": "Index-k0BnDQf7M"
}
],
"routes": [
{
"name": "Index",
"key": "Index-k0BnDQf7M"
},
{
"name": "My",
"key": "My-5atljuCzM"
}
]
}
打印descriptors的值
{
"Index-k0BnDQf7M": {
"navigation": {},
"options": {
"title": "6666",
"headerStyle": {
"height": 10
},
"tabBarLabel": "主页"
}
},
"My-5atljuCzM": {
"navigation": {},
"options": {
"title": "6666",
"headerStyle": {
"height": 10
},
"tabBarLabel": "我的"
}
}
}
so,这两个值打印出来之后,我们就可以从这两个值里面取出我们需要的部分,现在展示出来的样子大概是这样(有点丑,不过没关系,样式啥可以自己发挥)
接下来就是点击事件这个东西
function MyTabBar({ state, descriptors, navigation }) {
//state,descriptors
return (
<View style={{ flexDirection: 'row' }}>
{state.routes.map((route, index) => {
...
const onPress = () => {
//给当前点击的标签发送```tabPress```事件
const event = navigation.emit({
type: 'tabPress',
target: route.key,
});
//判断是否已经在当前标签页面
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name);
}
};
})}
</View>
);
}