ReactNative進階(七):導航組件react-navigation(react native導航欄)
前言
在RN項目開發過程中,經常會看到如下形式的路由跳轉。
render() { return (
今天就結合RN官方推薦的路由導航組件react-navigation,深入了解下相關技能知識。
總覽
React Native 中,官方推薦使用 react-navigation 來實現各個界面的跳轉和不同板塊的切換。react-navigation據稱有原生般的性能體驗效果。可能會成為未來React Native導航組件的主流軍。主要有三部分組成:
1、StackNavigator: 類似于普通的Navigator,屏幕上方導航欄,用于實現各個頁面之間的跳轉;
2、TabNavigator: 相當于iOS里面的TabBarController,屏幕下方的標簽欄,主要用于同一個頁面上不同界面之間的切換;
3、 DrawerNavigator: 抽屜效果,側邊滑出;
StackNavigator 導航欄
StackNavigator(RouteConfigs, StackNavigatorConfig)
StackNavigator 組件采用堆棧式的頁面導航來實現各個界面跳轉。
首先如果想要使用StackNavigator,一定要先注冊導航。
const Navigator = StackNavigator( { Tab: { screen: Tab }, Web: { screen: WebScene }, GroupPurchase: { screen: GroupPurchaseScene }, }, { navigationOptions: { // headerStyle: { backgroundColor: color.theme } headerBackTitle: null, headerTintColor: '#333333', showIcon: true, }, } );
這里給導航器配置了三個界面,如果需要更多界面,可以在之后進行添加。主要有一個用于主界面之間能夠切換的Tab界面和其他兩個能夠展示數據的界面。
StackNavigatorConfig 導航器配置
initialRouteName - 導航器組件中初始顯示頁面的路由名稱,如果不設置,則默認第一個路由頁面為初始顯示頁面;
initialRouteParams - 給初始路由的參數,在初始顯示的頁面中可以通過this.props.navigation.state.params 來獲取 navigationOptions -路由頁面的配置選項,它會被 RouteConfigs 參數中navigationOptions 的對應屬性覆蓋;
paths -路由中設置的路徑覆蓋映射配置;
mode - 頁面跳轉方式,有 card 和 modal 兩種,默認為 card :
card - 原生系統默認的的跳轉,左右互換;
modal - 只針對iOS平臺,模態跳轉,上下切換;
headerMode - 頁面跳轉時,頭部的動畫模式,有 float 、 screen 、 none 三種:
float -漸變,類似iOS的原生效果,無透明;
screen - 標題與屏幕一起淡入淡出,如微信一樣;
none - 沒有動畫;
cardStyle -為各個頁面設置統一的樣式,比如背景色,字體大小等;
transitionConfig - 配置頁面跳轉的動畫,覆蓋默認的動畫效果;
onTransitionStart - 頁面跳轉動畫即將開始時調用;
onTransitionEnd - 頁面跳轉動畫一旦完成會馬上調用;
代碼示例:
const StackNavigatorConfig = { initialRouteName: 'Home', initialRouteParams: {initPara: '初始頁面參數'}, navigationOptions: { title: '標題', headerTitleStyle: {fontSize: 18, color: '#666666'}, headerStyle: {height: 48, backgroundColor: '#'}, }, paths: 'page/main', mode: 'card', headerMode: 'screen', cardStyle: {backgroundColor: "#ffffff"}, transitionConfig: (() => ({ screenInterpolator: CardStackStyleInterpolator.forHorizontal, })), onTransitionStart: (() => { console.log('頁面跳轉動畫開始'); }), onTransitionEnd: (() => { console.log('頁面跳轉動畫結束'); }), };
navigationOptions 為對應路由頁面的配置選項
title - 可以作為頭部標題 headerTitle ,或者Tab標題 tabBarLabel;
header -自定義的頭部組件,使用該屬性后系統的頭部組件會消失,如果想在頁面中自定義,可以設置為null,這樣就不會出現頁面中留有一個高度為64 navigationBar的高度;
headerTitle - 頭部的標題,即頁面的標題 headerBackTitle - 返回標題,默認為 title;
headerTruncatedBackTitle - 返回標題不能顯示時(比如返回標題太長了)顯示此標題,默認為 “Back”;
headerRight - 頭部右邊組件;
headerLeft - 頭部左邊組件;
headerStyle - 頭部組件的樣式;
headerTitleStyle - 頭部標題的樣式;
headerBackTitleStyle - 頭部返回標題的樣式;
headerTintColor - 頭部顏色 headerPressColorAndroid - Android 5.0以上MD風格的波紋顏色 ;
gesturesEnabled - 否能側滑返回, iOS 默認 true , Android 默認 false;
使用示例
// 注冊導航 const Navs = StackNavigator({ Home: { screen: Tabs }, HomeTwo: { screen: HomeTwo, // 必須, 其他都是非必須 path:'app/homeTwo', // 使用url導航時用到, 如 web app 和 Deep Linking navigationOptions: {} // 此處參數設置會覆蓋組件內的`static navigationOptions`設置. 具體參數詳見下文; }, HomeThree: { screen: HomeThree }, HomeFour: { screen: HomeFour } }, { initialRouteName: 'Home', // 默認顯示界面 navigationOptions: { // 屏幕導航的默認選項, 也可以在組件內用 static navigationOptions 設置(會覆蓋此處的設置) header: { // 導航欄相關設置項 backTitle: '返回', // 左上角返回鍵文字 style: { backgroundColor: '#fff' }, titleStyle: { color: 'green' } }, cardStack: { gesturesEnabled: true } }, mode: 'card', // 頁面切換模式, 左右是card(相當于iOS中的push效果), 上下是modal(相當于iOS中的modal效果) headerMode: 'screen', // 導航欄的顯示模式, screen: 有漸變透明效果, float: 無透明效果, none: 隱藏導航欄 onTransitionStart: ()=>{ console.log('導航欄切換開始'); }, // 回調 onTransitionEnd: ()=>{ console.log('導航欄切換結束'); } // 回調 });
當然頁面配置選項 navigationOptions 還可以在對應頁面中去靜態配置。
// 配置頁面導航選項 static navigationOptions = ({navigation}) => ({ title: 'HOME', titleStyle: {color: '#ff00ff'}, headerStyle:{backgroundColor:'#000000'} }); render() { return (
在頁面采用靜態方式配置 navigationOptions 中的屬性,會覆蓋 StackNavigator 構造函數中兩個參數 RouteConfigs 和 StackNavigatorConfig 配置的 navigationOptions 里面的對應屬性。
navigationOptions 中屬性的優先級是:
頁面中靜態配置 > RouteConfigs > StackNavigatorConfig
至此,就可以在組件中直接使用該配置了,當然我們也可以像文章開頭提過的那樣使用:
const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig); export default class Main extends Component { render() { return (
至此,我們已經配置好導航器和對應的路由頁面,但是我們還需要navigation才能實現頁面的跳轉。
navigation 控制頁面跳轉
在導航器中的每一個頁面,都有 navigation 屬性,該屬性有以下幾個屬性/方法,可以在組件下console.log(this.props)查看。
navigate - 跳轉到其他頁面;
state - 當前頁面導航器的狀態;
setParams - 更改路由的參數;
goBack - 返回;
dispatch - 發送一個action;
navigate
this.props.navigation.navigate(‘Two’, { name: ‘two’ }) // push下一個頁面
routeName: 注冊過的目標路由名稱;
params: 傳遞的參數,傳遞到下一級界面 ;
action:如果該界面是一個navigator的話,將運行這個sub-action;
state
state 里面包含有傳遞過來的參數 params、key 、路由名稱 routeName。
routeName: 路由名;
key: 路由身份標識;
params: 參數;
setParams
this.props.navigation.setParams
該方法允許界面更改router中的參數,可以用來動態更改導航欄的內容。比如可以用來更新頭部的按鈕或者標題。
goBack
返回上一頁,可以不傳參數,也可以傳參數,還可以傳 null 。
this.props.navigation.goBack(); // 回退到上一個頁面 this.props.navigation.goBack(null); // 回退到任意一個頁面 this.props.navigation.goBack('Home'); // 回退到Home頁面
dispatch
this.props.navigation.dispatch
可以dispatch一些action,主要支持的action有:
1. Navigate
import { NavigationActions } from 'react-navigation' const navigationAction = 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(navigationAction)
2. Reset
Reset方法會清除原來的路由記錄,添加上新設置的路由信息, 可以指定多個action,index是指定默認顯示的那個路由頁面, 注意不要越界!
import { NavigationActions } from 'react-navigation' const resetAction = NavigationActions.reset({ index: 0, actions: [ NavigationActions.navigate({ routeName: 'Profile'}), NavigationActions.navigate({ routeName: 'Two'}) ] }) this.props.navigation.dispatch(resetAction)
SetParams
為指定的router更新參數,該參數必須是已經存在于router的param中。
import { NavigationActions } from 'react-navigation' const setParamsAction = NavigationActions.setParams({ params: {}, // these are the new params that will be merged into the existing route params // The key of the route that should get the new params key: 'screen-123', }) this.props.navigation.dispatch(setParamsAction)
頁面跳轉,傳值,回調傳參
跳轉,傳值
const {navigate} = this.props.navigation;
// 返回上一頁 this.props.navigation.goBack();
在下一界面接收參數
通過 this.props.navigation.state.params 接收參數
export default class Home1 extends Component { static navigationOptions = { // title 可以這樣設置成一個函數, state 會自動傳過來 title: ({state}) => `${state.params.name}`, }; componentDidMount() { const {params} = this.props.navigation.state; const id = params.id; } }
回調傳參
當前界面進行跳轉
navigate('Detail',{ // 跳轉的時候攜帶一個參數去下個頁面 callback: (data)=>{ console.log(data); // 打印值為:'回調參數' } });
下一界面在返回之前傳參
const {navigate,goBack,state} = this.props.navigation; // 在第二個頁面,在goBack之前,將上個頁面的方法取到,并回傳參數,這樣回傳的參數會重走render方法 state.params.callback('回調參數'); goBack();
TabNavigator 即Tab選項卡
TabNavigator(RouteConfigs, TabNavigatorConfig)
api和 StackNavigator 類似,參數 RouteConfigs 是路由配置,參數 TabNavigatorConfig是Tab選項卡配置。
RouteConfigs 路由配置
路由配置和 StackNavigator 中是一樣的,配置路由以及對應的 screen 頁面,navigationOptions 為對應路由頁面的配置選項:
title - Tab標題,可用作headerTitle 和 tabBarLabel 回退標題;
tabBarVisible -Tab是否可見,沒有設置的話默認為 true;
tabBarIcon - Tab的icon組件,可以根據 {focused:boolean, tintColor: string} 方法來返回一個icon組件;
tabBarLabel -Tab中顯示的標題字符串或者組件,也可以根據 { focused: boolean, tintColor: string };方法返回一個組件;
代碼示例:
Mine: { screen: MineScene, navigationOptions: ({ navigation }) => ({ tabBarLabel: '我的', tabBarIcon: ({ focused, tintColor }) => (
TabBarItem自定義組件
class TabBarItem extends PureComponent { render() { let selectedImage = this.props.selectedImage ? this.props.selectedImage : this.props.normalImage return (
TabNavigatorConfig Tab選項卡配置
tabBarComponent - Tab選項卡組件,有 TabBarBottom 和 TabBarTop 兩個值,在iOS中默認為 TabBarBottom ,在Android中默認為 TabBarTop 。
TabBarTop - 在頁面頂部;
TabBarBottom - 在頁面底部;
tabBarPosition - Tab選項卡的位置,有top或bottom兩個值
top:上面
bottom:下面
swipeEnabled - 是否可以滑動切換Tab選項卡;
animationEnabled - 點擊Tab選項卡切換界面是否需要動畫;
lazy - 是否懶加載頁面;
initialRouteName - 初始顯示的Tab對應的頁面路由名稱;
order - 用路由名稱數組來表示Tab選項卡的順序,默認為路由配置順序;
paths - 路徑配置;
backBehavior - android點擊返回鍵時的處理,有 initialRoute 和 none 兩個值:
initailRoute - 返回初始界面;
none - 退出;
tabBarOptions - Tab配置屬性,用在TabBarTop和TabBarBottom時有些屬性不一致:
用于 TabBarTop 時:
activeTintColor - 選中的文字顏色;
inactiveTintColor - 未選中的文字顏色;
showIcon -是否顯示圖標,默認顯示;
showLabel - 是否顯示標簽,默認顯示;
upperCaseLabel - 是否使用大寫字母,默認使用;
pressColor - android 5.0以上的MD風格波紋顏色;
pressOpacity - android5.0以下或者iOS按下的透明度;
scrollEnabled - 是否可以滾動;
tabStyle - 單個Tab的樣式;
indicatorStyle - 指示器的樣式;
labelStyle - 標簽的樣式;
iconStyle - icon的樣式;
style -整個TabBar的樣式;
用于 TabBarBottom 時:
activeTintColor - 選中Tab的文字顏色;
inactiveTintColor - 未選中Tab的的文字顏色;
activeBackgroundColor - 選中Tab的背景顏色;
inactiveBackgroundColor -未選中Tab的背景顏色;
showLabel - 是否顯示標題,默認顯示;
style - 整個TabBar的樣式;
labelStyle -標簽的樣式;
tabStyle - 單個Tab的樣式;
使用底部選項卡:
import React, {Component} from 'react'; import {StackNavigator, TabBarBottom, TabNavigator} from "react-navigation"; import HomeScreen from "./index18/HomeScreen"; import NearByScreen from "./index18/NearByScreen"; import MineScreen from "./index18/MineScreen"; import TabBarItem from "./index18/TabBarItem"; export default class MainComponent extends Component { render() { return (
使用頂部選項卡:
import React, {Component} from "react"; import {StackNavigator, TabBarTop, TabNavigator} from "react-navigation"; import HomeScreen from "./index18/HomeScreen"; import NearByScreen from "./index18/NearByScreen"; import MineScreen from "./index18/MineScreen"; export default class MainComponent extends Component { render() { return (
DrawerNavigator 抽屜導航
有一些APP都會采用側滑抽屜來實現主頁面導航,利用 DrawerNavigator 在RN中可以很方便實現抽屜導航。
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
和TabNavigator的構造函數一樣,參數配置也類似。
RouteConfigs
抽屜導航的路由配置 RouteConfigs ,和 TabNavigator 的路由配置完全一樣, screen 對應路由頁面配置, navigationOptions 對應頁面的抽屜配置:
title - 抽屜標題,和 headerTitle 、 drawerLabel 一樣;
drawerLabel -標簽字符串,或者自定義組件, 可以根據 { focused: boolean, tintColor: string }函數來返回一個自定義組件作為標簽;
drawerIcon - 抽屜icon,可以根據 { focused: boolean,tintColor: string } 函數來返回一個自定義組件作為icon ;
DrawerNavigatorConfig 屬性配置
drawerWidth - 抽屜寬度,可以使用Dimensions獲取屏幕寬度,實現動態計算;
drawerPosition -抽屜位置,可以是 left 或者 right;
contentComponent - 抽屜內容組件,可以自定義側滑抽屜中的所有內容,默認為DrawerItems;
contentOptions - 用來配置抽屜內容的屬性。當用來配置 DrawerItems 是配置屬性選項:
items - 抽屜欄目的路由名稱數組,可以被修改;
activeItemKey - 當前選中頁面的key id;
activeTintColor - 選中條目狀態的文字顏色;
activeBackgroundColor - 選中條目的背景色;
inactiveTintColor - 未選中條目狀態的文字顏色;
inactiveBackgroundColor - 未選中條目的背景色
onItemPress(route) - 條目按下時會調用此方法;
style - 抽屜內容的樣式;
labelStyle -抽屜的條目標題/標簽樣式;
initialRouteName - 初始化展示的頁面路由名稱;
order -抽屜導航欄目順序,用路由名稱數組表示;
paths - 路徑;
backBehavior -android點擊返回鍵時的處理,有initialRoute和none兩個值:
initailRoute:返回初始界面;
none :退出
抽屜導航示例:
import React, {Component} from 'react'; import {DrawerNavigator, StackNavigator, TabBarBottom, TabNavigator} from "react-navigation"; import HomeScreen from "./index18/HomeScreen"; import NearByScreen from "./index18/NearByScreen"; import MineScreen from "./index18/MineScreen"; import TabBarItem from "./index18/TabBarItem"; export default class MainComponent extends Component { render() { return (
擴展功能
默認DrawerView不可滾動。要實現可滾動視圖,必須使用contentComponent自定義容器:
{ drawerWidth:200, 抽屜位置:“對” contentComponent:props =>
可以覆蓋導航使用的默認組件,使用DrawerItems自定義導航組件:
import {DrawerItems} from 'react-navigation'; const CustomDrawerContentComponent = (props) => (
嵌套抽屜導航
如果嵌套DrawerNavigation,抽屜將顯示在父導航下方。
自定義react-navigation適配頂部導航欄標題
測試中發現,在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 ‘即可解決。
去除返回鍵文字顯示
【node_modules – react-navigation – src – views – HeaderBackButton.js】的91行代碼處,修改為如下即可。
{ Platform.OS === 'ios' && title &&
動態設置頭部按鈕事件
當我們在頭部設置左右按鈕時,肯定避免不了要設置按鈕的單擊事件,但是此時會有一個問題,navigationOptions是被修飾為static類型的,所以在按鈕的onPress方法中不能直接通過this來調用Component中的方法。如何解決呢?
componentDidMount () { /** * 將單擊回調函數作為參數傳遞 */ this.props.navigation.setParams({ switch: () => this.switchView() }); } /** * 切換視圖 */ switchView() { alert('切換') } static navigationOptions = ({navigation,screenProps}) => ({ headerTitle: '企業服務', headerTitleStyle: CommonStyles.headerTitleStyle, headerRight: (
結合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); };
實現Android中界面跳轉左右切換動畫
react-navigation在android中默認的界面切換動畫是上下。如何實現左右切換呢?很簡單的配置即可:
import CardStackStyleInterpolator from 'react-navigation/src/views/CardStackStyleInterpolator';
然后在StackNavigator配置下添加如下代碼:
transitionConfig:()=>({ screenInterpolator: CardStackStyleInterpolator.forHorizontal, })
解決快速點擊多次跳轉
當我們快速點擊跳轉時,會開啟多個重復的界面,如何解決呢?其實在官方Git中也有提示,解決這個問題需要修改react-navigation源碼。
找到scr文件夾中的addNavigationHelpers.js文件,替換為如下文本即可:
export default function) { // 添加點擊判斷 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, }), ), }; }
iOS NAT React
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。