1. 程式人生 > >[個人實戰]React Native專案中Navigation的使用

[個人實戰]React Native專案中Navigation的使用

        在看完React Native中文網的《聽清明老師從頭講React Native》的視訊後,自己準備摸索著做個小Demo出來把相應的知識點再鞏固鞏固,準備把一些個人認為有必要記錄的東西寫下來,方便以後查閱,如果能幫到別人那就是最好不過了~

        今天想要記錄的是外掛react-navigation的使用,首先是安裝:

npm install react-navigation --save

我主要用到的就是這個外掛裡面的TabNavigator(底部導航)和StackNavigator(標題導航),當然,這個名稱是我目前所理解的情況下方便自己記憶取的。

StackNavigator:


TabNavigator:


我們首先來說說TabNavigator的實現,因為相比StackNavigator所需要處理的細節問題會少一些。

首先,我在工程目錄下建立了TabNavigation.js檔案,當然,這只是為了讓我在檔案多了以後方便快速識別,你也可以取別的名稱。

接下來我們建立Home.js,Video.js,Info.js三個檔案,用來承載咱們切換的介面。

Home.js

import React from 'react'
import{
    View,
    Text,
    StyleSheet
}from 'react-native'

const style=StyleSheet.create({
    container:{
        flex:1,
        alignItems:'center',
        justifyContent:'center'
    }
})
export default class Home extends React.PureComponent{
    render(){
        return(
            <View style={style.container}>
                <Text>Home</Text>
            </View>
        )
    }
}

Video.js

import React from 'react'
import{
    View,
    Text,
    StyleSheet
}from 'react-native'

const style=StyleSheet.create({
    container:{
        flex:1,
        alignItems:'center',
        justifyContent:'center'
    }
})
export default class Video extends React.PureComponent{
    render(){
        return(
            <View style={style.container}>
                <Text>Video</Text>
            </View>
        )
    }
}

Info.js

import React from 'react'
import{
    View,
    Text,
    StyleSheet
}from 'react-native'

const style=StyleSheet.create({
    container:{
        flex:1,
        alignItems:'center',
        justifyContent:'center'
    }
})
export default class Info extends React.PureComponent{
    render(){
        return(
            <View style={style.container}>
                <Text>Info</Text>
            </View>
        )
    }
}

好了,接下來我們講三個檔案匯入咱們的TabNavigation.js檔案中:

import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'

然後再通過TabNavigator建立咱們的底部導航:

import{
    TabNavigator
}from 'react-navigation'

import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'
export default TabNavigator({
    Home:{
        screen:HomeScreen
    },
    Video:{
        screen:VideoScreen
    },
    Info:{
        screen:InfoScreen
    }
})

這時候我們執行程式的時候會發現還沒有效果,因為我們還沒有把我們的TabNavigation.js引入咱們的App.js入口檔案(個人喜歡把App.js檔案當做入口檔案使用)當中:

App.js

import React, { Component } from 'react';

import TabNavigation from './TabNavigation'
type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <TabNavigation/>
    );
  }
}

當然你也可以選擇不要App.js檔案,直接從index.js指向TabNavigation.js

index.js

import { AppRegistry } from 'react-native';
import TabNavigation from './TabNavigation'
AppRegistry.registerComponent('csdnDemo', () => TabNavigation);

接下來我們在模擬器中執行:


發現底部有黃色警告,我這裡選擇將其遮蔽掉,這裡提示黃色警告的原因是react-navigation外掛用了已經再ES6中棄用的isMounted()方法,這隻能等待作者修復了,當然如果你有好的處理方法也不妨告訴我一下,大家共同成長嘛。

那麼我們在App.js(當然也可以在index.js)檔案中處理一下:

App.js

import React, { Component } from 'react';
------------------------------------------------------------
//引入YellowBox
import{
  YellowBox
}from 'react-native'
------------------------------------------------------------
import TabNavigation from './TabNavigation'
------------------------------------------------------------
//遮蔽對應的警告
YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated in plain JavaScript React classes.'])
------------------------------------------------------------
type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <TabNavigation/>
    );
  }
}

這時候我們再重新reload一下,發現又出現了其他的黃色警告


同樣這個警告我們也進行遮蔽處理,這個警告是init專案之後自帶的。

//遮蔽對應的警告
YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated in plain JavaScript React classes.',
'Module RCTImageLoader requires main queue setup since it overrides `init` '])

我們遮蔽掉上述警告後,再來reload一下,可以看到咱們的tab導航已經出來了。


