react-navigation 使用詳解(轉載)

上篇博客和你們分享了關於React Native jsBundle預加載,界面啓動優化的內容,詳情可點擊:html

基於最新版本React Native實現JsBundle預加載,界面秒開優化

1、開源庫介紹

 

 

今年1月份,新開源的react-natvigation庫備受矚目。在短短不到3個月的時間,github上星數已達4000+。Fb推薦使用庫,而且在React Native當前最新版本0.44中將Navigator刪除。react-navigation據稱有原生般的性能體驗效果。可能會成爲將來React Native導航組件的主流軍。本篇內容基於【 ^1.0.0-beta.9 】版原本介紹關於該庫的使用和實戰技巧。能夠看到,雖然是beta版本,不過基本穩定,你們可放心在項目中使用。奉上  react-navigation 官方文檔node

 

該庫包含三類組件:react

(1)StackNavigator:用來跳轉頁面和傳遞參數ios

(2)TabNavigator:相似底部導航欄,用來在同一屏幕下切換不一樣界面
git

(3)DrawerNavigator:側滑菜單導航欄,用於輕鬆設置帶抽屜導航的屏幕github

2、react-navigation使用

 

具體內容大體分爲以下:npm

(1)react-navigation庫屬性介紹react-native

(2)StackNavigator、TabNavigator實現界面間跳轉,Tab切換數組

(3)StackNavigator界面間跳轉、傳值、取值瀏覽器

(4)DrawerNavigator實現抽屜導航菜單

(5)DrawerNavigator擴展功能

(6)自定義react-navigation

一、StackNavigator屬性介紹

navigationOptions:配置StackNavigator的一些屬性。

    title:標題,若是設置了這個導航欄和標籤欄的title就會變成同樣的,不推薦使用
    header:能夠設置一些導航的屬性,若是隱藏頂部導航欄只要將這個屬性設置爲null
    headerTitle:設置導航欄標題,推薦
    headerBackTitle:設置跳轉頁面左側返回箭頭後面的文字,默認是上一個頁面的標題。能夠自定義,也能夠設置爲null
    headerTruncatedBackTitle:設置當上個頁面標題不符合返回箭頭後的文字時,默認改爲"返回"
    headerRight:設置導航條右側。能夠是按鈕或者其餘視圖控件
    headerLeft:設置導航條左側。能夠是按鈕或者其餘視圖控件
    headerStyle:設置導航條的樣式。背景色,寬高等
    headerTitleStyle:設置導航欄文字樣式
    headerBackTitleStyle:設置導航欄‘返回’文字樣式
    headerTintColor:設置導航欄顏色
    headerPressColorAndroid:安卓獨有的設置顏色紋理,須要安卓版本大於5.0
    gesturesEnabled:是否支持滑動返回手勢,iOS默認支持,安卓默認關閉
 

screen:對應界面名稱,須要填入import以後的頁面

mode:定義跳轉風格

   card:使用iOS和安卓默認的風格

   modal:iOS獨有的使屏幕從底部畫出。相似iOS的present效果

headerMode:返回上級頁面時動畫效果

   float:iOS默認的效果

   screen:滑動過程當中,整個頁面都會返回

   none:無動畫

cardStyle:自定義設置跳轉效果

   transitionConfig: 自定義設置滑動返回的配置

   onTransitionStart:當轉換動畫即將開始時被調用的功能

   onTransitionEnd:當轉換動畫完成,將被調用的功能

path:路由中設置的路徑的覆蓋映射配置

initialRouteName:設置默認的頁面組件,必須是上面已註冊的頁面組件

initialRouteParams:初始路由參數

注:你們可能對於path不太理解。path屬性適用於其餘app或瀏覽器使用url打開本app並進入指定頁面。path屬性用於聲明一個界面路徑,例如:【/pages/Home】。此時咱們能夠在手機瀏覽器中輸入:app名稱://pages/Home來啓動該App,並進入Home界面。

二、TabNavigator屬性介紹

screen:和導航的功能是同樣的,對應界面名稱,能夠在其餘頁面經過這個screen傳值和跳轉。


navigationOptions:配置TabNavigator的一些屬性

title:標題,會同時設置導航條和標籤欄的title

