主界面开发
上一节,我们已经完成了首页的开发,现在,我们继续完成主界面的开发,就是添加底部‘首页’和‘我的’两个tabbar。
import React,{Component} from 'react';
import {
View,Text,TouchableOpacity,Image,StyleSheet,}from 'react-native';
export default class MainScene extends Component{
constructor(props){
super(props);
this.state={
tabIndex:0,}
}
render(){
return(
<View style={{flex:1,justifyContent:'flex-end'}}> <View style={{backgroundColor:'#d5d5d5',height:1,}}/> <View style={{height:55,flexDirection:'row',justifyContent:'center',alignItems:'center'}}> <TouchableOpacity style={{flex:1}} activeOpacity={0.6} onPress={this._onTabPress.bind(this,0)}> <View style={styles.ItemView}> <Image style={{height:30,width:30}} source={this.state.tabIndex==0?require('../img/icon_home_select.png'):require('../img/icon_home_unselect.png')} /> <Text style={this.state.tabIndex==0?styles.TabTextSelect:styles.TabTextUnSelect}>首页</Text> </View> </TouchableOpacity> <TouchableOpacity style={{flex:1}} activeOpacity={0.6} onPress={this._onTabPress.bind(this,1)}> <View style={styles.ItemView}> <Image style={{height:30,width:30}} source={this.state.tabIndex==0?require('../img/icon_my_unselect.png'):require('../img/icon_my_select.png')} /> <Text style={this.state.tabIndex==0?styles.TabTextUnSelect:styles.TabTextSelect}>我的</Text> </View> </TouchableOpacity> </View> </View> ); } //tab点击事件 _onTabPress(index){ //console.log('index = '+ index); //this.viewPager.setPage(index); this.setState({ tabIndex:index,}); } } var styles = StyleSheet.create({ ItemView:{ flex:1,alignItems:'center',marginTop:3,},TabTextSelect:{ flex:1,textAlign:'center',color:'#ff5722',TabTextUnSelect:{ flex:1,color:'#d5d5d5',});
主要绘制了,底部两个带图标的选项卡,根据state.tabIndex的索引,动态修改tab的icon和文字样式,然后将index.android.js的启动页修改为MainScene,运行程序,结果如下
MainScene->render
render(){
var page = this.state.tabIndex===0?<HomeScene/>:<MyScene />;
return(
<View style={{flex:1,justifyContent:'flex-end'}}> {page} ...//省略其它代码 </View> ); }
这里我们使用动态渲染页面的方式,当选项进行切换时,根据tabIndex索引值,获取到对应的页面进行显示。
到此,主界面的功能基本完成,只是点击列表item没反应,因为,我们还没给页面设计跳转功能,下面我们将来说说应用的导航器。
应用导航器
关于react-native页面跳转和数据传递,使用到的是Navigator这个组件。它可以帮我们在在不同的页面进行切换,只要指定了路由,在renderScene方法就会渲染对应的页面。更多详细介绍
由于我们的应用要跳转的地方会有很多,所以我们需要为APP定义一个统一的路由入口。
我们先抽出定义一个应用的导航器AppNavigator,在js/component/下新建AppNavigator.js
import React,{Component} from 'react';
import {
Navigator,} from 'react-native';
//应用导航器
export default class AppNavigator extends Component{
constructor(props){
super(props);
}
render(){
return(
<Navigator initialRoute={ { id:this.props.id,data:this.props.data,name:this.props.name,component:this.props.component } } renderScene={(route,navigator)=>{ let Scene = route.component; return <Scene id={route.id} data={route.data} name={route.name} navigator={navigator}/> }} style={{flex:1,}} configureScene={(route) => { if(route.sceneConfig){ return route.sceneConfig; } return Navigator.SceneConfigs.HorizontalSwipeJump; }} /> ); } }
我们指定了路由数据结构如下(这个数据结构根据自己的需求去定义),
{
id:this.props.id,data:this.props.data,name:this.props.name,component:this.props.component
}
页面的唯一识别id,要传递的数据data(可以任意类型的数据),页面的名称name(页面的名称,这里用于TitleBar显示标题名称),要渲染的页面组件component。重要的部分在于renderScene函数。
renderScene={(route,navigator)=>{
let Scene = route.component;
return <Scene id={route.id} data={route.data} name={route.name} navigator={navigator}/> }}
这里我们获取到route传过来的component,然后将该component当作一个view节点返回回去,并且给该组件传入了一组props,这样在该Scene下,就可以通过this.props获取到id\data\name\navigaror.当获取到navigaror对象之后,我们可以进行push\pop操作,实现页面的跳转\回退功能。
使用导航器进行页面跳转
导航器写好之后,我们就该思考将它作为应用的统一入口应该放置在哪合适。
如果你在使用Navigator过程中,碰到跳转时,某些元素比如标题、底部的tabbar,在跳转之后还需要特殊处理(隐藏),那么可能是你把Navigator放的位置不够准确。就像我之前写的时候,将Navigator放在了HomeScene中,导致点击影片item页面跳转之后(跳到详情),底部的tabbar还显示着。一开始想的是在跳转之后将tabbar隐藏,但是这样就会有闪动的情况,后来把Navigator位置改正之后,就达到想要的结果了。
既然作为程序的统一路由入口,那么我们是不是有理由这么想,它也应该是放在程序的启动处。是的,将它放在index.android.js。
import AppNavigator from './js/component/AppNavigator';
import MainScene from './js/MainScene';
class XiFan extends Component {
render(){
return(
<AppNavigator id='MainScene' data='' name='' component={MainScene}/> ); } }
将导航器的初始路由页面设置为MainScene,由于是主界面,所以不用传递data\name数据。
render(){
var page = this.state.tabIndex===0?<HomeScene navigator={this.props.navigator}/>:<MyScene />;
....//省略其它代码
}
增加了navigator参数,而this.props.navigator实例对象是来至于AppNavigator的renderScene中navigator,这样我们就把navigator实例对象传递给了HomeScene。再打开HomeScene,要修改的地方有两处
HomeScene->render()
- 让TitleBar具有返回/回退功能
将navigator对象传递给TitleBar
<TitleBar title="首页" subtitle="看韩剧,上稀饭" subScene={false} navigator={this.props.navigator}/>
再打开TitleBarComponent ->_onIconClick()
//返回按钮事件
_onIconClick(){
var navigator = this.props.navigator;
if(navigator){
navigator.pop();
}
}
在获取到navigator对象后,进行pop操作,就可以返回了。
- 将navigator对象传递给DramaComponent
<ViewPagerAndroid
...//省略其它代码
>
<View style={{flex:1}}> <DramaComponent url='/hanju/new/' navigator={this.props.navigator}/> </View> <View style={{flex:1}}> <DramaComponent url='/hanju/renqi/' navigator={this.props.navigator}/> </View> </ViewPagerAndroid>
这样DramaComponent也有了navigator实例对象,打开DramaComponent,我们给它item点击事件添加跳转到详情的功能。
DramaComponent -> _onItemPress
//item 点击跳转
_onItemPress(movie){
const navigator = this.props.navigator;
if(navigator){
navigator.push({
id:'DramaDetailScene',data:movie,name:movie.name,component:DramaDetailScene
});
}
}
也就是,如果是由Navigator导航的组件,那么该组件本身就有Navigator对象,并由this.props.navigator取得,如果是导航组件的内部组件要使用导航对象,那么就要给该组件增加props属性
navigator={this.props.navigator}
这里item点击跳转的是详情页DramaDetailScene,我们先简单的写下这个页面,后面在具体实现详情页
新建DramaDetailScene.js
import React,Text
} from 'react-native';
import TitleBar from './component/TitleBarComponent';
export default class DramaDetailScene extends Component{
constructor(props) {
super(props);
}
render(){
return (
<View> <TitleBar title={this.props.name} subtitle='' subScene={true} navigator={this.props.navigator}/> <Text>影片详情页</Text> </View> ); }; }
这样,我们就实现了页面的跳转功能,但是由于Android还有物理返回键,因此要实现按下返回键时退出当前页、在主界面 “再按一次退出程序” 的功能。
首先要拦截返回键事件,在主界面MainScene,添加如下代码
import {
...//省略其它代码
BackAndroid,Platform,ToastAndroid
} from 'react-native';
export default class MainScene extends Component{
...//省略其它代码
componentDidMount(){
this._addBackAndroidListener(this.props.navigator);
}
componentWillUnmount(){
this._removeBackAndroidListener();
}
//监听Android返回键
_addBackAndroidListener(navigator){
if(Platform.OS==='android'){
var currTime = 0;
BackAndroid.addEventListener('hardwareBackPress',()=>{
if(!navigator){return false;}
const routers = navigator.getCurrentRoutes();
if(routers.length == 1){//在主界面
var nowTime = (new Date()).valueOf();
if(nowTime - currTime > 2000){
currTime = nowTime;
ToastAndroid.show("再按一次退出程序",ToastAndroid.SHORT);
return true;
}
return false;
}else{//在其他子页面
navigator.pop();
return true;
}
});
}
}
//移除监听
_removeBackAndroidListener(){
if (Platform.OS === 'android') {
BackAndroid.removeEventListener('hardwareBackPress');
}
}
}
总结
至此,应用的页面跳转已经实现了,navigator对于react native 来说还是比较重要的一个组件,因为每个应用都会涉及到页面的切换,而这都离不开对navigator的使用。看完本节的内容,应该对navigator的使用已经掌握得差不多了,下一节,我们将对详情页进行开发,其中重要的是学习如何使用原生代码为react native提供原生能力。