
TabNavigator 即 Tab 选项卡
TabNavigator(RouteConfigs, TabNavigatorConfig)
- 1
api和 StackNavigator 类似,参数 RouteConfigs 是路由配置,参数 TabNavigatorConfig是Tab选项卡配置。
如果要实现底部选项卡切换功能,可以直接使用react-navigation提供的createBottomTabNavigator接口,并且此导航器需要使用createAppContainer函数包裹后才能作为React组件被正常调用。例如:
import React, {PureComponent} from 'react';
import {StyleSheet, Image} from 'react-native';
import {createAppContainer, createBottomTabNavigator} from 'react-navigation'
import Home from './tab/HomePage'
import Mine from './tab/MinePage'
const BottomTabNavigator = createBottomTabNavigator(
{
Home: {
screen: Home,
navigationOptions: () => ({
tabBarLabel: '首页',
tabBarIcon:({focused})=>{
if(focused){
return(
<Image/> //选中的图片
)
}else{
return(
<Image/> //默认图片
)
}
}
}),
},
Mine: {
screen: Mine,
navigationOptions: () => ({
tabBarLabel: '我的',
tabBarIcon:({focused})=>{
…
}
})
}
}, { //默认参数设置
initialRouteName: 'Home',
tabBarPosition: 'bottom',
showIcon: true,
showLabel: true,
pressOpacity: 0.8,
tabBarOptions: {
activeTintColor: 'green',
style: {
backgroundColor: '#fff',
},
}
}
);
const AppContainer = createAppContainer(BottomTabNavigator);
export default class TabBottomNavigatorPage extends PureComponent {
render() {
return (
<AppContainer/>
);
}
}
RouteConfigs 路由配置
路由配置和 StackNavigator 中是一样的,配置路由以及对应的 screen 页面,navigationOptions 为对应路由页面的配置选项:
title-Tab标题,可用作headerTitle和tabBarLabel回退标题;tabBarVisible-Tab是否可见,没有设置的话默认为true;tabBarIcon-Tab的icon组件,可以根据{focused:boolean, tintColor: string}方法来返回一个icon组件;tabBarLabel-Tab中显示的标题字符串或者组件,也可以根据{ focused: boolean, tintColor: string };方法返回一个组件;
代码示例:
Mine: {
screen: MineScene,
navigationOptions: ({ navigation }) => ({
tabBarLabel: '我的',
tabBarIcon: ({ focused, tintColor }) => (
<TabBarItem
tintColor={tintColor}
focused={focused}
normalImage={require('./img/tabbar/pfb_tabbar_mine@2x.png')}
selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected@2x.png')}
/>
)
}),
},
TabBarItem自定义组件
class TabBarItem extends PureComponent {
render() {
let selectedImage = this.props.selectedImage ? this.props.selectedImage : this.props.normalImage
return (
<Image
source={this.props.focused
? selectedImage
: this.props.normalImage}
style={{ tintColor: this.props.tintColor, width: 25, height: 25 }}
/>
);
}
}
TabNavigatorConfig Tab选项卡配置
-
tabBarComponent- Tab选项卡组件,有TabBarBottom和TabBarTop两个值,在iOS中默认为TabBarBottom,在Android中默认为TabBarTop。TabBarTop- 在页面顶部;TabBarBottom- 在页面底部;
-
tabBarPosition- Tab选项卡的位置,有top或bottom两个值top:上面bottom:下面
-
swipeEnabled- 是否可以滑动切换Tab选项卡; -
animationEnabled- 点击Tab选项卡切换界面是否需要动画; -
lazy- 是否懒加载页面; -
initialRouteName- 初始显示的Tab对应的页面路由名称; -
order- 用路由名称数组来表示Tab选项卡的顺序,默认为路由配置顺序; -
paths- 路径配置; -
backBehavior-android点击返回键时的处理,有initialRoute和none两个值:initailRoute- 返回初始界面;none- 退出;
-
tabBarOptions- Tab配置属性,用在TabBarTop和TabBarBottom时有些属性不一致:
用于 TabBarTop 时:
activeTintColor- 选中的文字颜色;inactiveTintColor- 未选中的文字颜色;showIcon-是否显示图标,默认显示;showLabel- 是否显示标签,默认显示;upperCaseLabel- 是否使用大写字母,默认使用;pressColor-android 5.0以上的MD风格波纹颜色;pressOpacity-android5.0以下或者iOS按下的透明度;scrollEnabled- 是否可以滚动;tabStyle- 单个Tab的样式;indicatorStyle- 指示器的样式;labelStyle- 标签的样式;iconStyle-icon的样式;style-整个TabBar的样式;
用于 TabBarBottom 时:
activeTintColor- 选中Tab的文字颜色;inactiveTintColor- 未选中Tab的的文字颜色;activeBackgroundColor- 选中Tab的背景颜色;inactiveBackgroundColor-未选中Tab的背景颜色;showLabel- 是否显示标题,默认显示;style- 整个TabBar的样式;labelStyle-标签的样式;tabStyle- 单个Tab的样式;
使用底部选项卡:
import React, {Component} from 'react';
import {StackNavigator, TabBarBottom, TabNavigator} from "react-navigation";
import HomeScreen from "./index18/HomeScreen";
import NearByScreen from "./index18/NearByScreen";
import MineScreen from "./index18/MineScreen";
import TabBarItem from "./index18/TabBarItem";
export default class MainComponent extends Component {
render() {
return (
<Navigator/>
);
}
}
const TabRouteConfigs = {
Home: {
screen: HomeScreen,
navigationOptions: ({navigation}) => ({
tabBarLabel: '首页',
tabBarIcon: ({focused, tintColor}) => (
<TabBarItem
tintColor={tintColor}
focused={focused}
normalImage={require('./img/tabbar/pfb_tabbar_homepage_2x.png')}
selectedImage={require('./img/tabbar/pfb_tabbar_homepage_selected_2x.png')}
/>
),
}),
},
NearBy: {
screen: NearByScreen,
navigationOptions: {
tabBarLabel: '附近',
tabBarIcon: ({focused, tintColor}) => (
<TabBarItem
tintColor={tintColor}
focused={focused}
normalImage={require('./img/tabbar/pfb_tabbar_merchant_2x.png')}
selectedImage={require('./img/tabbar/pfb_tabbar_merchant_selected_2x.png')}
/>
),
},
}
,
Mine: {
screen: MineScreen,
navigationOptions: {
tabBarLabel: '我的',
tabBarIcon: ({focused, tintColor}) => (
<TabBarItem
tintColor={tintColor}
focused={focused}
normalImage={require('./img/tabbar/pfb_tabbar_mine_2x.png')}
selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected_2x.png')}
/>
),
},
}
};
const TabNavigatorConfigs = {
initialRouteName: 'Home',
tabBarComponent: TabBarBottom,
tabBarPosition: 'bottom',
lazy: true,
};
const Tab = TabNavigator(TabRouteConfigs, TabNavigatorConfigs);
const StackRouteConfigs = {
Tab: {
screen: Tab,
}
};
const StackNavigatorConfigs = {
initialRouteName: 'Tab',
navigationOptions: {
title: '标题',
headerStyle: {backgroundColor: '#5da8ff'},
headerTitleStyle: {color: '#333333'},
}
};
const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);
使用顶部选项卡:
import React, {Component} from "react";
import {StackNavigator, TabBarTop, TabNavigator} from "react-navigation";
import HomeScreen from "./index18/HomeScreen";
import NearByScreen from "./index18/NearByScreen";
import MineScreen from "./index18/MineScreen";
export default class MainComponent extends Component {
render() {
return (
<Navigator/>
);
}
}
const TabRouteConfigs = {
Home: {
screen: HomeScreen,
navigationOptions: ({navigation}) => ({
tabBarLabel: '首页',
}),
},
NearBy: {
screen: NearByScreen,
navigationOptions: {
tabBarLabel: '附近',
},
}
,
Mine: {
screen: MineScreen,
navigationOptions: {
tabBarLabel: '我的',
},
}
};
const TabNavigatorConfigs = {
initialRouteName: 'Home',
tabBarComponent: TabBarTop,
tabBarPosition: 'top',
lazy: true,
tabBarOptions: {}
};
const Tab = TabNavigator(TabRouteConfigs, TabNavigatorConfigs);
const StackRouteConfigs = {
Tab: {
screen: Tab,
}
};
const StackNavigatorConfigs = {
initialRouteName: 'Tab',
navigationOptions: {
title: '标题',
headerStyle: {backgroundColor: '#5da8ff'},
headerTitleStyle: {color: '#333333'},
}
};
const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);
当然,除了支持创建底部选项卡之外,react-navigation还支持创建顶部选项卡,此时只需要使用react-navigation提供的createMaterialTopTabNavigator即可。如果要使用实现抽屉式菜单功能,还可以使用react-navigation提供的createDrawerNavigator。
DrawerNavigator 抽屉导航
有一些APP都会采用侧滑抽屉来实现主页面导航,利用 DrawerNavigator 在RN中可以很方便实现抽屉导航。
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
- 1
和TabNavigator的构造函数一样,参数配置也类似。
RouteConfigs
抽屉导航的路由配置 RouteConfigs ,和 TabNavigator 的路由配置完全一样, screen 对应路由页面配置, navigationOptions 对应页面的抽屉配置:
title- 抽屉标题,和headerTitle、drawerLabel一样;drawerLabel-标签字符串,或者自定义组件, 可以根据{ focused: boolean, tintColor: string }函数来返回一个自定义组件作为标签;drawerIcon- 抽屉icon,可以根据{ focused: boolean,tintColor: string }函数来返回一个自定义组件作为icon;
DrawerNavigatorConfig 属性配置
drawerWidth- 抽屉宽度,可以使用Dimensions获取屏幕宽度,实现动态计算;drawerPosition-抽屉位置,可以是left或者right;contentComponent- 抽屉内容组件,可以自定义侧滑抽屉中的所有内容,默认为DrawerItems;contentOptions- 用来配置抽屉内容的属性。当用来配置DrawerItems是配置属性选项:
items- 抽屉栏目的路由名称数组,可以被修改;activeItemKey- 当前选中页面的key id;activeTintColor- 选中条目状态的文字颜色;activeBackgroundColor- 选中条目的背景色;inactiveTintColor- 未选中条目状态的文字颜色;inactiveBackgroundColor- 未选中条目的背景色onItemPress(route)- 条目按下时会调用此方法;style- 抽屉内容的样式;labelStyle-抽屉的条目标题/标签样式;initialRouteName- 初始化展示的页面路由名称;order-抽屉导航栏目顺序,用路由名称数组表示;paths- 路径;backBehavior-android点击返回键时的处理,有initialRoute和none两个值:
- initailRoute:返回初始界面;
- none :退出
抽屉导航示例:
import React, {Component} from 'react';
import {DrawerNavigator, StackNavigator, TabBarBottom, TabNavigator} from "react-navigation";
import HomeScreen from "./index18/HomeScreen";
import NearByScreen from "./index18/NearByScreen";
import MineScreen from "./index18/MineScreen";
import TabBarItem from "./index18/TabBarItem";
export default class MainComponent extends Component {
render() {
return (
<Navigator/>
);
}
}
const DrawerRouteConfigs = {
Home: {
screen: HomeScreen,
navigationOptions: ({navigation}) => ({
drawerLabel : '首页',
drawerIcon : ({focused, tintColor}) => (
<TabBarItem
tintColor={tintColor}
focused={focused}
normalImage={require('./img/tabbar/pfb_tabbar_homepage_2x.png')}
selectedImage={require('./img/tabbar/pfb_tabbar_homepage_selected_2x.png')}
/>
),
}),
},
NearBy: {
screen: NearByScreen,
navigationOptions: {
drawerLabel : '附近',
drawerIcon : ({focused, tintColor}) => (
<TabBarItem
tintColor={tintColor}
focused={focused}
normalImage={require('./img/tabbar/pfb_tabbar_merchant_2x.png')}
selectedImage={require('./img/tabbar/pfb_tabbar_merchant_selected_2x.png')}
/>
),
},
},
Mine: {
screen: MineScreen,
navigationOptions: {
drawerLabel : '我的',
drawerIcon : ({focused, tintColor}) => (
<TabBarItem
tintColor={tintColor}
focused={focused}
normalImage={require('./img/tabbar/pfb_tabbar_mine_2x.png')}
selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected_2x.png')}
/>
),
},
}
};
const DrawerNavigatorConfigs = {
initialRouteName: 'Home',
tabBarComponent: TabBarBottom,
tabBarPosition: 'bottom',
lazy: true,
tabBarOptions: {}
};
const Drawer = DrawerNavigator(DrawerRouteConfigs, DrawerNavigatorConfigs);
const StackRouteConfigs = {
Drawer: {
screen: Drawer,
}
};
const StackNavigatorConfigs = {
initialRouteName: 'Drawer',
navigationOptions: {
title: '标题',
headerStyle: {backgroundColor: '#5da8ff'},
headerTitleStyle: {color: '#333333'},
}
};
const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);
扩展功能
默认DrawerView不可滚动。要实现可滚动视图,必须使用contentComponent自定义容器:
{
drawerWidth:200,
抽屉位置:“对”
contentComponent:props => <ScrollView>
<DrawerItems {... props}></DrawerItems> </ ScrollView>
}
可以覆盖导航使用的默认组件,使用DrawerItems自定义导航组件:
import {DrawerItems} from 'react-navigation';
const CustomDrawerContentComponent = (props) => (
<View style = {style.container}>
<DrawerItems {... props} />
</View>
);
嵌套抽屉导航
如果嵌套DrawerNavigation,抽屉将显示在父导航下方。
自定义react-navigation适配顶部导航栏标题
测试中发现,在iphone上标题栏的标题为居中状态,而在Android上则是居左对齐。所以需要修改源码,进行适配。【node_modules – react-navigation – src – views – Header.js】的326行代码处,修改为如下:
title: {
bottom: 0,
left: TITLE_OFFSET,
right: TITLE_OFFSET,
top: 0,
position: 'absolute',
alignItems: 'center',
}
上面方法通过修改源码的方式其实略有弊端,毕竟扩展性不好。还有另外一种方式就是,在navigationOptions中设置headerTitleStyle的alignSelf为 ’ center ‘即可解决。
去除返回键文字显示
【node_modules – react-navigation – src – views – HeaderBackButton.js】的91行代码处,修改为如下即可。
{
Platform.OS === 'ios' &&
title &&
<Text
onLayout={this._onTextLayout}
style={[styles.title, { color: tintColor }]}
numberOfLines={1}
>
{backButtonTitle}
</Text>
}
当我们在头部设置左右按钮时,肯定避免不了要设置按钮的单击事件,但是此时会有一个问题,navigationOptions是被修饰为static类型的,所以在按钮的onPress方法中不能直接通过this来调用Component中的方法。如何解决呢?
在官方文档中,作者给出利用设置params的思想来动态设置头部标题。我们可以利用这种方式,将单击回调函数以参数的方式传递到params,然后在navigationOption中利用navigation来取出,并设置到onPress即可:
componentDidMount () {
/**
* 将单击回调函数作为参数传递
*/
this.props.navigation.setParams({
switch: () => this.switchView()
});
}
/**
* 切换视图
*/
switchView() {
alert('切换')
}
static navigationOptions = ({navigation,screenProps}) => ({
headerTitle: '企业服务',
headerTitleStyle: CommonStyles.headerTitleStyle,
headerRight: (
<NavigatorItem icon={ Images.ic_navigator } onPress={ ()=> navigation.state.params.switch() }/>
),
headerStyle: CommonStyles.headerStyle
});
结合BackHandler处理返回和点击返回键两次退出App效果
点击返回键两次退出App效果的需求屡见不鲜。相信很多人在react-navigation下实现该功能都遇到了很多问题,例如,其他界面不能返回。也就是手机本身返回事件在react-navigation之前拦截了。如何结合react-natigation实现呢?和大家分享两种实现方式:
(1)在注册StackNavigator的界面中,注册BackHandler
componentWillMount(){
BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid );
}
componentUnWillMount(){
BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid);
}
_onBackAndroid=()=>{
let now = new Date().getTime();
if(now - lastBackPressed < 2500) {
return false;
}
lastBackPressed = now;
ToastAndroid.show('再点击一次退出应用',ToastAndroid.SHORT);
return true;
}
(2)监听react-navigation的Router
/**
* 处理安卓返回键
*/
const defaultStateAction = AppNavigator.router.getStateForAction;
AppNavigator.router.getStateForAction = (action,state) => {
if(state && action.type === NavigationActions.BACK && state.routes.length === 1) {
if (lastBackPressed + 2000 < Date.now()) {
ToastAndroid.show(Constant.hint_exit,ToastAndroid.SHORT);
lastBackPressed = Date.now();
const routes = [...state.routes];
return {
...state,
...state.routes,
index: routes.length - 1,
};
}
}
return defaultStateAction(action,state);
};
实现Android中界面跳转左右切换动画react-navigation在android中默认的界面切换动画是上下。如何实现左右切换呢?很简单的配置即可:
import CardStackStyleInterpolator from 'react-navigation/src/views/CardStackStyleInterpolator';
- 1
然后在StackNavigator配置下添加如下代码:
transitionConfig:()=>({
screenInterpolator: CardStackStyleInterpolator.forHorizontal,
})
- 1
- 2
- 3
解决快速点击多次跳转
当我们快速点击跳转时,会开启多个重复的界面,如何解决呢?其实在官方Git中也有提示,解决这个问题需要修改react-navigation源码。
找到scr文件夹中的addNavigationHelpers.js文件,替换为如下文本即可:
export default function<S: *>(navigation: NavigationProp<S, NavigationAction>) {
// 添加点击判断
let debounce = true;
return {
...navigation,
goBack: (key?: ?string): boolean =>
navigation.dispatch(
NavigationActions.back({
key: key === undefined ? navigation.state.key : key,
}),
),
navigate: (routeName: string,
params?: NavigationParams,
action?: NavigationAction,): boolean => {
if (debounce) {
debounce = false;
navigation.dispatch(
NavigationActions.navigate({
routeName,
params,
action,
}),
);
setTimeout(
() => {
debounce = true;
},
500,
);
return true;
}
return false;
},
/**
* For updating current route params. For example the nav bar title and
* buttons are based on the route params.
* This means `setParams` can be used to update nav bar for example.
*/
setParams: (params: NavigationParams): boolean =>
navigation.dispatch(
NavigationActions.setParams({
params,
key: navigation.state.key,
}),
),
};
}