[個人實戰]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~