Navigator
已經被React Native廢棄了。也許你可以在另外的一個依賴庫里react-native-deprecated-custom-components
里找到。不過既然官方推薦的是react-navigation
那我們就來看看這個東西到底有什么好的,值不值得用。
一句話概括的話,react-navigation
非常值得用。之前配置一個Navigator
非常的繁瑣,但是使用react-navigation
的任何一個導航組件都非常簡單。項目的github地址在這里。
react-navigation
包括下面三個Navigator:
StackNavigator
: 這個組件是用來代替之前的Navigator
的。凡是維持一種“先進后廚”的棧式導航的話就可以用這個。TabNavigator
:這個組件和iOS的`TabBarController。看起來是這樣的。DrawerNavigator
:這個組件就是抽屜式的導航菜單。在React Native里只有Android才有:DrawerLayoutAndroid
,在iOS里是沒有的。有了DrawerNavigator
,兩個平台都可以用了。
我會在下文里主要介紹StackNavigator
和DrawerNavigator
。對於TabNavigatgor
它的使用非常簡單,當你回了前面的兩種的時候你就自然可以搞定它了。
Stack Navigator
在
react-native init AwesomeProject
命令后生成的默認項目里,查看index.js文件:
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('AwesomeProject', () => App);
APP開始執行后運行的就是App
組件。也就是App.js文件export的是什么組件,App就運行什么組件。
在App.js文件中,去掉export default
。就如我們的demo做的一樣,添加一個MessageContainer.js文件,並添加demo中的內容。這樣在其中我們已經有了App
、MessageContainer
兩個組件。
下面看下如何配置。
最簡單的:
export default NavHome = StackNavigator({
Home: {
screen: NavApp,
},
Message: {
screen: MessageContainer,
},
})
導出StackNavigator
方法生成的組件NavHome
。運行起來之后,理論上就可以導航了。但是會有問題,因為這時還沒有能夠跳轉的觸發點。所以,我們還要做如下的修改。
修改App.js文件的內容。在其中添加一個按鈕,點擊之后可以進入到MessageContainer
組件。修改MessageContainer.js文件,在里面添加一個按鈕返回。
//App.js
<Button onPress={this.props.navigation.navigate('Message')} title={'To message'} />
//MessageContainer.js
<Button onPress={this.props.navigation.goBack()} title={'Go Back'} />
但是,這樣還是demo的水平,離真正的產品級使用還差很多。一般的App,在push到下一個頁面的時候會點擊navigation bar的回退按鈕返回上一頁。我們就來實現這個功能。
使我們首先看一下StackNavigator
的API:
StackNavigator(RouteConfigs, StackNavigatorConfig)
通過查看文檔,要實現這個功能需要在RouteConfigs
里面增加navigationOptions
來達到。如下:
export default NavHome = StackNavigator({
Home: {
screen: App,
navigationOptions: ({navigation}) => ({
title: 'Home',
headerLeft: (<Button onPress={() => navigation.navigate('DrawerToggle')} title={'User'} />),
headerRight: (<Button onPress={() => navigation.navigate('Message')} title={'Message'} />),
})
},
Message: {
screen: MessageContainer,
navigationOptions: ({navigation}) => ({
title: "Message",
headerLeft: (<Button title='Back' onPress={() => {navigation.goBack();}} />)
})
},
});
詳細看一下navigationOptions
。
- title:是導航欄上顯示的title。
- headerLeft: 是導航欄左側的組件。我這里放了一個按鈕。更好的是放置一個
TouchableOpacity
組件。因為按鈕在iOS上還好,但是在Android上就是一個明晃晃的按鈕啊,各種邊框和陰影。 - headerRight: 是導航欄右側的組件。
在MessageContainer
的導航欄上就只需要一個“返回”按鈕,所以只有一個headerLeft
就足夠了。在首頁上的導航欄的headerLeft
是用來觸發稍后講到的DrawerNavigator
的。
這樣,這個靠譜的導航就完成了。
Drawer Navigator
DrawerNavigator
和StackNavigator
的配置很類似。
const NavApp = DrawerNavigator({
Home: {
screen: App,
},
MyWallet: {
screen: MyWalletView,
},
MyVoucher: {
screen: MyVoucherView,
}
});
這個時候看起來是這樣的:
但是我想要的效果是這樣的:
顯然,文檔里提供的一些簡單的定制是不能完成這樣的效果的。於是,我們查看文檔,發現有辦法直接替換掉默認的Drawer實現,非常簡單:
const NavApp = DrawerNavigator({
Home: {
screen: App,
},
User: {
screen: UserContainer,
},
MyWallet: {
screen: MyWalletView,
},
MyVoucher: {
screen: MyVoucherView,
}
}, {
contentComponent: props => (<UserDrawer items={props} />)
})
看下API:
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
只要叫上DrawerNavigatorConfig
配置里的contentComponent
配置。也就是上面配置的第二個參數。
{
contentComponent: props => (<UserDrawer items={props} />)
}
contentComponent
就是drawer的內容組件。這里我們用的是UserDrawer
組件,並把props傳遞了進去。
這樣我們想要的抽屜式菜單就實現了。
StackNavigator和DrawerNavigator結合使用
現在把這兩個組件結合在一起使用。在首頁上的導航欄里的兩個按鈕,左側的開啟drawer導航,右側的是“message”按鈕,使用StackNavigator組件導航。
如果是在drawer導航里使用StackNavigator
的話,那么只要這樣配置:
export default NavHome = StackNavigator({
Home: {
screen: NavApp,
navigationOptions: ({navigation}) => ({
title: 'Home',
headerLeft: (<Button onPress={() => navigation.navigate('DrawerToggle')} title={'User'} />),
headerRight: (<Button onPress={() => navigation.navigate('Message')} title={'Message'} />),
})
},
Message: {
screen: MessageContainer,
navigationOptions: ({navigation}) => ({
title: "Message",
headerLeft: (<Button title='Back' onPress={() => {navigation.goBack();}} />)
})
},
});
export default NavApp = DrawerNavigator({
Home: {
screen: NavHome, // ***
},
MyWallet: {
screen: MyWalletView,
},
MyVoucher: {
screen: MyVoucherView,
}
}, {
contentComponent: props => (<UserDrawer items={props} />)
})
要在Drawer導航里用stack導航,那么就在drawer導航里的某個路由選項里加上screen: NavHome
。這個NavHome
就是stack導航。反之,則是在stack導航里的某個route選項的screen上指定drawer導航。
但是在使用上還是有一點區別的。如果drawer導航里包含stack導航。那么drawer導航菜單的最高點是在屏幕的最高點。反之,如果drawer導航被stack導航包含的話,drawer導航菜單的最高點是在導航欄的下方的。如圖:
回到正題。從drawer導航菜單跳轉到任何的頁面后如何跳轉回來呢?還是老方法:
export default class MyWalletView extends React.Component {
render() {
return (
<TouchableOpacity
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
onPress={() => this.props.navigation.goBack()}>
<Text>{'My Wallet'}</Text>
</TouchableOpacity>
);
}
}
調用props傳入的navigation的方法來實現返回:
this.props.navigation.goBack()
總結
更多請看代碼吧。留下來TabNavigator
來給各位讀者朋友實踐一下練練手吧。其實配置的簡單程度比早前React Native里的Navigator已經降低了很多了。
StackNavigator
里還有除了navigate()
和goBack()
兩個方法之外,還有其他的一些方法可以調用。實際的App交互中也並不是只有導航到某一頁,然后再從那一頁跳轉回來這么簡單。后面有機會會講到這方面的內容。