React Navigation制作导航栏
React Native开发有一点挺友好的,就是文档写的比较清楚详细,而且有中文网。中文网不只是把英语翻译成中文,而在需要注意的地方加了一些译注。最好不要忽略这些译注,不然新手很容易掉坑里,这也是我的亲身经验。
React Native现有几个导航组件,但是导航库react-navigation
是社区主推使用的导航库。
如果你刚开始接触,那么直接选择React Navigation就好。如果你只针对iOS平台开发,并且想和系统原生外观一致,那么可以选择NavigatorIOS。
功能
首页>注册>登录>注销这个Demo的主要页面如上所示。导航的逻辑:
-
进入首页,导航栏的右侧包含一个按钮,点击可以调用系统自带浏览器。
-
a.选择注册,跳转到注册页面。
b.如果已有账号,则点击登录跳转到登录页面。 -
注册成功自动跳转到登录页面。
-
a.此时可以选择用刚注册的账户登录;
b.或者点击导航栏的返回键回到首页。 -
登录成功自动跳转到包含两个标签页的页面。
-
选择“账户”,点击注销,可以直接跳转回首页。
整个过程不考虑cookie,着重考虑导航栏控制屏幕之间跳转。
实现
这个逻辑是很基础很简单的,用到了 React Navigation
库的三个组件:
-
StackNavigator
:基础。使用堆栈储存导航信息(新的屏幕信息保存在堆栈的顶部)。 -
NavigationActions
:各种跳转方法。 -
TabNavigator
:标签页的导航栏。
导航结构
能够跳转到的页面都要加到StackNavigator
初始化中。
StackNavigator(RouteConfigs, StackNavigatorConfig)
第一个参数是Route配置,第二个参数是统一的导航栏样式等的配置。在Route配置中,可以单独配置某个页面的导航栏。比如Tabs
页面,因为添加了底部标签页导航栏,不需要顶部的导航栏了,所以设置navigationOptions
的header
,从而隐藏这个页面的顶部导航栏。
//App.js
//顶部导航栏
export const App = StackNavigator({
Home: {screen: HomeScreen}, //首页
Register: {screen: RegisterScreen}, //注册
Login: {screen: LoginScreen}, //登录
Tabs: { //登录之后的标签页
screen: BottomTab,
navigationOptions:{
header: null //隐藏顶部导航栏
}
},
},{
navigationOptions: { //配置顶部导航
headerTitleStyle: { //导航栏文字的样式
fontSize: 18,
color: '#b2b2b2',
},
// headerBackTitleStyle: { //‘返回’文字的样式
// color: '#b2b2b2'
// },
headerStyle: { //导航栏的样式
backgroundColor: '#343434',
borderBottomColor: '#f5a623',
borderBottomWidth: 1
},
}
});
//注册组件。TestProject是APP的名称。
AppRegistry.registerComponent('TestProject', () => App);
跳转到某个页面
如果我们要跳转到StackNavigator
中的某个页面,可以调用this.props.navigation
提供的navigat
方法。应用中的每个Screen
组件都接收到一个navigation
的prop。详情可阅读:
https://reactnavigation.org/docs/navigators/navigation-prop。
const { navigate } = this.props.navigation;
navigate('Tabs',{username:this.state.username}) //可传递参数
首页>注册>登录>返回主页
注册成功之后自动跳转到登录页,这时候点击返回的,怎么直接返回主页,而不是返回上一级的注册页?
我的方法是,在注册成功之后,使用NavigationActions
的reset
方法重置导航的堆栈信息。只保留首页和当前的登录页。
//app.js
//注册之后自动跳转到登录页面中,并且重置导航stack,使得不能返回注册页面
export const afterRegisterAction = NavigationActions.reset({
index: 1, //1表示当前调度到Login页面
actions: [ //新的导航操作历史
NavigationActions.navigate({ routeName: 'Home'}),
NavigationActions.navigate({ routeName: 'Login'}),
]
})
使用时:
import {afterRegisterAction} from '../App'
......
this.props.navigation.dispatch(afterRegisterAction) //跳转到登录页面
在账户页面注销回到首页也是这个原理:
//回到首页,且清空stack中的其他的导航记录。
export const resetToHomeAction = NavigationActions.reset({
index: 0, //对应actions中的index。指定当前页面。
actions: [ //替换之后的导航记录
NavigationActions.navigate({ routeName: 'Home'})
]
})
底部标签页导航
使用TabNavigator
制作标签页导航。清晰明了直接上代码:
import {Text} from 'react-native';
import { TabNavigator } from "react-navigation";
import AssetListScreen from './AssetList'
import AccountScreen from './Account'
const BottomTab = TabNavigator({
AssetList: { screen: AssetListScreen}, //资产标签页
Account: { screen: AccountScreen}, //账号标签页
},
{
tabBarPosition: 'bottom', //android默认的TabNavigator是在上方。在StackNavigator下面。所以需要特意指定。
animationEnabled: true, //标签切换时的动画
tabBarOptions: {
activeTintColor: '#f5a623', //active时标签中文字和Icon的颜色
labelStyle: { //标签文字的样式
fontSize: 12
},
indicatorStyle:{ //android标签下划线的样式。注意这里不是设置color。
backgroundColor: '#f5a623'
},
style: { //整体标签栏的样式
backgroundColor: '#343434',
},
showIcon: true, //android上默认不显示icon。所以要特别设定。
}
}
);
export default BottomTab;
导航栏中的按钮
在每个Screen组件的内部,可以设置navigationOptions
来修改这一页的导航栏,比如设置标题等。
//Home.js
import Button from 'react-native-button' //使用react-native-button组件。其他按钮组件:https://js.coach/react-native
export default class HomeScreen extends React.Component {
static navigationOptions = {
title: 'ReactNativeDemo',
headerRight: ( //定义导航栏右侧的按钮
<Button
style={{fontSize:12, color:'#fff'}}
containerStyle={{marginRight:10,height:30, width:50, overflow:'hidden', borderRadius:4, backgroundColor: '#343434', borderColor:'#b2b2b2', borderWidth:1, justifyContent:'center'}}
onPress={()=>contact()}
>
联系
</Button>),
};
}
...
参考链接
- React Native 中文网,使用导航器跳转页面:http://reactnative.cn/docs/0.48/navigation.html#content
- Hello Mobile Navigation: https://reactnavigation.org/docs/intro/
React Navigation的官方文档,例子挺多而且详细。入门必看。要定制导航栏的话,就仔细看看API中有没有对应的属性。毕竟例子不能全都覆盖到。