不過咱們看到tab導航只顯示了文字,沒有圖片,這也太醜了吧,那麼接下來就是圖片外掛大展神通的時候了。先讓我們安裝一下這個外掛:

npm install --save react-native-vector-icons

安裝完成之後我們還需要執行以下命令:

react-native link

我們為什麼要執行這個命令呢,因為上述外掛用到了ios原生庫,所以我們需要link一下。接下來我們在需要用到上述外掛的TabNavigation.js檔案中匯入外掛:

import FontAwesome from 'react-native-vector-icons/FontAwesome'

這裡咱們需要注意一下,我這裡匯入的是FontAwesome的字型庫,你也可以匯入其他的字型庫,具體可以匯入的字型庫官網上都有列出來。好了,讓我們來進行下一步把。

import{
    TabNavigator
}from 'react-navigation'
import FontAwesome from 'react-native-vector-icons/FontAwesome'

import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'
export default TabNavigator({
    Home:{
        screen:HomeScreen
    },
    Video:{
        screen:VideoScreen
    },
    Info:{
        screen:InfoScreen
    }
},{
------------------------------------------------------------
    //導航選項 navigation 導航內建物件,裡面包含傳遞的引數、路由地址等
    navigationOptions:({ navigation })=>({
        //focused 選中狀態
        //tintColor 傳遞過來的系統顏色(預設選中時藍色,未選中時白色)
        tabBarIcon:({ focused, tintColor})=>{
            //獲取路由名稱
            const { routeName }=navigation.state
            let iconName
            //結合路由名稱和選中狀態進行動態圖示名稱賦值
            if (routeName==='Home') {
                iconName='home'
            }else if(routeName==='Video'){
                iconName=`play-circle${focused?'-o':''}`
            }else if(routeName==='Info'){
                iconName=`user${focused?'-o':''}`
            }
            //name 圖示名稱 size 圖示大小 color 圖示顏色
            return <FontAwesome name={iconName} size={25} color={tintColor}/>
        }
    })
}
------------------------------------------------------------
)

這時候我們重新react-native run-ios 一次,因為需要重新打包生成安裝程式,會發現還有個問題:


這個問題是因為當前TabNavigation.js檔案中沒有引入React導致的(該外掛用了react),我們只需要引入一下就可以了:

import React from 'react'

接下來我們reload一下,就可以看到我們久違的tab導航啦。


接下來我們再改變一下tab導航的背景色以及圖示的預設顏色和點選顏色:

tabBarOptions: {
        activeTintColor: 'tomato',
        inactiveTintColor: '#fff',
        style:{
            backgroundColor:'rgb(58,134,207)'
        }
    },

TabNavigation.js 

import{
    TabNavigator
}from 'react-navigation'
import React from 'react'
import FontAwesome from 'react-native-vector-icons/FontAwesome'

import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'
export default TabNavigator({
    Home:{
        screen:HomeScreen
    },
    Video:{
        screen:VideoScreen
    },
    Info:{
        screen:InfoScreen
    }
},{
    --------------------------------------------
    tabBarOptions: {
        activeTintColor: 'tomato',
        inactiveTintColor: '#fff',
        style:{
            backgroundColor:'rgb(58,134,207)'
        }
    },
    --------------------------------------------
    //導航選項 navigation 導航內建物件,裡面包含傳遞的引數、路由地址等
    navigationOptions:({ navigation })=>({
        //focused 選中狀態
        //tintColor 傳遞過來的系統顏色(預設選中時藍色,未選中時白色)
        tabBarIcon:({ focused, tintColor})=>{
            //獲取路由名稱
            const { routeName }=navigation.state
            let iconName
            //結合路由名稱和選中狀態進行動態圖示名稱賦值
            if (routeName==='Home') {
                iconName='home'
            }else if(routeName==='Video'){
                iconName=`play-circle${focused?'-o':''}`
            }else if(routeName==='Info'){
                iconName=`user${focused?'-o':''}`
            }
            //name 圖示名稱 size 圖示大小 color 圖示顏色
            return <FontAwesome name={iconName} size={25} color={tintColor}/>
        }
    })
})

我們再來看下咱們改變之後的tab導航


這時候我們想要的效果已經達到了,不過我還想改變一下tab導航顯示的名稱,比如改成首頁,視訊,我的。那麼我們需要在Home.js,VIdeo.js,Info.js都加上對應的配置:

static navigationOptions={
        tabBarLabel:'首頁'
    }

比如:Home.js

import React from 'react'
import{
    View,
    Text,
    StyleSheet
}from 'react-native'

const style=StyleSheet.create({
    container:{
        flex:1,
        alignItems:'center',
        justifyContent:'center'
    }
})
export default class Home extends React.PureComponent{
    ------------------------------
    static navigationOptions={
        tabBarLabel:'首頁'
    }
    -----------------------------
    render(){
        return(
            <View style={style.container}>
                <Text>Home</Text>
            </View>
        )
    }
}

我們再reload一下


好了,TabNavigator就先說到這裡,下面我們來看看StackNavigator


我們先建立一下StackNavigation.js檔案:(Detail是子頁面,也就是每個頁面的詳細頁面,子頁面都是在這裡進行配置的)

import{
    StackNavigator
}from 'react-navigation'

import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'
import DetailScreen from './Detail'
const HomeStack=StackNavigator({
    Home:{
        screen:HomeScreen
    },
    Detail:{
        screen:DetailScreen
    }
})
const VideoStack=StackNavigator({
    Video:{
        screen:VideoScreen
    },
    Detail:{
        screen:DetailScreen
    }
})
const InfoStack=StackNavigator({
    Info:{
        screen:InfoScreen
    },
    Detail:{
        screen:DetailScreen
    }
})

export{
    HomeStack,
    VideoStack,
    InfoStack
}

同時我們對TabNavigation.js做一下程式碼改動,形成TabNavigation.js對StackNavigation.js的包含

TabNavigation.js

import{
    TabNavigator
}from 'react-navigation'
import React from 'react'
import FontAwesome from 'react-native-vector-icons/FontAwesome'
/* 這段引入放到StackNavigation.js中,有StackNavigator來控制
import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'
*/
/* 增加對StackNavigation.js的匯入*/
import{
    HomeStack,
    VideoStack,
    InfoStack
}from './StackNavigation'
export default TabNavigator({
    /*由StackNavigator來負責具體每個頁面的導航,TabNavigator只負責tab導航上三個頁面的呈現和導航
    Home:{
        screen:HomeScreen
    },
    Video:{
        screen:VideoScreen
    },
    Info:{
        screen:InfoScreen
    }
    */
    Home:{
        screen:HomeStack
    },
    Video:{
        screen:VideoStack
    },
    Info:{
        screen:InfoStack
    }
},{
    tabBarOptions: {
        activeTintColor: 'tomato',
        inactiveTintColor: '#fff',
        style:{
            backgroundColor:'rgb(58,134,207)'
        }
    },
    //導航選項 navigation 導航內建物件,裡面包含傳遞的引數、路由地址等
    navigationOptions:({ navigation })=>({
        //focused 選中狀態
        //tintColor 傳遞過來的系統顏色(預設選中時藍色,未選中時白色)
        tabBarIcon:({ focused, tintColor})=>{
            //獲取路由名稱
            const { routeName }=navigation.state
            let iconName
            //結合路由名稱和選中狀態進行動態圖示名稱賦值
            if (routeName==='Home') {
                iconName='home'
            }else if(routeName==='Video'){
                iconName=`play-circle${focused?'-o':''}`
            }else if(routeName==='Info'){
                iconName=`user${focused?'-o':''}`
            }
            //name 圖示名稱 size 圖示大小 color 圖示顏色
            return <FontAwesome name={iconName} size={25} color={tintColor}/>
        }
    })
})

現在我們就形成了index.js>App.js>TabNavigation.js>StackNavigation.js的形式,這種形式的好處就是在於我們程式繼續擴充套件的情況下,可以非常明確的修改對應的程式碼。

這時候我們再reload一下,可以看到我們的header部分已經出來了。


不過我們發現並沒有標題的顯示,那麼和TabNavigator是一樣的,我們需要在Home.js,VIdeo.js,Info.js都加上對應的配置:

static navigationOptions={
        headerTitle:'首頁',
        tabBarLabel:'首頁'
    }

例如Home.js

import React from 'react'
import{
    View,
    Text,
    StyleSheet
}from 'react-native'

const style=StyleSheet.create({
    container:{
        flex:1,
        alignItems:'center',
        justifyContent:'center'
    }
})
export default class Home extends React.PureComponent{
    ------------------------------
    static navigationOptions={
        headerTitle:'首頁',
        tabBarLabel:'首頁'
    }
    -----------------------------
    render(){
        return(
            <View style={style.container}>
                <Text>Home</Text>
            </View>
        )
    }
}

再這裡我們注意下,headerTitle是顯示header標題的,tabBarLabel是顯示Tab文字的,那麼還有一個屬性title,他是同時控制兩者的,所我並不推薦使用,因為很多時候我們都需要兩者顯示不一樣的名稱。


