Android Jetpack之Navigation導航系統
Navigators
Navigators允許你定義你的導航結構.Navigators也可以渲染普通的元素,例如你配置好的header和tab bar.
navigators可以是單純的React元件.
內建的Navigators
react-navigation
包含下面的幾個函式幫助你建立navigators:
StackNavigator TabNavigator DrawNavigator
使用Navigators渲染screen
navigators實際渲染的就是React元件
瞭解怎麼建立screen,讀讀一下內容:
navigation navigationOptions
在頂層元件上呼叫導航
萬一你想在同一級別的Navigation screen之間使用Navigator,你可以使用react的 ref
選項:
const AppNavigator = StackNavigator(SomeAppRouteConfigs); class App extends React.Component { someEvent() { // call navigate for AppNavigator here: this.navigator && this.navigator.dispatch({ type: 'Navigate', routeName, params }); } render() { return ( <AppNavigator ref={nav => { this.navigator = nav; }} /> ); } }
注意:這個解決辦法只能用在頂層navigator上.
Navigation Containers
如果navigators沒有props的話,他就會表現為頂層navigators.這個方式提供了一個透明的navigator container,這是頂層導航props的來源.
當渲染其中一個navigators的時候,navigation prop是可選的.如果沒有navigation prop,container將會管理自己的導航state.他也可以使用URLs,外部連結以及整合android的back button.
為了使用方便,在幕後內建的navigators有這個能力,因為在幕後他們使用了 createNavigationContainer
.通常,navigators需要一個navigation prop來執行一定的功能.
onNavigationStateChange(prevState, newState)
當navigation state由頂層navigator變化管理的時候,這一點非常有用.為了達到這個目的,這個函式在每次呼叫的時候都會使用導航之前的state和導航之後的新state作為引數.
containerOptions
當一個navigator在頂層被使用的時候,這些選項可以來配置這個navigator.
如果一個navigator配置了 containerOptions
,但是也接受了 navigation
prop,會丟擲錯誤.因為在這種情況下,navigator有兩種選擇,它就不知道怎麼做了.
-
URIPrefic
-app可以處理的URI字首.在處理deep link
的時候,可以提取路徑,並且傳遞到router.
StackNavigator
給你的app提供screen之間轉變的方法,每個轉變到的screen會存放在堆疊的棧頂.
預設情況下,StackNavigator配置有iOS和android的外觀和感覺:在iOS下,新的screen從螢幕的右側滑入,在android下,新的screen從底部淡入.iOS下也可以配置為從螢幕底部滑入.
class MyHomeScreen extends React.Component { static navigationOptions = { title: 'Home', } render() { return ( <Button onPress={() => this.props.navigation.navigate('Profile', {name: 'Lucy'})} title="Go to Lucy's profile" /> ); } } const ModalStack = StackNavigator({ Home: { screen: MyHomeScreen, }, Profile: { path: 'people/:name', screen: MyProfileScreen, }, });
定義API
StackNavigator(Routeconfigs,StackNavigatorConfig)
RouteConfigs
route的配置物件是route name到route config的對映(譯者:這才是重點),配置物件告訴navigator什麼來代表route.
StackNavigator({ // 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/:username', // The action and route params are extracted from the path. // Optional: Override the `navigationOptions` for the screen navigationOptions: { title: ({state}) => `${state.params.username}'s Profile'`, }, }, ...MyOtherRoutes, });
StackNavigatorConfig
Router的Options:
initialRouteName initalRouteParams navigationOptions path
視覺化選項:
-
mode
-定義渲染和切換之間的樣式:card modal
-
headerMode
-定製header渲染的方法float screen none
-
cardStyle
-使用這個prop來重寫或者擴充套件單個card的預設style -
onTransitionStart
-當card開始切換動畫的時候,這個函式被呼叫 -
onTransitionEnd
-當切換動畫完成的時候,這個函式被呼叫
Screen Navigation Options
通常在screen元件中定義靜態的 navigationOptions
.例如:
class ProfileScreen extends React.Component { static navigationOptions = { title: ({ state }) => `${state.params.name}'s Profile!`, header: ({ state, setParams }) => ({ // Render a button on the right side of the header // When pressed switches the screen to edit mode. right: ( <Button title={state.params.editing ? 'Done' : 'Edit'} onPress={() => setParams({editing: state.params.editing ? false : true})} /> ), }), }; ...
所有的 stackNavigator
的 navigationOptions
:
-
title
-scene的標題(字串) -
header
-header bar的配置物件-
visible
-header可視性的切換.只有當headerMode
是screen
的時候才可以工作 -
title
-header可以使用的字串或者React元件,預設是scene的title
-
backTitle
-iOS back按鈕的title字串或者null
到disable標籤,預設設定到scene的title
. -
right
-顯示在header右側的React元件 -
left
-同上,左側 -
style
-header的Style物件 -
titleStyle
-title組建的Style物件 -
tintColor
-header的著色
-
-
cardStack
-card stack的配置物件-
gesturesEnabled
-不管你是不是用手勢,在iOS上是true,在android裡是false.
-
Navigator Props
由 StackNavigator(...)
建立的navigator元件接收兩個props:
screenProps
-向下傳遞到子screen,例如:
const SomeStack = StackNavigator({ // config }); <SomeStack screenProps={/* this prop will get passed to the screen components as this.props.screenProps */} />
Examples
看看例項 SimpleStack.js 和 ModalStack.js ,可以在本地的 NavigationPlayground app中執行.
TabNavigator
通常很容易使用TabRouter來建立有幾個tabs的screen.
class MyHomeScreen extends React.Component { static navigationOptions = { tabBar: { label: 'Home', // Note: By default the icon is only shown on iOS. Search the showIcon option below. icon: ({ tintColor }) => ( <Image source={require('./chats-icon.png')} style={[styles.icon, {tintColor: tintColor}]} /> ), }, } render() { return ( <Button onPress={() => this.props.navigation.navigate('Notifications')} title="Go to notifications" /> ); } } class MyNotificationsScreen extends React.Component { static navigationOptions = { tabBar: { label: 'Notifications', icon: ({ tintColor }) => ( <Image source={require('./notif-icon.png')} style={[styles.icon, {tintColor: tintColor}]} /> ), }, } render() { return ( <Button onPress={() => this.props.navigation.goBack()} title="Go back home" /> ); } } const styles = StyleSheet.create({ icon: { width: 26, height: 26, }, }); const MyApp = TabNavigator({ Home: { screen: MyHomeScreen, }, Notifications: { screen: MyNotificationsScreen, }, }, { tabBarOptions: { activeTintColor: '#e91e63', }, });
定義API
TabNavigator(RouteConfigs,TabNavigator)
RouteConfigs
route的配置物件是route name到route config的對映(譯者:這才是重點),配置物件告訴navigator什麼來代表route.
TabNavigatorConfig
-
tabBarComponent
-作為tab bar的元件.例如,TabView.TabBarBottom
(ios的預設配置),TabView.TabBarTop
(android的預設配置) -
tabBarPosition
-tab bar的位置,可以是top
和bottom
-
swipeEnabled
-是否在tab之間滑動 -
animationEnabled
-變換tabs的時候是否開啟動畫效果 -
lazyLoad
-是否在需要的時候才惰性載入tabs,代替預渲染 -
tabBarOption
-配置tab bar,看下面
幾個Options可以傳遞到潛在的的router,修改導航的邏輯 -
initialRouteName
-初始化時載入的tab route -
order
-定義tabs順序的routeName的陣列 -
paths
-提供routeName到path配置的對映,重寫routeConfigs裡的paths設定 -
backBehavior
-back button是不是應該導致tab切換到初始的tab?入如果是的話,設定initialRoute
,否則就是none
.預設到initialRoute
的行為.
TabBarTop
的 tabBarOptions
設定(android預設的tab bar)
activeTintColor
inactiveTintColor
showIcon
showLabel
upperCaseLabel
pressColor
pressOpacity
scrollEnabled
tabStyle
indicatorStyle
labelStyle
style
例項:
tabBarOptions: { labelStyle: { fontSize: 12, }, style: { backgroundColor: 'blue', }, }
Screen導航的選項
通常在screen元件中定義靜態的 navigationOptions
.例如:
class ProfileScreen extends React.Component { static navigationOptions = { title: ({ state }) => `${state.params.name}'s Profile!`, tabBar: ({ state, setParams }) => ({ icon: ( <Image src={require('./my-icon.png')} /> ), }), }; ...
所有 TabNavigator
的 navigationOption
:
-
title
-scene的title(字串) -
tabBar
-tab bar的config物件:-
visible
-tab bar的可見性的切換 -
icon
-React元件或者函式給出{focused:boolean,tintColor:string}
,返回一個React元件,顯示在tab bar -
label
-顯示在tab bar中的tab的名字.如果定義為undefined,scene的title
會被使用.如果要隱藏,看前面部分的tabBarOption.showLabel
.
-
Navigator Props
由 TabNavigator(...)
建立的navigator元件接收下面的props:
-
screenProps
-向下傳遞額外的options給子screen,例如:
const TabNav = TabNavigator({ // config }); <TabNav screenProps={/* this prop will get passed to the screen components as this.props.screenProps */} />
抽屜式導航
用來構建抽屜式導航
class MyHomeScreen extends React.Component { static navigationOptions = { drawer: () => ({ label: 'Home', icon: ({ tintColor }) => ( <Image source={require('./chats-icon.png')} style={[styles.icon, {tintColor: tintColor}]} /> ), }), } render() { return ( <Button onPress={() => this.props.navigation.navigate('Notifications')} title="Go to notifications" /> ); } } class MyNotificationsScreen extends React.Component { static navigationOptions = { drawer: () => ({ label: 'Notifications', icon: ({ tintColor }) => ( <Image source={require('./notif-icon.png')} style={[styles.tabIcon, {tintColor: tintColor}]} /> ), }), } render() { return ( <Button onPress={() => this.props.navigation.goBack()} title="Go back home" /> ); } } const styles = StyleSheet.create({ icon: { width: 24, height: 24, }, }); const MyApp = DrawerNavigator({ Home: { screen: MyHomeScreen, }, Notifications: { screen: MyNotificationsScreen, }, });
開啟抽屜或者關閉抽屜,分別導航到 DrawerOpen
和 Drawerclose
.
this.props.navigation.navigate('DrawerOpen'); // open drawer this.props.navigation.navigate('DrawerClose'); // close drawer
定義API
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
RouteConfigs
參看前面的內容
DrawerNavigatonConfig
-
drawerWidth
-抽屜的寬度 -
drawerPosition
-選項是left
和right
.預設是left
. -
contentComponent
-用來渲染抽屜內容的元件,例如,navigation item.接收navigation
prop.預設是DrawerView.Items
.瞭解更多內容看下面內容. -
contentOptions
-配置drawer的內容,看下面內容
幾個選項傳遞給潛在的router,用來修改navigation的邏輯: -
initialRouteName
-初始化route的routeName -
order
-定義drawer item順序的routeName陣列 -
path
-提供一個routeName到path config的對映,重寫掉routeConfigs中的path配置 -
backBehavior
-back按鈕一定要返回到初始化的route嗎?如果是的話,設定到initialRoute
,否則就用none
.預設到initialRoute
的行為.
提供定製化的 contentComponent
可以使用 react-navigation
重寫預設的元件.
const CustomDrawerContentComponent = (props) => ( <View style={style.container}> <DrawerView.Items {...props} /> </View> ); const styles = StyleSheet.create({ container : { flex : 1, }, });
DrawerView.Item
的 contentOptions
配置
activeTintColor
activeBackgroundColor
inactiveTintColor
inactiveBackgroundColor
style
示例:
contentOptions: { activeTintColor: '#e91e63', style: { marginVertical: 0, } }
Screen導航的選項
通常在元件中定義靜態的 navigationOptions
.
class ProfileScreen extends React.Component { static navigationOptions = { title: ({ state }) => `${state.params.name}'s Profile!`, drawer: { icon: ( <Image src={require('./my-icon.png')} /> ), }, }; ...
所有的 DrawerNavigation
navigationOption
配置項
-
title
-scene的標題 -
drawer
-drawer的配置物件-
label
-字串,React元件或者函式被設定{fcoused:boolean,tinColor:string}
返回一個React元件,顯示在drawer的邊欄上.當label定義為undefined時,scene的``title被使用. -
icon
-React元件或者函式被設定為{fcoused:boolean,tintColor:string}
返回一個React元素,顯示在drawer的邊欄上.
-
Navigator 的Props
由 DrawerNavigator(...)
建立的navigator元件接受下面的props:
-
screenProps
-向下傳遞額外的options到子screen,例如:
const DrawerNav = DrawerNavigator({ // config }); <DrawerNav screenProps={/* this prop will get passed to the screen components as this.props.screenProps */} />
Screen Navigation Prop
app中的每個screen都接收navigation prop 包含下面的內容:
navigate state setParam goBack dispatch
Navigation Actions
所有的Navigation Actions都會返回一個物件,這個物件可以使用 navigation.dispatch
方法傳遞到router.
注意 :如果你想dispatch react-navigation,你應該使用這個庫提供的action creators.
下面的actions是可以使用的:
Navigate Reset Back Set Params Init
Navigate
Navigatie action
會使用 Navigate action
的結果來更新當前的state.
routeName params actions
import { NavigationActions } from 'react-navigation' const navigateAction = NavigationActions.navigate({ routeName: 'Profile', params: {}, action: NavigationActions.navigate({ routeName: 'SubProfileRoute'}) }) this.props.navigation.dispatch(navigateAction)
Reset
Reset
action刪掉所有的navigation state並且使用幾個actions的結果來代替.
-
index
—陣列-必選-navigationstate
中route
陣列中啟用route的index. -
actions
-陣列-必選項-Navigation Actions陣列,將會替代navigation state
import { NavigationActions } from 'react-navigation' const resetAction = NavigationActions.reset({ index: 0, actions: [ NavigationActions.navigate({ routeName: 'Profile'}) ] }) this.props.navigation.dispatch(resetAction)
怎麼使用 index
引數
index
引數被用來定製化當前啟用的route
例如:使用兩個routes Profile
和 Settings
給一個基礎的stakc navigation設定.為了重置route到經過 Settings
的啟用screen那一點,但是在堆疊中他又存放在 Setting
screen之上,你可以這麼做:
import { NavigationActions } from 'react-navigation' const resetAction = NavigationActions.reset({ index: 1, actions: [ NavigationActions.navigate({ routeName: 'Profile'}), NavigationActions.navigate({ routeName: 'Settings'}) ] }) this.props.navigation.dispatch(resetAction)
Back
返回到前一個screen並且關閉當前screen. back
action creator接受一個可選的引數:
-
key
-字串或者空-可選項-如果設定了,navigation將會從設定的key返回.如果是null,navigation將返回到任何地方.
import { NavigationActions } from 'react-navigation' const backAction = NavigationActions.back({ key: 'Profile' }) this.props.navigation.dispatch(backAction)
SetParams
當dispatching setParams的時候
,router將會產出一個新的state,這個state是已經改變了特定route的引數,以key作為身份驗證
params key
import { NavigationActions } from 'react-navigation' const setParamsAction = NavigationActions.setParams({ params: { title: 'Hello' }, key: 'screen-123', }) this.props.navigation.dispatch(setParamsAction)
Screen Navigation Options
每個screen都可以配置幾個方面的內容,這些內容影響到在父navigators中怎麼得到展示.
定製每一個可選項的兩種方法
靜態配置方法 :每一個navigation 可選項都可以被直接設定:
class MyScreen extends React.Component { static navigationOptions = { title: 'Great', }; ...
動態配置方法
要麼就採用函式式的方法,接受引數,然後返回可選項的值.
-
navigation
-screen的navigation prop和navigation.state
中screen的route -
childRouter
-如果screen是一個navigator,這個引數就是子代router.
class ProfileScreen extends React.Component { static navigationOptions = { title: (navigation, childRouter) => { return navigation.state.params.name + "'s Profile!"; }, }; ...
通用的Navigation Options
navigation的可選項 title
在每一個navigator之間是通用的,用來設定每一個screen的標題字串.
class MyScreen extends React.Component { static navigationOptions = { title: 'Great', }; ...
不像其他的navigation的可配置項僅僅由navigator view來使用,title 選項可以被環境變數使用來更新瀏覽器的標題或者app切換時候的標題.
預設的Navigation選項
在screen中定義 navigationOption
非常普遍,但是有時候在navigator中定義 navitationOptions
也是非常有用
想象下面的場景:你的 TabNavigator
代表app中的一個screen.他在頂層 StackNavigator
之內:
StackNavigator: - route1: A screen - route2: A TabNavigator
現在 route2
是啟用的,你可能會隱藏header,隱藏 route1
的header非常容易, route2
的header應該也很容易隱藏.這就是預設Navigation Option 要做的.可以在 navigationOptions
中設定:
TabNavigator({ profile: ProfileScreen, ... }, { navigationOptions: { header: { visible: false, }, }, });
提示:你仍然可以在子代導航screen上定製 navigationOptions
.-例如,上面的 ProfileScreen
.從screen獲得的 navigationOptions
會和從navigator來的配置按照鍵-鍵的方式融合在一起.無論在什麼而時間,navigator和screen定義相同的配置(例如: header
),screen會優先使用.因此,當 ProfileScreen
啟用的時候,你可以使header再次可見.
擴充套件預設配置 :為了使用screen特定的properties擴充套件預設配置,而不是重寫它,你可以像下面一樣配置選項:
class ProfileScreen extends React.Component { static navigationOptions = { header: (navigation, defaultHeader) => ({ ...defaultHeader, visible: true, }), } ... }
傳遞到函式的第二個引數作為在navigator中定義的 header
的預設值.
Tab Navigation Options
class TabScreen extends React.Component { static navigationOptions = { tabBar: ({ state }) => ({ label: 'Tab Label', icon: ({ tintColor }) => ( <Image source={require('./tab-icon.png')} style={[styles.icon, {tintColor: tintColor}]} /> ), visible: true }), }; };
label icon visible
Custom Navigation
一個navigator是任何包含router的React元件.這裡是一個基本navigator,使用router的API去獲得啟用元件來渲染
class MyNavigator extends React.Component { static router = MyRouter; render() { const { state, dispatch } = this.props.navigation; const { routes, index } = state; // Figure out what to render based on the navigation state and the router: const Component = MyRouter.getComponentForState(state); // The state of the active child screen can be found at routes[index] let childNavigation = { dispatch, state: routes[index] }; // If we want, we can also tinker with the dispatch function here, to limit // or augment our children's actions // Assuming our children want the convenience of calling .navigate() and so on, // we should call addNavigationHelpers to augment our navigation prop: childNavigation = addNavigationHelpers(childNavigation); return <Component navigation={childNavigation} />; } }
Navigation Prop
navigation prop傳遞給navigator的僅僅包含 state
和 dispatch
,這是當前的navigator的state,但是還有一個事件頻道用來發送action request.
所有的navigators都是受控元件:他們總是顯示根據 props.navigation.state
來顯示,他們要改變state,唯一的辦法是傳送actions到 props.navigation.dispatch
.
Navigators可以通過定製他們的router來改變父navigators的行為.例如,當action應該從 router.getStateForAction
返回null來阻止其執行的時候.或者一個navigator可以為了定製URI的操作而改寫 router.getActionForPathParams
,為了輸出相對navigation action以及操作 router.getStateForAction
的action.
Navigation State
傳遞到 props.navigation.state
的navigation state有下面的結構:
{ index: 1, // identifies which route in the routes array is active routes: [ { // Each route needs a name, which routers will use to associate each route // with a react component routeName: 'MyRouteName', // A unique id for this route, used to keep order in the routes array: key: 'myroute-123', // Routes can have any additional data. The included routers have `params` ...customRouteData, }, ...moreRoutes, ] }
Navigation Dispatchers
navigator可以dispatch navigation actions,例如Go to URI,Go back.
如果action被成功操作了,dispatcher將會返回true,否則就是false
構建定製navigators的API
為了幫助開發者實施定製navigators,React Navigation提供了下面的工具
createNavigator
這個工具使用標準方法把router和navigation view合併在一起.
const MyApp = createNavigator(MyRouter)(MyView);
幕後所做的是:
const MyApp = ({ navigation }) => ( <MyView router={MyRouter} navigation={navigation} /> ); MyApp.router = MyRouter;
addNavigationHelpers
接收一個擁有 state
和 dispatch
的純navigator的prop,傳遞的引數是在screen navigation prop中的各種函式,例如 navigation.navigate()
和 navigation.goBack()
.這些函式是簡單的助手函式幫助建立action並且傳送到 dispatch
.
createNavigationContainer
如果你想讓你的navigator作為頂層元件使用(沒有navigation prop傳入),你可以使用 createNavigationContainer
.當缺少navigtion prop的時候,這個工具使你的navigator看起來像一個頂層的導航元件.它將管理app的state,和app級別的導航特性整合在一起,例如操作進出的連結和android的返回按鈕行為。
【附錄】

資料圖
需要資料的朋友可以加入Android架構交流QQ群聊:513088520
點選連結加入群聊【Android移動架構總群】: 加入群聊
獲取免費學習視訊,學習大綱另外還有像高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。