tabBarVisible:是否隱藏標籤欄。默認不隱藏(true)

tabBarIcon:設置標籤欄的圖標。須要給每一個都設置

tabBarLabel:設置標籤欄的title。推薦

導航欄配置

tabBarPosition:設置tabbar的位置,iOS默認在底部,安卓默認在頂部。(屬性值:'top''bottom')

swipeEnabled:是否容許在標籤之間進行滑動

animationEnabled:是否在更改標籤時顯示動畫

lazy:是否根據須要懶惰呈現標籤,而不是提早,意思是在app打開的時候將底部標籤欄所有加載,默認false,推薦爲true

trueinitialRouteName: 設置默認的頁面組件

backBehavior:按 back 鍵是否跳轉到第一個Tab(首頁), none 爲不跳轉

tabBarOptions:配置標籤欄的一些屬性iOS屬性

activeTintColor:label和icon的前景色 活躍狀態下

activeBackgroundColor:label和icon的背景色 活躍狀態下

inactiveTintColor:label和icon的前景色 不活躍狀態下

inactiveBackgroundColor:label和icon的背景色 不活躍狀態下

showLabel:是否顯示label,默認開啓 style:tabbar的樣式

labelStyle:label的樣式安卓屬性

activeTintColor:label和icon的前景色 活躍狀態下

inactiveTintColor:label和icon的前景色 不活躍狀態下

showIcon:是否顯示圖標,默認關閉

showLabel:是否顯示label,默認開啓 style:tabbar的樣式

labelStyle:label的樣式 upperCaseLabel:是否使標籤大寫,默認爲true

pressColor:material漣漪效果的顏色(安卓版本須要大於5.0)

pressOpacity:按壓標籤的透明度變化(安卓版本須要小於5.0)

scrollEnabled:是否啓用可滾動選項卡 tabStyle:tab的樣式

indicatorStyle:標籤指示器的樣式對象(選項卡底部的行)。安卓底部會多出一條線,能夠將height設置爲0來暫時解決這個問題

labelStyle:label的樣式

iconStyle:圖標樣式

三、DrawerNavigator屬性介紹

 DrawerNavigatorConfig

     drawerWidth - 抽屜的寬度
     drawerPosition - 選項是左或右。 默認爲左側位置
     contentComponent - 用於呈現抽屜內容的組件,例如導航項。 接收抽屜的導航。 默認爲DrawerItems
     contentOptions - 配置抽屜內容

     initialRouteName - 初始路由的routeName
     order - 定義抽屜項目順序的routeNames數組。
     路徑 - 提供routeName到路徑配置的映射,它覆蓋routeConfigs中設置的路徑。
     backBehavior - 後退按鈕是否會切換到初始路由? 若是是,設置爲initialRoute,不然爲none。 默認爲initialRoute行爲

    DrawerItems的contentOptions屬性

     activeTintColor - 活動標籤的標籤和圖標顏色
     activeBackgroundColor - 活動標籤的背景顏色
     inactiveTintColor - 非活動標籤的標籤和圖標顏色
     inactiveBackgroundColor - 非活動標籤的背景顏色
     內容部分的樣式樣式對象
     labelStyle - 當您的標籤是字符串時,要覆蓋內容部分中的文本樣式的樣式對象

從上述中大體瞭解了react-navigation三種組件的一些基本屬性,因此到咱們甩起袖子擼代碼見證下奇蹟了。

四、使用StackNavigator + TabNavigator實現Tab界面切換、界面間導航

 

API定義:StackNavigator(RouteConfigs, StackNavigatorConfig)、TabNavigator(RouteConfigs, TabNavigatorConfig)

(1)集成 react-navigation:在終端執行 【 npm install react-navigation --save 】

(2)界面中導入必要組件:

import {StackNavigator,TabNavigator,TabBarBottom} from 'react-navigation';
import HomeScreen from './pages/HomePage';
import MineScreen from './pages/MinePage';

(3)定義TabNavigator:

const Tab = TabNavigator(
  {
    Home:{
      screen:HomeScreen,
      navigationOptions:({navigation}) => ({
        tabBarLabel:'首頁',
        tabBarIcon:({focused,tintColor}) => (
          <TabBarItem
            tintColor={tintColor}
            focused={focused}
            normalImage={require('./imgs/nav_fav@2x.png')}
            selectedImage={require('./imgs/nav_fav_actived@3x.png')}
          />
        )
      }),
    },

    Mine:{
          screen:MineScreen,
          navigationOptions:({navigation}) => ({
          tabBarLabel:'',
          tabBarIcon:({focused,tintColor}) => (
            <TabBarItem
             tintColor={tintColor}
              focused={focused}
              normalImage={require('./imgs/tab_me_nor@3x.png')}
              selectedImage={require('./imgs/tab_me_selected@2x.png')}
            />
          )
        }),
      },
    },

    {
      tabBarComponent:TabBarBottom,
      tabBarPosition:'bottom',
      swipeEnabled:false,
      animationEnabled:false,
      lazy:true,
      tabBarOptions:{
        activeTintColor:'#06c1ae',
        inactiveTintColor:'#979797',
        style:{backgroundColor:'#ffffff',},
        labelStyle: {
              fontSize: 20, // 文字大小
          },
      }
      
    }

  );

TabBarItem爲封裝的組件:

import React,{Component} from 'react';
import {Image} from 'react-native';

export default class TabBarItem extends Component {

    render() {
        return(
            <Image source={ this.props.focused ? this.props.selectedImage : this.props.normalImage }
                style={ { tintColor:this.props.tintColor,width:25,height:25 } }
            />
        )
    }
    
}

能夠看到,咱們定義了一個名稱爲【Tab】的TabNavigator的導航組件。在組件中,分爲兩層參數:

(1)第一層參數定義了要切換的界面,即【首頁】、【我】兩個界面組件,經過screen屬性指定。而且經過navigationOptions屬性設置相關屬性參數。

(2)設置導航欄的屬性參數。

TabNavigator定義好以後,須要用StackNavigator,顧名思義,StackNavigator就是以棧的方式來存放整個界面的,而TabNavigator是做爲一個界面內不一樣子界面之間切換。因此還須要咱們定義StackNavigator:

  const Navigator = StackNavigator(
    
    {
      Tab:{screen:Tab},
      Product:{screen:ProductScreen}
    },

    {
      navigationOptions:{
        headerBackTitle:null,
        headerTintColor:'#333333',
        showIcon:true,
       swipeEnabled:false,
       animationEnabled:false,
      },

      mode:'card',
    });

看起來和TabNavigator很類似,一樣是指定了兩個參數:

(1)指定要跳轉的界面組件。一樣是screen屬性標識界面組件,很少贅述。

(2)定義跳轉屬性參數,即頂部導航欄的一些參數設置和跳轉方式。

能夠看到,咱們將Tab做爲一個界面設置到了StackNavigator。這樣就能夠實現Tab導航和界面間跳轉的效果了。

最後就是在render中引用StackNavigator:

export default class Demo extends Component {

  render() {
        return (
          <Navigator />
        );
  }
}

tackNavigator還提供了onNavigationStateChange回調方法,用來監聽導航狀態的改變。具體再也不贅述。實現了界面跳轉和切換,那麼就該來增長下界面之間的感情了,來看看如何實現界面之間的傳值和取值。

五、界面間跳轉、傳值、取值

在界面組件注入到StackNavigator中時,界面組件就被賦予了navigation屬性,即在界面組件中能夠經過【this.props.navigation】獲取並進行一些操做。

navigation屬性中提供了不少的函數簡化界面間操做,簡單列舉幾點:

(1)經過navigate函數實現界面之間跳轉:

 this.props.navigation.navigate('Mine');

參數爲咱們在StackNavigator註冊界面組件時的名稱。一樣也能夠從當前頁面返回到上一頁:

// 返回上一頁
this.props.navigation.goBack();

(2)跳轉時傳值:

this.props.navigation.navigate('Mine',{info:'傳值過去'});

第一個參數一樣爲要跳轉的界面組件名稱,第二個參數爲要傳遞的參數,info能夠理解爲key,後面即傳遞的參數。

(3)獲取值:

{this.props.navigation.state.params.info}

經過state.params來獲取傳來的參數,後面爲key值。此處爲info。

