注意了,若是有小伙伴们发现运行做者提供的react-navigation示例项目报以下的错误,多是你们使用了 yarn install
命令致使的,解决这个错误的办法就是将nodemodules删除,而后从新使用npm install
命令来安装,最后使用 npm start
来起服务,应该就不报错了。若是还有报错,请加做者交流群,将问题反馈到群里,谢谢。node
react-navigation 组件是官方推荐使用的导航组件,功能和性能都远远的优于以前的Navigator组件,公司的RN项目最先是使用的react-native-router-flux
导航组件,由于那个时候react-navigation
组件尚未出来,在使用了react-navigation
后,感受比react-native-router-flux组件有更增强大的功能,体验也略好些,这两个导航组件是目前star最多的导航组件,而且他们都完美的支持与Redux框架的结合使用,推荐小伙伴们两个组件都尝试使用下。
react-navigationreact
https://github.com/guangqiang-liu/react-navigation-demoandroid
Demo示例讲解包含三部分ios
注意: 有小伙伴说Demo运行报错,这里你们须要注意,Demo clone下来以后,咱们先要执行 npm install
操做, 而后在执行 react-native link
,最后在 执行 npm start
来运行项目,若是还有其余的报错信息,欢迎进群提出报错信息git
http://www.jianshu.com/p/faa98d8bd3fagithub
react-navigation 组件主要由三大部分组成npm
下面咱们对react-navigation
详解也主要围绕这三个API来展开redux
StackNavigator导航栏的工做原理就和iOS中原生的UINavigationController同样的,是以栈的方式来管理每个页面控制器的,当使用push就是入栈,当使用pop操做时就是出栈,这个很好理解,若是咱们想让一个页面控制器有导航栏,那么咱们首先要作的就是给这个页面注册导航react-native
API数组
StackNavigator(RouteConfigs, StackNavigatorConfig)
StackNavigator函数中有两个参数:
配置RouteConfigs
const RouteConfigs = { Home: { screen: TabBar // screen属性为必须配置属性 }, Home2: { screen: Home2, path:'app/Home2', navigationOptions: { title: '这是在RouteConfigs中设置的title', headerTitleStyle: { fontSize: 10 } } }, Home3: { screen: Home3 }, Home4: { screen: Home4 }, Home5: {screen: Home5}, Home6: {screen: Home6}, Home7: {screen: Home7}, Setting2: {screen: Setting2}, Setting3: {screen: Setting3}, }
配置StackNavigatorConfig
const StackNavigatorConfig = { initialRouteName: 'Home', initialRouteParams: {initPara: '初始页面参数'}, navigationOptions: { title: '标题', headerTitleStyle: {fontSize: 18, color: 'red'}, headerStyle: {height: 49}, }, paths: 'page/main', mode: 'card', headerMode: 'screen', cardStyle: {backgroundColor: "#ffffff"}, transitionConfig: (() => ({ })), onTransitionStart: (() => { console.log('页面跳转动画开始') }), onTransitionEnd: (() => { console.log('页面跳转动画结束') }), }
注册导航
import {StackNavigator, TabNavigator} from "react-navigation" const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig) export default class Main extends Component { render() { return ( <Navigator/> ) } }
从上面注册导航的代码块中,咱们能够看出StackNavigator函数接受两个配置对象RouteConfigs
和 StackNavigatorConfig
,可是这里须要注意,第二个参数StackNavigatorConfig能够省略,表示不作任何导航默认配置
StackNavigatorConfig配置参数
initialRouteName
:导航器组件中初始显示页面的路由名称,若是不设置,则默认第一个路由页面为初始显示页面initialRouteParams
:给初始路由的参数,在初始显示的页面中能够经过this.props.navigation.state.params
来获取navigationOptions
:路由页面的全局配置项paths
:RouteConfigs里面路径设置的映射mode
:页面跳转方式,有card和modal两种,默认为 card
headerMode
:页面跳转时,头部的动画模式,有 float 、 screen 、 none 三种
cardStyle
:为各个页面设置统一的样式,好比背景色,字体大小等transitionConfig
:配置页面跳转的动画,覆盖默认的动画效果onTransitionStart
:页面跳转动画即将开始时调用onTransitionEnd
:页面跳转动画一旦完成会立刻调用在StackNavigatorConfig配置参数中有一个navigationOptions
属性的配置,这个配置项能够理解为导航栏的全局配置表,下面就讲解这个属性的可配置参数
navigationOptions配置参数
title
:导航栏的标题,或者Tab标题 tabBarLabelheader
:自定义的头部组件,使用该属性后系统的头部组件会消失,若是想在页面中自定义,能够设置为null,这样就不会出现页面中留有一个高度为64navigationBar的高度headerTitle
:头部的标题,即页面的标题headerBackTitle
:返回标题,默认为 title的标题headerTruncatedBackTitle
:返回标题不能显示时(好比返回标题太长了)显示此标题,默认为'Back'headerRight
:头部右边组件headerLeft
:头部左边组件headerStyle
:头部组件的样式headerTitleStyle
:头部标题的样式headerBackTitleStyle
:头部返回标题的样式headerTintColor
:头部颜色headerPressColorAndroid
:Android 5.0 以上MD风格的波纹颜色gesturesEnabled
:否能侧滑返回,iOS 默认 true , Android 默认 falsenavigationOptions
// StackNavigatorConfig中的navigationOptions属性也能够在组件内用static navigationOptions 设置(会覆盖此处的设置) navigationOptions: { header: { // 导航栏相关设置项 backTitle: '返回', // 左上角返回键文字 style: { backgroundColor: '#fff' }, titleStyle: { color: 'green' } }, cardStack: { gesturesEnabled: true } }
注意:
RouteConfigs
中配置 navigationOptions属性,咱们也能够在单独页面配置navigationOptionsRouteConfigs
和StackNavigatorConfig
对象中的navigationOptions属性里面的对应属性在RouteConfigs
中配置 navigationOptions
const RouteConfigs = { Home: { screen: TabBar }, Home2: { screen: Home2, path:'app/Home2', // 此处设置了, 会覆盖组件内的`static navigationOptions`设置. 具体参数详见下文 navigationOptions: { title: '这是在RouteConfigs中设置的title', headerTitleStyle: { fontSize: 10 } } }, Home3: { screen: Home3 }, Home4: { screen: Home4 }, Home5: {screen: Home5}, Home6: {screen: Home6}, Home7: {screen: Home7}, Setting2: {screen: Setting2}, Setting3: {screen: Setting3}, }
在具体页面中配置 navigationOptions
import {StackNavigator, TabNavigator} from "react-navigation" const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig) export default class Main extends Component { // 配置页面导航选项 static navigationOptions = { title: 'homeThree', header: (navigation, defaultHeader) => ({ ...defaultHeader, // 默认预设 visible: true // 覆盖预设中的此项 }), cardStack: { gesturesEnabled: false // 是否能够右滑返回 } } // 或这样 static navigationOptions = { // title: 'Two', // 固定标题 title: (navigation, childRouter) => { // 动态标题 if (navigation.state.params.isSelected) { return `${navigation.state.params.name}选中`; } else { return `${navigation.state.params.name}没选中`; } }, header: ({ state, setParams, goBack }) => { let right; if (state.params.isSelected) { right = (<Button title="取消" onPress={() => setParams({ isSelected: false })}/>); } else { right = (<Button title="选择" onPress={() => setParams({ isSelected: true })}/>); } let left = (<Button title="返回" onPress={() => goBack()}/>); let visible = false; // 是否显示导航栏 return { right, left, visible }; }, // header: {left: <Button title="返回"/>}, } render() { return ( <Navigator/> ) } }
API
TabNavigator(RouteConfigs, TabNavigatorConfig)
从API上看,TabNavigator 和 StackNavigator 函数用法同样,都是接受RouteConfigs和TabNavigatorConfig这两个参数
RouteConfigs配置参数
路由配置和StackNavigator中同样,配置路由以及对应的 screen 页面,navigationOptions 为对应路由页面的配置选项
title
:Tab标题,可用做headerTitle 和 tabBarLabel 回退标题tabBarVisible
:Tab的是否可见,默认为 truetabBarIcon
:Tab的icon组件,能够根据 {focused: boolean, tintColor: string} 方法来返回一个icon组件tabBarLabel
:Tab中显示的标题字符串或者组件,也能够根据{ focused: boolean, tintColor: string } 方法返回一个组件配置RouteConfigs
const RouteConfigs = { Home: { screen: Home, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'Home', tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-home' : 'ios-home-outline'} size={26} style={{ color: tintColor }}/> ) }), }, People: { screen: People, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'People', tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-people' : 'ios-people-outline'} size={26} style={{ color: tintColor }}/> ) }), }, Chat: { screen: Chat, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'Chat', tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'} size={26} style={{ color: tintColor }}/> ) }), }, Setting: { screen: Setting, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'Settings', tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-settings' : 'ios-settings-outline'} size={26} style={{ color: tintColor }}/> ) }), } }
TabNavigatorConfig配置参数
tabBarComponent
: Tab选项卡组件,有TabBarBottom和TabBarTop两个值,在iOS中默认为 TabBarBottom ,在Android中默认为 TabBarTop
tabBarPosition
:Tab选项卡的位置,有top或bottom两个值
swipeEnabled
:是否能够滑动切换Tab选项卡animationEnabled
:切换界面是否须要动画lazy
:是否懒加载页面initialRouteName
:初始显示的Tab对应的页面路由名称order
:用路由名称数组来表示Tab选项卡的顺序,默认为路由配置顺序paths
: 路径配置backBehavior
:androd点击返回键时的处理,有initialRoute 和 none 两个值
tabBarOptions
:Tab配置属性,用在TabBarTop和TabBarBottom时有些属性不一致
用在TabBarTop时对应的属性:
用在TabBarBottom时对应的属性:
使用TabBarTop代码示例
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);
使用TabBarBottom代码示例
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);
API
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
DrawerNavigator
与StackNavigator
和 TabNavigator
函数的使用方式同样,参数配置也相似
路由配置和StackNavigator中同样,配置路由以及对应的 screen 页面,navigationOptions 为对应路由页面的配置选项
RouteConfigs参数配置
title
:抽屉标题,和headerTitle 、 drawerLabel同样drawerLabel
:标签字符串,或者自定义组件,能够根据{ focused: boolean, tintColor: string } 函数来返回一个自定义组件做为标签drawerIcon
:抽屉icon,能够根据 { focused: boolean, tintColor: string } 函数来返回一个自定义组件做为iconDrawerNavigatorConfig参数配置
drawerWidth
:抽屉宽度,可使用Dimensions获取屏幕的宽度动态计算drawerPosition
:抽屉位置,能够是 left 或者 rightcontentComponent
:抽屉内容组件,能够自定义侧滑抽屉中的全部内容,默认为 DrawerItemscontentOptions
:用来配置抽屉内容的属性。当用来配置 DrawerItems 是配置属性选项
initialRouteName
:初始化展现的页面路由名称order
:抽屉导航栏目顺序,用路由名称数组表示paths
:路径backBehavior
:androd点击返回键时的处理,有initialRoute和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"; const RouteConfigs = { 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(RouteConfigs, DrawerNavigatorConfigs) const StackRouteConfigs = { Drawer: { screen: Drawer, } }; const StackNavigatorConfigs = { initialRouteName: 'Drawer', navigationOptions: { title: '标题', headerStyle: {backgroundColor: '#5da8ff'}, headerTitleStyle: {color: '#333333'}, } } const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs) export default class Main extends Component { render() { return ( <Navigator/> ) } }
在StackNavigator中注册过的组件都有navigation这个属性,navigation有5个主要参数
咱们平时使用react-navigation做为导航组件来开发时,常用到的也就是这5个属性的功能
navigate
导航到下一个页面
<Button buttonStyle={{marginVertical: 10}} title={'跳转到Home2界面'} onPress={() => this.props.navigation.navigate('Home2')} />
<Button buttonStyle={{marginVertical: 10}} title={'跳转到Home3界面,并传递参数'} // 这里传递了参数`id` onPress={() => this.props.navigation.navigate('Home3', {id: 123})} />
navigation中的navigate函数能够接受三个参数
routeName
:注册过的目标路由名称,也就是准备跳转到的页面路由地址(例如上面的Home3
)params
:跳转到下一个页面,传递的参数(例如上面的id
)action
:下文有讲到state
state
属性包含有传递过来的三个参数 params、key 、routeName
routeName
:注册过的目标路由名称key
:路由身份标识params
:跳转时传递的参数获取state中的参数:this.props.navigation.state.params.id
这样就能拿到上一个页面传递的参数:id
setParams
this.props.navigation.setParams()
: 该方法容许界面更改router中的参数,能够用来动态的更改导航栏的内容。好比能够用来更新头部的按钮或者标题等
使用场景:重写导航按钮的返回按钮,自定义返回事件
export default class Home5 extends Component { static navigationOptions = ({navigation, screenProps}) => ({ title: 'Home5', headerLeft: ( <Button title={'customAction'} onPress={() => navigation.state.params.customAction()} /> ) } ) componentDidMount() { const {setParams} = this.props.navigation setParams({customAction: () => this.tempAction()}) } tempAction() { alert('在导航栏按钮上调用Component内中的函数,由于static修饰的函数为静态函数,内部不能使用this') } render() { return ( <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}> <Text>Home5</Text> </View> ) } }
goBack
退出当前页面,返回到上一个页面,能够不传参数,也能够传参数,还能够传 null
<Button title={'返回上一页面'} onPress={() => goBack()} />
dispatch
this.props.navigation.dispatch
: 能够dispatch一些action,主要支持的action有一下几种
import { NavigationActions } from 'react-navigation' const navigationAction = NavigationActions.navigate({ routeName: 'Profile', params: {}, // navigate can have a nested navigate action that will be run inside the child router action: NavigationActions.navigate({ routeName: 'SubProfileRoute'}) }) this.props.navigation.dispatch(navigationAction)
Reset方法会清除原来的路由记录,添加上新设置的路由信息, 能够指定多个action,index是指定默认显示的那个路由页面, 注意不要越界了
import { NavigationActions } from 'react-navigation' const resetAction = NavigationActions.reset({ index: 0, actions: [ NavigationActions.navigate({ routeName: 'Profile'}), NavigationActions.navigate({ routeName: 'Two'}) ] }) this.props.navigation.dispatch(resetAction)
为指定的router更新参数,该参数必须是已经存在于router的param中
import { NavigationActions } from 'react-navigation' const setParamsAction = NavigationActions.setParams({ params: {}, // these are the new params that will be merged into the existing route params // The key of the route that should get the new params key: 'screen-123', }) this.props.navigation.dispatch(setParamsAction)
NavigationActions.back()
const initialState = Navigator.router.getStateForAction(NavigationActions.init()); export default (state = initialState, actions) => { const nextState = Navigator.router.getStateForAction(actions, state); return nextState || state; }
注意: 若是你的项目中使用了与Redux框架结合,这里的dispatch就能够派发任何你想dispatch的Action了
使用场景:Counter 计数器
class Counter extends Component { static navigationOptions = () => ({ title: 'Counter加减计数器' }) render() { const {dispatch} = this.props.navigation return ( <View> <Text>Counter</Text> <Text style={{marginVertical: 20, color: 'red', fontSize: 30}}>{this.props.counterValue}</Text> <Button buttonStyle={{marginVertical: 10}} title={'+'} onPress={() => dispatch(increaseAction())} /> <Button buttonStyle={{marginVertical: 10}} title={'-'} onPress={() => dispatch(decreaseAction())} /> </View> ) } } const mapStateToProps = state => { return { counterValue: state.home.counter.counterValue } } export default connect(mapStateToProps)(Counter)
页面跳转与传值
<Button buttonStyle={{marginVertical: 10}} title={'跳转到Home2界面'} onPress={() => this.props.navigation.navigate('Home2')} /> <Button buttonStyle={{marginVertical: 10}} title={'跳转到Home3界面,并传递参数'} onPress={() => this.props.navigation.navigate('Home3', {id: 123})} />
在下一界面接收参数,经过this.props.navigation.state.params
接收参数
export default class Home3 extends Component { render() { const {navigate} = this.props.navigation return ( <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}> <Text>Home3</Text> <Text style={{marginVertical: 20}}>{`Home界面传递的参数为:${this.props.navigation.state.params.id}`}</Text> <Button buttonStyle={{marginVertical: 10}} title={'跳转到Home4界面'} onPress={() => navigate('Home4')} /> </View> ) } }
回调传参
当前界面进行跳转,并传递参数
<Button buttonStyle={{marginVertical: 10}} title={'跳转到Home6界面,回调传参'} onPress={() => this.props.navigation.navigate('Home6',{callback: (response) => alert(response)})} />
下一界面在返回以前执行函数回调传参给上一个页面
const {state, goBack} = this.props.navigation <Button title={'回调传参'} onPress={() => { state.params.callback && state.params.callback('这是回调参数') goBack() }} />
其余app或浏览器使用url打开次app并进入指定页面,相似于iOS中的URL导航同样。如浏览器输入url OneM://home/home2直接进入home2页面
iOS平台须要额外配置
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { return [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation]; }
在js中配置
js组件在注册路由时设置惟一的路径path, 例如Home2: { screen: Home2, path:'home/Home2'}
在手机浏览器访问OneM://home/Home2, 弹窗选择打开, 就能够打开OneM app并进到Home2页面了
{ drawerWidth:200, contentComponent:props => <ScrollView> <DrawerItems {... props}></DrawerItems> </ ScrollView> }
import {DrawerItems} from 'react-navigation'; const CustomDrawerContentComponent = (props) => ( <View style = {style.container}> <DrawerItems {... props} /> </View> )
测试中发现,在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便可:
export default class Home5 extends Component { static navigationOptions = ({navigation, screenProps}) => ({ title: 'Home5', headerRight: ( <Button title={'customAction'} onPress={() => navigation.state.params.customAction()} /> ) } ) componentDidMount() { const {setParams} = this.props.navigation setParams({customAction: () => this.tempAction()}) } tempAction() { alert('在导航栏按钮上调用Component内中的函数,由于static修饰的函数为静态函数,内部不能使用this') } render() { return ( <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}> <Text>Home5</Text> </View> ) } }
点击返回键两次退出App效果的需求家常便饭。相信不少人在react-navigation下实现该功能都遇到了不少问题,例如,其余界面不能返回。也就是手机自己返回事件在react-navigation以前拦截了。如何结合react-natigation实现呢?和你们分享两种实现方式:
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; }
/** * 处理安卓返回键 */ 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); }
react-navigation在android中默认的界面切换动画是上下。如何实现左右切换呢?很简单的配置便可:
import CardStackStyleInterpolator from 'react-navigation/src/views/CardStackStyleInterpolator';
而后在StackNavigator的配置下添加以下代码:
transitionConfig:()=>({ screenInterpolator: CardStackStyleInterpolator.forHorizontal, })
当咱们快速点击跳转时,会开启多个重复的界面,如何解决呢。其实在官方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, }), ), } }
有时咱们点击tabBar上的tab来切换页面,可是在切换页面以前咱们想先作一些逻辑处理,而后在切换到tab页面,这时咱们就须要先hook到这个tab的点击事件,下面代码块就是告诉你如何hook到tab的点击事件,处理完事件在打开tab页面,这个使用具体使用方式在示例Demo中都有实际使用,不清楚的同窗们直接去运行示例项目了解便可
Chat: { screen: Chat, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'Chat', tabBarOnPress: () => { Alert.alert( '注意!', '这里作了hook tabBar的点击事件操做,咱们能够hook到这个点击事件,处理咱们想要处理的业务后再打开 Chat这个页面', [ {text: '打开tab页面', onPress: () => navigation.navigate('Chat')}, {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'}, ], { cancelable: false } ) }, tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'} size={26} style={{ color: tintColor }}/> ) }), },
navigator与tabBar嵌套 具体的结合使用方式示例Demo中有给出具体示例,这个同窗们直接运行示例Demo查看便可
以前有很多同窗问我,怎么给一个tabBar设置badge,前段时间因为太忙,一直没有去处理这个问题,后面去实现了下自定义badge,感受仍是挺简单的,由于navigation的tabBarItem原本就是支持自定义的,既然可以自定义,那实现badge天然也是可行的了,下面就是具体实现代码块
People: { screen: People, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'People', tabBarIcon: ({ focused, tintColor }) => ( <View style={{position: 'absolute', top: -10}}> <Ionicons name={focused ? 'ios-people' : 'ios-people-outline'} size={26} style={{ color: tintColor }}/> <View style={{backgroundColor: 'red', position: 'absolute', right: -10, top: -5, height: 15, width: 20, borderRadius: 8, overflow: 'hidden'}}> <Text style={{fontSize: 12, textAlign: 'center'}}>10</Text> </View> </View> ) }), },
有时候咱们在开发的时候,不免会遇到在点击返回按钮的时候,想直接返回到指定的某一个页面,而不是返回上一级页面,这时咱们就须要对goback()函数作些处理了,具体的代码实现以下
const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig) const defaultStateAction = Navigator.router.getStateForAction; Navigator.router.getStateForAction = (action, state) => { if (state && action.key && action.type === 'Navigation/BACK') { const desiredRoute = state.routes.find((route) => route.routeName === action.key) if (desiredRoute) { const index = state.routes.indexOf(desiredRoute) const finalState = { ...state, routes: state.routes.slice(0, index + 1), index: index, }; return finalState } else { if (state.routes.length > action.key) { const stacksLength = state.routes.length - action.key const stacks = state.routes.slice(0, stacksLength) const finalState = { ...state, routes: stacks, index: stacksLength - 1, }; return finalState } } } return defaultStateAction(action, state) }
其实goback()函数,是很容易的就能够作到返回到指定页面,和返回指定层级的页面的,并不像网上其余的文章说的须要改源码啊,或者是须要结合redux才能实现啊,并非这样的,只须要咱们简单的维护下导航的路由栈便可解决问题,这个其实和原生iOS中处理导航的栈管理是一个道理
const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig) const defaultStateAction = Navigator.router.getStateForAction; Navigator.router.getStateForAction = (action, state) => { if (state && action.key && action.type === 'Navigation/BACK') { const desiredRoute = state.routes.find((route) => route.routeName === action.key) if (desiredRoute) { const index = state.routes.indexOf(desiredRoute) const finalState = { ...state, routes: state.routes.slice(0, index + 1), index: index, }; return finalState } else { if (state.routes.length > action.key) { const stacksLength = state.routes.length - action.key const stacks = state.routes.slice(0, stacksLength) const finalState = { ...state, routes: stacks, index: stacksLength - 1, }; return finalState } } } return defaultStateAction(action, state) }
navigator与抽屉嵌套使用的方式,示例Demo中已经有具体实现了,这个比较简单,就不作详细解答了
简书上面的 开发中遇到的问题及注意事项中有讲解决办法,不过做者仍是建议你们将导航栏封装成一个组件,使用自定义的组件灵活性更高
这个懒加载Tab,这个也没有什么好解答的,官方已经给我提供了设置属性,咱们只须要设置个属性便可,具体代码以下
const TabNavigatorConfigs = { initialRouteName: 'Home', lazy: true, tabBarOptions: { activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff', } }
react-navigation导航组件的API相对较多,若是小伙伴们看完讲解仍是不清楚如何使用,建议直接运行Demo项目,边调试边理解。
针对以前不少同窗反映出关于react-navigation 使用上遇到的一些问题,上面基本上都逐一解答了,而且都在示例Demo实战的测试过是可行方案,后期还有其余的小伙伴遇到使用上的问题,欢迎进群讨论,或者是给我简书留言,谢谢你们的支持。