現在我們看到我們的頭部標題已經出來了,我們再來設定一下頭部標題的背景色

StackNavigation.js

import{
    StackNavigator
}from 'react-navigation'

import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'
import DetailScreen from './Detail'
---------------------------------
//設定頭部背景顏色
const bgColor='rgb(58,134,207)'
//設定頭部文字顏色
const fontColor='#FFF'
--------------------------------

const HomeStack=StackNavigator({
    Home:{
        screen:HomeScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    ----------增加導航配置選項----------
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
        },
        headerTitleStyle:{
            color:fontColor
        }
    }
    --------------------------------
})
const VideoStack=StackNavigator({
    Video:{
        screen:VideoScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    --------------------------------
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
        },
        headerTitleStyle:{
            color:fontColor
        }
    }
    --------------------------------
})
const InfoStack=StackNavigator({
    Info:{
        screen:InfoScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    --------------------------------
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
        },
        headerTitleStyle:{
            color:fontColor
        }
    }
    --------------------------------
})

export{
    HomeStack,
    VideoStack,
    InfoStack
}

這裡我們可以看到每個頁面的樣式設定都是重複的,為什麼我們不提取出來公用呢,這樣還可以省了程式碼量,我的想法的不能保證每個頁首的佈局的樣式都要求一樣,比如某些頁面標題要按鈕或者是搜尋框,所以還是各自設定的話比較好,我們再reload一下,發現我們想要的效果已經出來了


不過,到了這裡我們還沒有結束,因為我們都知道android和ios在某些地方會有些不同,所以我們還需要在安卓虛擬機器上看一下我們的效果。

讓我們來看看在安卓模擬器上的表現,我去~怎麼長這樣


原來TabNavigator在安卓上預設就是至於頂部,而我們的StackNavigator則是在安卓上預設我們的標題在左側。那麼接下來,你懂得,只能著手進行調整了。首先我們來調整一下TabNavigator,引入TabBarBottom,然後指定tab元件和位置。

TabNavigation.js

import{
    TabNavigator,
    ----------------------------------
    TabBarBottom
    ----------------------------------
}from 'react-navigation'
import React from 'react'
import FontAwesome from 'react-native-vector-icons/FontAwesome'
import{
    HomeStack,
    VideoStack,
    InfoStack
}from './StackNavigation'
export default TabNavigator({
    Home:{
        screen:HomeStack
    },
    Video:{
        screen:VideoStack
    },
    Info:{
        screen:InfoStack
    }
},{
    tabBarOptions: {
        activeTintColor: 'tomato',
        inactiveTintColor: '#fff',
        style:{
            backgroundColor:'rgb(58,134,207)'
        }
    },
    ----------------------------------
    //指定tabBar元件
    tabBarComponent:TabBarBottom,
    //指定位置
    tabBarPosition: 'bottom',
    ----------------------------------
    //導航選項 navigation 導航內建物件,裡面包含傳遞的引數、路由地址等
    navigationOptions:({ navigation })=>({
        //focused 選中狀態
        //tintColor 傳遞過來的系統顏色(預設選中時藍色,未選中時白色)
        tabBarIcon:({ focused, tintColor})=>{
            //獲取路由名稱
            const { routeName }=navigation.state
            let iconName
            //結合路由名稱和選中狀態進行動態圖示名稱賦值
            if (routeName==='Home') {
                iconName='home'
            }else if(routeName==='Video'){
                iconName=`play-circle${focused?'-o':''}`
            }else if(routeName==='Info'){
                iconName=`user${focused?'-o':''}`
            }
            //name 圖示名稱 size 圖示大小 color 圖示顏色
            return <FontAwesome name={iconName} size={25} color={tintColor}/>
        }
    })
})

我們發現在安卓上在切換tab的時候會有預設的動畫和左右滑動切換,如果不想要這些的話我們可以做一下配置

TabNavigation.js(放在指定tab位置那裡)

animationEnabled: false,
    swipeEnabled: false,

好了,我們再reload一下,看下效果


不錯,接下來我們調整下標題的位置

StackNavigation.js

import{
    StackNavigator
}from 'react-navigation'

import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'
import DetailScreen from './Detail'
//設定頭部背景顏色
const bgColor='rgb(58,134,207)'
//設定頭部文字顏色
const fontColor='#FFF'