以上實現完成,咱們就能夠愉快的玩耍啦~~ 什麼?突然發如今Android上的效果和IOS效果不同。老闆要界面一致哇~ 怎麼辦?那就須要咱們進行簡單的適配了。

3、DrawerNavigator實現抽屜導航

一、導航實現

API定義:DrawerNavigator(RouteConfigs,DrawerNavigatorConfig)

(1)界面中定義DrawerNavigator:

import {StackNavigator,TabNavigator,DrawerNavigator} from 'react-navigation';
import HomeScreen from './pages/HomePage';
import MineScreen from './pages/MinePage';

export default class Demo extends Component {

  render() {
        return (
          <Navigator />
        );
  }
}

const Navigator = DrawerNavigator({

    Home:{screen:HomeScreen},
    Mine:{screen:MineScreen},
});

const styles = StyleSheet.create({

    container: {
        flex: 1,
    },
});

AppRegistry.registerComponent('Demo', () => Demo);

定義方式和StackNavigator基本相似,再也不贅述。

(2)HomeScreen界面和MineScreen界面:

export default class HomePage extends Component {

    static navigationOptions = {
        drawerLabel: '首頁',
        drawerIcon:({tintColor}) => (
            <Image
                source={require('./../imgs/ic_happy.png')}
                style={[styles.icon, {tintColor: tintColor}]}/>
        ),
    };

    render() {
        return(
            <View style={{flex:1}}>
                <Text onPress={this._skip.bind(this)}>點擊跳轉</Text>
            </View>
        );
    }

    _skip() {
        this.props.navigation.navigate("Mine");
    }
}


export default class MinePage extends Component {

    static navigationOptions = {
        drawerLabel:'',
         drawerIcon: ({ tintColor }) => (
            <Image
                source={require('./../imgs/ic_h.png')}
                style={[styles.icon, {tintColor: tintColor}]}
            />
        ),
    };

    render() {
        return(
            <View style={{flex:1}}>
                <Text onPress={this._skip.bind(this)}>返回上一界面</Text>
            </View>
        );
    }

    /**
     * 跳轉
     */
    _skip() {
        this.props.navigation.goBack();
    }
}

代碼很簡單,實現了界面之間的跳轉。

二、擴展功能

(1)默認DrawerView不可滾動。要實現可滾動視圖,必須使用contentComponent自定義容器,以下所示:

{
  drawerWidth:200,
  抽屜位置:「對」
  contentComponent:props => <ScrollView> <DrawerItems {... props} /> </ ScrollView>
}

(2)能夠覆蓋導航使用的默認組件,使用DrawerItems自定義導航組件:

import {DrawerItems} from 'react-navigation';

const CustomDrawerContentComponent = (props) => (
  <View style = {style.container}>
    <DrawerItems {... props} />
  </View>  
);

(3)嵌套抽屜導航
若是您嵌套DrawerNavigation,抽屜將顯示在父導航下方。

4、自定義react-navigation

(1)適配頂部導航欄標題:
   測試中發現,在iphone上標題欄的標題爲居中狀態,而在Android上則是居左對齊。因此須要咱們修改源碼,進行適配。
【node_modules -- react-navigation -- src -- views -- Header.js】的326行代碼處,修改成以下:

 title: {
    bottom: 0,
    left: TITLE_OFFSET,
    right: TITLE_OFFSET,
    top: 0,
    position: 'absolute',
    alignItems: 'center',
  }

上面方法經過修改源碼的方式其實略有弊端,畢竟擴展性很差。還有另一種方式就是,在navigationOptions中設置headerTitleStyle的alignSelf爲 ' center '便可解決。

 

(2)去除返回鍵文字顯示:

【node_modules -- react-navigation -- src -- views -- HeaderBackButton.js】的91行代碼處,修改成以下便可。

       {Platform.OS === 'ios' &&
            title &&
            <Text
              onLayout={this._onTextLayout}
              style={[styles.title, { color: tintColor }]}
              numberOfLines={1}
            >
              {backButtonTitle}
            </Text>}

將上述代碼刪除便可。

 

(3)動態設置頭部按鈕事件:

