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


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,兩個平台都可以用了。

我會在下文里主要介紹StackNavigatorDrawerNavigator。對於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中的內容。這樣在其中我們已經有了AppMessageContainer兩個組件。

下面看下如何配置。

最簡單的:

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交互中也並不是只有導航到某一頁,然后再從那一頁跳轉回來這么簡單。后面有機會會講到這方面的內容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM