主界面開發
上一節,我們已經完成了首頁的開發,現在,我們繼續完成主界面的開發,就是添加底部‘首頁’和‘我的’兩個tabbar。
在js/文件夾下,新建MainScene.js文件
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,
justifyContent:'center',
alignItems:'center',
marginTop:3,
},
TabTextSelect:{
flex:1,
textAlign:'center',
alignItems:'center',
color:'#ff5722',
},
TabTextUnSelect:{
flex:1,
textAlign:'center',
alignItems:'center',
color:'#d5d5d5',
},
});
主要繪製了,底部兩個帶圖標的選項卡,根據state.tabIndex的索引,動態修改tab的icon和文字樣式,然後將index.android.js的啓動頁修改爲MainScene,運行程序,結果如下
然後就是給tab上方添加內容頁面
MainScene->render
render(){
var page = this.state.tabIndex===0?<HomeScene/>:<MyScene />;
return(
<View style={{flex:1,justifyContent:'flex-end'}}>
{page}
...//省略其它代碼
</View>
);
}
這裏我們使用動態渲染頁面的方式,當選項進行切換時,根據tabIndex索引值,獲取到對應的頁面進行顯示。
ok,添加完之後,運行,現在的效果如下
到此,主界面的功能基本完成,只是點擊列表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操作,實現頁面的跳轉\回退功能。
configureScene函數是配置頁面跳轉時的動畫效果
使用導航器進行頁面跳轉
導航器寫好之後,我們就該思考將它作爲應用的統一入口應該放置在哪合適。
如果你在使用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數據。
然後打開MainScene,修改代碼
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,{Component} from 'react';
import {
View,
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提供原生能力。