當咱們在頭部設置左右按鈕時,確定避免不了要設置按鈕的單擊事件,可是此時會有一個問題,navigationOptions是被修飾爲static類型的,因此咱們在按鈕的onPress的方法中不能直接經過this來調用Component中的方法。如何解決呢?在官方文檔中,做者給出利用設置params的思想來動態設置頭部標題。那麼咱們能夠利用這種方式,將單擊回調函數以參數的方式傳遞到params,而後在navigationOption中利用navigation來取出設置到onPress便可:

    componentDidMount () {
        /**
         * 將單擊回調函數做爲參數傳遞
         */
        this.props.navigation.setParams({
                switch: () => this.switchView()
        });
    }
    /**
     * 切換視圖
     */
    switchView() {
        alert('切換')
    }
    static navigationOptions = ({navigation,screenProps}) => ({
        headerTitle: '企業服務',
        headerTitleStyle: CommonStyles.headerTitleStyle,
        headerRight: (
            <NavigatorItem icon={ Images.ic_navigator } onPress={ ()=> navigation.state.params.switch() }/>
        ),
        headerStyle: CommonStyles.headerStyle
    });

(4)結合BackHandler處理返回和點擊返回鍵兩次退出App效果

點擊返回鍵兩次退出App效果的需求家常便飯。相信不少人在react-navigation下實現該功能都遇到了不少問題,例如,其餘界面不能返回。也就是手機自己返回事件在react-navigation以前攔截了。如何結合react-natigation實現呢?和你們分享兩種實現方式:

(1)在註冊StackNavigator的界面中,註冊BackHandler:

    componentWillMount(){
        BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid );
    }


    componentUnWillMount(){
        BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid);
    }

    _onBackAndroid=()=>{
        let now = new Date().getTime();
        if(now - lastBackPressed < 2500) {
            return false;
        }
        lastBackPressed = now;
        ToastAndroid.show('再點擊一次退出應用',ToastAndroid.SHORT);
        return true;
    }

(2)監聽react-navigation的Router

/**
 * 處理安卓返回鍵
 */
const defaultStateAction = AppNavigator.router.getStateForAction;
AppNavigator.router.getStateForAction = (action,state) => {
    if(state && action.type === NavigationActions.BACK && state.routes.length === 1) {
        if (lastBackPressed + 2000 < Date.now()) {
            ToastAndroid.show(Constant.hint_exit,ToastAndroid.SHORT);
            lastBackPressed = Date.now();
            const routes = [...state.routes];
            return {
                ...state,
                ...state.routes,
                index: routes.length - 1,
            };
        }
    }
    return defaultStateAction(action,state);
};

(5)實現Android中界面跳轉左右切換動畫

react-navigation在Android中默認的界面切換動畫是上下。如何實現左右切換呢?很簡單的配置便可:

import CardStackStyleInterpolator from 'react-navigation/src/views/CardStackStyleInterpolator';

而後在StackNavigator的配置下添加以下代碼:

        transitionConfig:()=>({
            screenInterpolator: CardStackStyleInterpolator.forHorizontal,
        })

(6)解決快速點擊屢次跳轉

當咱們快速點擊跳轉時,會開啓多個重複的界面,如何解決呢。其實在官方git中也有提示,解決這個問題須要修改react-navigation源碼:

找到src文件夾中的addNavigationHelpers.js文件,替換爲以下文本便可:

export default function<S: *>(navigation: NavigationProp<S, NavigationAction>) {
  // 添加點擊判斷
  let debounce = true;
  return {
      ...navigation,
      goBack: (key?: ?string): boolean =>
          navigation.dispatch(
              NavigationActions.back({
                  key: key === undefined ? navigation.state.key : key,
              }),
          ),
      navigate: (routeName: string,
                 params?: NavigationParams,
                 action?: NavigationAction,): boolean => {
          if (debounce) {
              debounce = false;
              navigation.dispatch(
                  NavigationActions.navigate({
                      routeName,
                      params,
                      action,
                  }),
              );
              setTimeout(
                  () => {
                      debounce = true;
                  },
              500,
              );
              return true;
          }
          return false;
      },
    /**
     * For updating current route params. For example the nav bar title and
     * buttons are based on the route params.
     * This means `setParams` can be used to update nav bar for example.
     */
    setParams: (params: NavigationParams): boolean =>
      navigation.dispatch(
        NavigationActions.setParams({
          params,
          key: navigation.state.key,
        }),
      ),
  };
}