const HomeStack=StackNavigator({
    Home:{
        screen:HomeScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
        },
        headerTitleStyle:{
            ---------------------
            flex:1,
            textAlign:'center',
            ---------------------
            color:fontColor
        }
    }
})
const VideoStack=StackNavigator({
    Video:{
        screen:VideoScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
        },
        headerTitleStyle:{
            ---------------------
            flex:1,
            textAlign:'center',
            ---------------------
            color:fontColor
        }
    }
})
const InfoStack=StackNavigator({
    Info:{
        screen:InfoScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
        },
        headerTitleStyle:{
            ---------------------
            flex:1,
            textAlign:'center',
            ---------------------
            color:fontColor
        }
    }
})

export{
    HomeStack,
    VideoStack,
    InfoStack
}

到這一步我們發現android和ios上的表現基本上一致了,為什麼說基本上一致呢,因為我們安卓上還有一個沉浸式沒有實現,ios上是純天然自帶的。那麼這裡我們需要用到StatusBar這個react-native自帶的元件了。

Home.js

import React from 'react'
import{
    View,
    Text,
    StatusBar,
    StyleSheet
}from 'react-native'

const style=StyleSheet.create({
    container:{
        flex:1,
        alignItems:'center',
        justifyContent:'center'
    }
})
export default class Home extends React.PureComponent{
    static navigationOptions={
        headerTitle:'首頁',
        tabBarLabel:'首頁'
    }
    render(){
        return(
            <View style={style.container}>
            ---------------------------------------
            {/* 
                backgroundColor設定為白色,透明度為0
                barStyle 將狀態列文字設定為白色,IOS下有效
                translucent 為Ture時設定狀態列為透明,同時應用從狀態列下面開始繪製
             */}
                <StatusBar 
                backgroundColor={"#00000000"}
                barStyle="light-content"
                translucent={true}/>
            ----------------------------------------
                <Text>Home</Text>
            </View>
        )
    }
}

咱們再reload一下,發現咱們的沉浸式效果已經實現了


不過我們還需要微調一個地方,我們看到咱們的狀態列的高度沒有了,這也就導致我們的頭部文字太過於靠近頂部,那麼我們怎麼處理一下呢,幸好我們有StatusBar.currentHeight它能獲取咱們狀態列的高度,當然,在ios上是undefined。那麼我們需要在StackNavigation.js檔案裡面對header的高度做一下處理:

StackNavigation.js

import{
    StackNavigator
}from 'react-navigation'
-------------------------
//引入statusBar
import{
    StatusBar
}from 'react-native'
-------------------------

import HomeScreen from './Home'
import VideoScreen from './Video'
import InfoScreen from './Info'
import DetailScreen from './Detail'
-------------------------
//你想要的頭部高度
const headerHeight=56
//獲取狀態列的高度,因為在ios上是undefined,所以我們需要處理一下
const StuBarHeight=StatusBar.currentHeight?StatusBar.currentHeight:0
-------------------------
//設定頭部背景顏色
const bgColor='rgb(58,134,207)'
//設定頭部文字顏色
const fontColor='#FFF'

const HomeStack=StackNavigator({
    Home:{
        screen:HomeScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
            -------------------------
            height:StuBarHeight+headerHeight
            -------------------------
        },
        headerTitleStyle:{
            -------------------------
            //距離頂部狀態列的高度,這樣可以讓文字針對於真正的header居中而不是針對header+StatusBar的高度居中
            marginTop:StuBarHeight,
            -------------------------
            flex:1,
            textAlign:'center',
            color:fontColor
        }
    }
})
const VideoStack=StackNavigator({
    Video:{
        screen:VideoScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
            height:StuBarHeight+headerHeight
        },
        headerTitleStyle:{
            -------------------------
            marginTop:StuBarHeight,
            -------------------------
            flex:1,
            textAlign:'center',
            color:fontColor
        }
    }
})
const InfoStack=StackNavigator({
    Info:{
        screen:InfoScreen
    },
    Detail:{
        screen:DetailScreen
    }
},{
    navigationOptions:{
        headerStyle:{
            backgroundColor:bgColor,
            height:StuBarHeight+headerHeight
        },
        headerTitleStyle:{
            -------------------------
            marginTop:StuBarHeight,
            -------------------------
            flex:1,
            textAlign:'center',
            color:fontColor
        }
    }
})

export{
    HomeStack,
    VideoStack,
    InfoStack
}

好,再讓我們來看下ios和android上的表現,現在終於統一啦。SO,本文到此就結束了,當然,react-navigation絕對不止這麼些功能,我這裡描述的只是我在專案中所用到的一些部分,也許後續使用中還會涉及到其它功能也說不定。Bye~