React Native填坑之旅 -- 使用react-navigation代替Navigator

Navigator已經被React Native廢棄了。也許你能夠在另外的一個依賴庫裏react-native-deprecated-custom-components裏找到。不過既然官方推薦的是react-navigation那咱們就來看看這個東西到底有什麼好的,值不值得用。javascript

一句話歸納的話,react-navigation很是值得用。以前配置一個Navigator很是的繁瑣,可是使用react-navigation的任何一個導航組件都很是簡單。項目的github地址在這裏java

react-navigation包括下面三個Navigator:react

  • StackNavigator: 這個組件是用來代替以前的Navigator的。凡是維持一種「先進後廚」的棧式導航的話就能夠用這個。
  • TabNavigator:這個組件和iOS的`TabBarController。看起來是這樣的。
  • DrawerNavigator:這個組件就是抽屜式的導航菜單。在React Native裏只有Android纔有:DrawerLayoutAndroid,在iOS裏是沒有的。有了DrawerNavigator,兩個平臺均可以用了。

我會在下文裏主要介紹StackNavigatorDrawerNavigator。對於TabNavigatgor它的使用很是簡單,當你回了前面的兩種的時候你就天然能夠搞定它了。git

Stack Navigator

github

react-native init AwesomeProject

命令後生成的默認項目裏,查看index.js文件:npm

import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('AwesomeProject', () => App);

APP開始執行後運行的就是App組件。也就是App.js文件export的是什麼組件,App就運行什麼組件。react-native

App.js文件中,去掉export default。就如咱們的demo作的同樣,添加一個MessageContainer.js文件,並添加demo中的內容。這樣在其中咱們已經有了AppMessageContainer兩個組件。flex

下面看下如何配置。this

最簡單的:code

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

  1. title:是導航欄上顯示的title。
  2. headerLeft: 是導航欄左側的組件。我這裏放了一個按鈕。更好的是放置一個TouchableOpacity組件。由於按鈕在iOS上還好,可是在Android上就是一個明晃晃的按鈕啊,各類邊框和陰影。
  3. headerRight: 是導航欄右側的組件。

MessageContainer的導航欄上就只須要一個「返回」按鈕,因此只有一個headerLeft就足夠了。在首頁上的導航欄的headerLeft是用來觸發稍後講到的DrawerNavigator的。

這樣,這個靠譜的導航就完成了。


Drawer Navigator

DrawerNavigatorStackNavigator的配置很相似。

const NavApp = DrawerNavigator({
  Home: {
    screen: App,
  },
  MyWallet: {
    screen: MyWalletView,
  },
  MyVoucher: {
    screen: MyVoucherView,
  }
});

這個時候看起來是這樣的:
normal_drawer_nav_top.png

可是我想要的效果是這樣的:
drawer_top.png

顯然,文檔裏提供的一些簡單的定製是不能完成這樣的效果的。因而,咱們查看文檔,發現有辦法直接替換掉默認的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導航包含stack導航

drawer導航被stack導航包含

回到正題。從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交互中也並非只有導航到某一頁,而後再從那一頁跳轉回來這麼簡單。後面有機會會講到這方面的內容。

相關文章
相關標籤/搜索