(7)解決goBack,根據路由名稱返回指定界面

react-navigation默認不支持根據路由名返回指定界面,官方只提供了根據Key來作goBack的指定返回。解決這個問題一樣須要修改react-navigation源碼,在Navigation.goBack條件下添加對路由名的支持。找到/node_modules/react-navigation/src/routers/StackRouter.js, 全局搜索backRoute,將條件判斷語句替換爲以下代碼:

if (action.type === NavigationActions.BACK) {
        const key = action.key;
        let backRouteIndex = null;
        if (key) {
          const backRoute = null;
          if(key.indexOf('id') >= 0) {
            backRoute = state.routes.find((route: *) => route.key === action.key);
          } else {
            backRoute = state.routes.find(route => route.routeName === action.key);
          }
          backRouteIndex = state.routes.indexOf(backRoute);
        }
        if (backRouteIndex == null) {
          return StateUtils.pop(state);
        }
        if (backRouteIndex > 0) {
          return {
            ...state,
            routes: state.routes.slice(0, backRouteIndex),
            index: backRouteIndex - 1,
          };
        }
      }

(8)自定義Tab

import React, { Component } from 'react';
import {
    AppRegistry,
    Platform,
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
    NativeModules,
    ImageBackground,
    DeviceEventEmitter
} from 'react-native';

export default class Tab extends Component {
    renderItem = (route, index) => {
        const {
            navigation,
            jumpToIndex,
        } = this.props;

        const focused = index === navigation.state.index;
        const color = focused ? this.props.activeTintColor : this.props.inactiveTintColor;
        let TabScene = {
            focused:focused,
            route:route,
            tintColor:color
        };

        if(index==1){
            return (<View style={[styles.tabItem,{backgroundColor:'transparent'}]}>
                    </View>
            );
        }

        return (
            <TouchableOpacity
                key={route.key}
                style={styles.tabItem}
                onPress={() => jumpToIndex(index)}
            >
                <View
                    style={styles.tabItem}>
                    {this.props.renderIcon(TabScene)}
                    <Text style={{ ...styles.tabText,marginTop:SCALE(10),color }}>{this.props.getLabel(TabScene)}</Text>
                </View>
            </TouchableOpacity>
        );
    };
    render(){
        const {navigation,jumpToIndex} = this.props;
        const {routes,} = navigation.state;
        const focused = 1 === navigation.state.index;
        const color = focused ? this.props.activeTintColor : this.props.inactiveTintColor;
        let TabScene = {
            focused:focused,
            route:routes[1],
            tintColor:color
        };
        return (
        <View style={{width:WIDTH}}>
            <View style={styles.tab}>
                {routes && routes.map((route,index) => this.renderItem(route, index))}
            </View>
            <TouchableOpacity
            key={"centerView"}
            style={[styles.tabItem,{position:'absolute',bottom:0,left:(WIDTH-SCALE(100))/2,right:WIDTH-SCALE(100),height:SCALE(120)}]}
            onPress={() => jumpToIndex(1)}
        >
            <View
                style={styles.tabItem}>
                {this.props.renderIcon(TabScene)}
                <Text style={{ ...styles.tabText,marginTop:SCALE(10),color }}>{this.props.getLabel(TabScene)}</Text>
            </View>
        </TouchableOpacity>
        </View>
        );
    }
}
const styles = {
    tab:{
        width:WIDTH,
        backgroundColor:'transparent',
        flexDirection:'row',
        justifyContent:'space-around',
        alignItems:'flex-end'
    },
    tabItem:{
        height:SCALE(80),
        width:SCALE(100),
        alignItems:'center',
        justifyContent:'center'
    },
    tabText:{
        marginTop:SCALE(13),
        fontSize:FONT(10),
        color:Color.C7b7b7b
    },
    tabTextChoose:{
        color:Color.f3474b
    },
    tabImage:{
        width:SCALE(42),
        height:SCALE(42),
    },
}

5、效果圖

抽屜導航:

以上就是咱們實戰中經常使用的屬性和技巧。具體的操做還須要你們在實踐過程當中測試體會。

源碼參考

 

原文地址 http://blog.csdn.net/u013718120/article/details/72357698

相關文章
相關標籤/搜索