本文是在react-navigation v2版本基礎上編輯的。中文文檔傳送口
為了便于牢記react-navigation的功能,我將其分成了幾個小塊:
- StackNavigator
堆棧導航- Tab Navigator
Tab菜單- Drawer Navigator
側邊欄菜單- SwitchNavigator
- navigation
navigation屬性- withNavigation
傳遞navigation- android物理返回鍵重寫
- Stack navigator、Tab navigator等相互嵌套
- SafeAreaView
1.StackNavigator
stack navigator 為你的應用提供了一種在屏幕之間切換并管理導航歷史記錄的方式。 如果您的應用程序只使用一個 stack navigator ,則它在概念上類似于Web瀏覽器處理導航狀態的方式 - 當用戶與它進行交互時,應用程序會從導航堆棧中新增和刪除頁面,這會導致用戶看到不同的頁面。 Web瀏覽器和 React Navigation 工作原理的一個主要區別是:React Navigation 的 stack navigator 提供了在 Android 和 iOS 設備上,在堆棧中的路由之間導航時你期望的手勢和動畫。
創建一個stack navigator
通過createStackNavigator(RouteConfigs, StackNavigatorConfig)
函數,返回一個React組件。
RouteConfigs:
createStackNavigator({
// For each screen that you can navigate to, create a new entry like this:
Profile: {
// `ProfileScreen` is a React component that will be the main content of the screen.
screen: ProfileScreen,
// When `ProfileScreen` is loaded by the StackNavigator, it will be given a `navigation` prop.
// Optional: When deep linking or using react-navigation in a web app, this path is used:
path: 'people/:name',
// The action and route params are extracted from the path.
// Optional: Override the `navigationOptions` for the screen
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.name}'s Profile'`,
}),
},
...MyOtherRoutes,
});
screen添加到stack navigation中時,以key:object
的形式添加,key必須唯一,object必須屬性:screen,選配屬性path、navigationOptions。此處的navigationOptions是針對本screen的配置。
StackNavigatorConfig:
-
initialRouteName
:設置堆棧的默認屏幕。 必須匹配路由配置中的某個 Key。 -
initialRouteParams
: 初始路由的參數 -
navigationOptions
: 用于屏幕的默認導航選項。是對stack navigator內所有screen的配置,可重寫。 -
mode
: 定義渲染和轉換的樣式;
card - 使用標準的 iOS 和 Android 屏幕轉換。 這是默認設置。
modal -使屏幕從底部滑動, 這是一個常見的 iOS 模式。僅在 iOS 上工作, 對 Android 沒有影響。 -
headerMode
: 指定標題欄的呈現方式:
float -呈現一個位于頂部的單個標題欄, 并在屏幕被更改時進行動畫顯示。這是 iOS 上常見的模式。
screen -每個屏幕都有一個標頭, 并且標題欄隨屏幕一起淡入和淡出。這是 Android 的常見模式。
none -不會呈現標題欄。
2. Tab Navigator
屏幕底部的一個簡單的選項卡欄, 可以在不同的路由之間切換。路由被懶加載--他們的屏幕組件在第一次聚焦之前不會載入。(以底部選項卡進行說明)
-
創建方式
:有三種,分別對應了底部選項卡欄、屏幕底部的材料設計主題標簽欄、屏幕頂部的材料設計主題標簽欄。 -
initialRouteName
:第一次加載時初始選項卡路由的 routeName。 -
order
:定義選項卡順序的 routeNames 數組。 -
backBehavior
:控制 "返回" 按鈕是否會導致 Tab 頁切換到初始 Tab 頁? 如果是, 設置為 initialRoute, 否則 none。 默認為 initialRoute的行為。 -
tabBarComponent
: -可選,覆蓋用作標簽欄的組件。 -
BottomTabNavigatorConfig
:tabbar配置選項。 -
navigationOptions
:導航器內部的navigationOptions。
示例代碼:
const MineStack = createStackNavigator(
{
Mine:MineScreen,
Setting:SettingScreen
}
)
const HomeStack = createStackNavigator(
{
Home:HomeScreen
}
)
const TabNavigator = createBottomTabNavigator(
{
Home2:HomeStack,
Detail: DetailScreen,
Mine: MineStack,
}
)
需要注意的是,TabNavigator中的tab頁面是沒有標題欄的。
3. Drawer Navigator
側邊抽屜式菜單導航。
drawerWidth
:抽屜的寬度或返回它的函數。drawerPosition
:選項為 左 或 右. 默認值為 左 位置。contentComponent
:用于呈現抽屜內容 (例如, 導航項) 的組件。 接收用于抽屜的 navigation 支柱。 默認為 DrawerItems。contentOptions
:配置抽屜內容useNativeAnimations
:啟用本機動畫。默認值為 true。drawerBackgroundColor
:使用某種顏色的抽屜背景。默認值為 白色。initialRouteName
:第一次加載時初始選項卡路由的 routeName。order
:定義選項卡順序的 routeNames 數組。backBehavior
:控制 "返回" 按鈕是否會導致 Tab 頁切換到初始 Tab 頁? 如果是, 設置為 initialRoute, 否則 none。 默認為 initialRoute的行為。navigationOptions
:頁面導航選項.title
:可用作headerTitle和tabBarLabel的后備的通用標題。drawerLabel
:字符串,React 元素或給定{focused:boolean,tintColor:string}的函數返回一個React.Node,以顯示在抽屜邊欄中。 未定義時,使用場景titledrawerIcon
:React 元素或給定{ focused: boolean, tintColor: string }的函數返回一個 React.Node,用于在標簽欄中顯示。drawerLockMode
:指定抽屜的鎖定模式。 這也可以通過在頂級路由器上使用 screenProps.drawerLockMode 動態更新。open/close
:通過navigation的api開啟/關閉drawer navigator。
4. SwitchNavigator
SwitchNavigator的目的是一次只顯示一個屏幕。默認情況下,它不處理返回操作,并在你切換時將路由重置為默認狀態。
-
initialRouteName
:第一次加載時初始選項卡路由的 routeName。 -
resetOnBlur
:切換離開屏幕時,重置所有嵌套導航器的狀態。 默認為true。 -
backBehavior
:控制 "返回" 按鈕是否會導致 Tab 頁切換到初始 Tab 頁? 如果是, 設置為 initialRoute, 否則 none。 默認為none行為。
5. Navigation Prop
重要的是要強調導航道具沒有傳遞到所有組件;只有屏幕組件自動接收navigation!
-
navigate
:轉到另一個屏幕,如果已存在,轉到已存在的屏幕,如果不存在,則增加。
navigation.navigate({routeName, params, action, key})
navigation.navigate(routeName, params, action)
-
addListener
:訂閱導航生命周期的更新。
React Navigation發射事件以屏幕訂閱它們的組件:willBlur、willFocus、didFocus、didBlur
const didBlurSubscription = this.props.navigation.addListener(
'didBlur',
payload => {
console.debug('didBlur', payload);
}
);
// Remove the listener when you are done
didBlurSubscription.remove();
The JSON payload:
{
action: { type: 'Navigation/COMPLETE_TRANSITION', key: 'StackRouterRoot' },
context: 'id-1518521010538-2:Navigation/COMPLETE_TRANSITION_Root',
lastState: undefined,
state: undefined,
type: 'didBlur',
};
-
isFocused
:如果屏幕聚焦則返回true,否則返回false。 -
state
:屏幕的當前狀態/路由
{
// the name of the route config in the router
routeName: 'profile',
//a unique identifier used to sort routes
key: 'main0',
//an optional object of string options for this screen
params: { hello: 'world' }
}
-
dispatch
:向路由器發送一個動作
import { NavigationActions } from 'react-navigation';
const navigateAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
// navigate can have a nested navigate action that will be run inside the child router
action: NavigationActions.navigate({ routeName: 'SubProfileRoute' }),
});
this.props.navigation.dispatch(navigateAction);
-
push
:添加新路由到堆棧,不管堆棧中是否已有相同key的路由。
navigation.push(routeName, params, action)
-
pop
:帶你到堆棧中的前一個屏幕。如果您提供一個數字“n”,它將指定多少屏幕將您帶回堆棧。
navigation.pop(n)
-
popToTop
:將其調用以跳回堆棧中的頂部路徑,關閉所有其他屏幕。。 -
replace
:用新的路由替換當前的路由。
navigation.replace(routeName, params, action)
如果當前導航器是堆棧導航器,navigation prop才具有額外的 push、pop、popToTop、replace。
6. withNavigation
withNavigation是一個高階組件,它可以將 navigation 這個 prop 傳遞到一個包裝的組件。 當你無法直接將navigation 這個 prop 傳遞給組件,或者不想在深度嵌套的子組件中傳遞它時,它將非常有用。
withNavigation (Component)
返回一個Component
import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />;
}
}
// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);
7. android物理返回鍵重寫
默認情況下,當用戶按下 Android 物理返回鍵時,react-navigation 會返回到新頁面,如果沒有可返回的頁面,則退出應用。 這是一個合理的默認行為,但有些情況下你可能想要實現自定義處理。
推薦的第三方依賴:react-navigation-backhandler
我的方式是:定義BaseComponent,需要重寫android返回鍵的,集成該組件。
let lastBackPressed = 0;
export default class BaseComponent extends Component {
_didFocusSubscription;
_willBlurSubscription;
constructor(props) {
super(props);
this._didFocusSubscription = props.navigation.addListener('didFocus', payload =>
BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
);
}
componentDidMount() {
this._willBlurSubscription = this.props.navigation.addListener('willBlur', payload =>
BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
);
}
componentWillUnmount() {
this._didFocusSubscription && this._didFocusSubscription.remove();
this._willBlurSubscription && this._willBlurSubscription.remove();
}
onBackButtonPressAndroid = () => {
let now = new Date().getTime();
if(now - lastBackPressed < 2500) {
return false;
}
lastBackPressed = now;
ToastAndroid.show('再點擊一次退出應用',ToastAndroid.SHORT);
return true;
};
}
其中使用到了navigation的addListener屬性。
onBackButtonPressAndroid
返回true,則react-navigation將不會接收到返回事件,當返回false時,react-navigation將處理返回事件,返回上個路由或者退出應用。
8. Stack navigator、Tab navigator等相互嵌套
navigator之間的相互嵌套示例:
const DrawNavigator = createDrawerNavigator(
{
Draw1:DrawScreen1,
Draw2: DrawScreen2,
}
)
const MineStack = createStackNavigator(
{
Mine:MineScreen,
Setting:SettingScreen
}
)
const HomeStack = createStackNavigator(
{
Home:HomeScreen
}
)
HomeStack.navigationOptions={tabBarLabel: 'Home!',}
//****stack navigator嵌套在Tab navigator中
const TabNavigator = createBottomTabNavigator(
{
Home2:HomeStack,
Detail: DetailScreen,
Mine: MineStack,
},
)
TabNavigator.navigationOptions = {header:null}
//****Draw navigator 和Tab navigator 嵌套在stack navigator中
const RootStack = createStackNavigator(
{
Draw:DrawNavigator,
Tab: TabNavigator
},{
initialRouteName: 'Tab',
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
關注下TabNavigator.navigationOptions = {header:null}
代碼設置,其效果是將父navigator(stack navigator)的標題欄隱藏。
Why?
TabNavigator是一個React組件,TabNavigator.navigationOptions配置的是TabNavigator組件的的導航配置,是相對于父navigator(組件)的導航配置。我們其稱之為TabNavigator外部navigationOptions
。
那么內部的navigationOptions如何理解呢?
TabNavigator的內部navigationOptions
指的是tab navigator包含的組件(screen)中的tabBar相關的configs(tab navigator包含的screen是沒有標題欄的)。那么tabNavigator的內部navigationOptions該如何設置呢?示例代碼如下:
const TabNavigator = createBottomTabNavigator(
{
Home2:HomeStack,
Detail: DetailScreen,
Mine: MineStack,
},{
navigationOptions:{
title:'Test'
}
}
)
上述代碼中navigationOptions是針對TabNavigator內部所有的screen而言,可在screen中重寫效果。
關于外部navigationOptions
和內部navigationOptions
的概念,同樣適用于其他navigator,如HomeStack.navigationOptions={tabBarLabel: 'Home!',}
,HomeStack是Tab navigator的包含的一個組件,外部的navigationOptions就是設置這個組件的tabbar配置。
9. SafeAreaView
默認情況下,React Navigation 會幫助確保您的應用程序在iPhoneX上正確顯示。 它通過在可能與傳感器集群(“齊劉海”)或 Home 主頁指示器交互的UI元素內部使用SafeAreaView來實現。
當我們隱藏/自定義導航欄或選項卡欄時,可能存在以下問題:
Landscape Mode(橫屏模式)時,可能的錯誤:
此時,我們可以 通過使用SafeAreaView
包裝內容,避免這些問題。示例代碼如下:
import { SafeAreaView } from 'react-navigation';
class App extends Component {
render() {
return (
<SafeAreaView style={styles.container}>
<Text style={styles.paragraph}>
This is top text.
</Text>
<Text style={styles.paragraph}>
This is bottom text.
</Text>
</SafeAreaView>
);
}
}
使用 forceInset
獲得更多控制
在某些情況下,你可能需要更多控制應用哪些填充。 例如,你可以通過將forceInset 這個 prop 傳遞給SafeAreaView來移除底部填充。
<SafeAreaView style={styles.container} forceInset={{ bottom: 'never' }}>
<Text style={styles.paragraph}>
This is top text.
</Text>
<Text style={styles.paragraph}>
This is bottom text.
</Text>
</SafeAreaView>
forceInset使用 key(取值:top | bottom | left | right | vertical | horizontal)和value(取值:'always' | 'never')組成一個對象 或者,您可以通過傳遞一個整數來完全重寫填充。