注意了,若是有小夥伴們發現運行做者提供的react-navigation示例項目報以下的錯誤,多是你們使用了 yarn install
命令致使的,解決這個錯誤的辦法就是將nodemodules刪除,而後從新使用npm install
命令來安裝,最後使用 npm start
來起服務,應該就不報錯了。若是還有報錯,請加做者交流羣,將問題反饋到羣裏,謝謝。node
react-navigation 組件是官方推薦使用的導航組件,功能和性能都遠遠的優於以前的Navigator組件,公司的RN項目最先是使用的react-native-router-flux
導航組件,由於那個時候react-navigation
組件尚未出來,在使用了react-navigation
後,感受比react-native-router-flux組件有更增強大的功能,體驗也略好些,這兩個導航組件是目前star最多的導航組件,而且他們都完美的支持與Redux框架的結合使用,推薦小夥伴們兩個組件都嘗試使用下。
react-navigationreact
https://github.com/guangqiang-liu/react-navigation-demoandroid
Demo示例講解包含三部分ios
注意: 有小夥伴說Demo運行報錯,這裏你們須要注意,Demo clone下來以後,咱們先要執行 npm install
操做, 而後在執行 react-native link
,最後在 執行 npm start
來運行項目,若是還有其餘的報錯信息,歡迎進羣提出報錯信息git
http://www.jianshu.com/p/faa98d8bd3fagithub
react-navigation 組件主要由三大部分組成npm
下面咱們對react-navigation
詳解也主要圍繞這三個API來展開redux
StackNavigator導航欄的工做原理就和iOS中原生的UINavigationController同樣的,是以棧的方式來管理每個頁面控制器的,當使用push就是入棧,當使用pop操做時就是出棧,這個很好理解,若是咱們想讓一個頁面控制器有導航欄,那麼咱們首先要作的就是給這個頁面註冊導航react-native
API數組
StackNavigator(RouteConfigs, StackNavigatorConfig)
StackNavigator函數中有兩個參數:
配置RouteConfigs
const RouteConfigs = { Home: { screen: TabBar // screen屬性爲必須配置屬性 }, Home2: { screen: Home2, path:'app/Home2', navigationOptions: { title: '這是在RouteConfigs中設置的title', headerTitleStyle: { fontSize: 10 } } }, Home3: { screen: Home3 }, Home4: { screen: Home4 }, Home5: {screen: Home5}, Home6: {screen: Home6}, Home7: {screen: Home7}, Setting2: {screen: Setting2}, Setting3: {screen: Setting3}, }
配置StackNavigatorConfig
const StackNavigatorConfig = { initialRouteName: 'Home', initialRouteParams: {initPara: '初始頁面參數'}, navigationOptions: { title: '標題', headerTitleStyle: {fontSize: 18, color: 'red'}, headerStyle: {height: 49}, }, paths: 'page/main', mode: 'card', headerMode: 'screen', cardStyle: {backgroundColor: "#ffffff"}, transitionConfig: (() => ({ })), onTransitionStart: (() => { console.log('頁面跳轉動畫開始') }), onTransitionEnd: (() => { console.log('頁面跳轉動畫結束') }), }
註冊導航
import {StackNavigator, TabNavigator} from "react-navigation" const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig) export default class Main extends Component { render() { return ( <Navigator/> ) } }
從上面註冊導航的代碼塊中,咱們能夠看出StackNavigator函數接受兩個配置對象RouteConfigs
和 StackNavigatorConfig
,可是這裏須要注意,第二個參數StackNavigatorConfig能夠省略,表示不作任何導航默認配置
StackNavigatorConfig配置參數
initialRouteName
:導航器組件中初始顯示頁面的路由名稱,若是不設置,則默認第一個路由頁面爲初始顯示頁面initialRouteParams
:給初始路由的參數,在初始顯示的頁面中能夠經過this.props.navigation.state.params
來獲取navigationOptions
:路由頁面的全局配置項paths
:RouteConfigs裏面路徑設置的映射mode
:頁面跳轉方式,有card和modal兩種,默認爲 card
headerMode
:頁面跳轉時,頭部的動畫模式,有 float 、 screen 、 none 三種
cardStyle
:爲各個頁面設置統一的樣式,好比背景色,字體大小等transitionConfig
:配置頁面跳轉的動畫,覆蓋默認的動畫效果onTransitionStart
:頁面跳轉動畫即將開始時調用onTransitionEnd
:頁面跳轉動畫一旦完成會立刻調用在StackNavigatorConfig配置參數中有一個navigationOptions
屬性的配置,這個配置項能夠理解爲導航欄的全局配置表,下面就講解這個屬性的可配置參數
navigationOptions配置參數
title
:導航欄的標題,或者Tab標題 tabBarLabelheader
:自定義的頭部組件,使用該屬性後系統的頭部組件會消失,若是想在頁面中自定義,能夠設置爲null,這樣就不會出現頁面中留有一個高度爲64navigationBar的高度headerTitle
:頭部的標題,即頁面的標題headerBackTitle
:返回標題,默認爲 title的標題headerTruncatedBackTitle
:返回標題不能顯示時(好比返回標題太長了)顯示此標題,默認爲'Back'headerRight
:頭部右邊組件headerLeft
:頭部左邊組件headerStyle
:頭部組件的樣式headerTitleStyle
:頭部標題的樣式headerBackTitleStyle
:頭部返回標題的樣式headerTintColor
:頭部顏色headerPressColorAndroid
:Android 5.0 以上MD風格的波紋顏色gesturesEnabled
:否能側滑返回,iOS 默認 true , Android 默認 falsenavigationOptions
// StackNavigatorConfig中的navigationOptions屬性也能夠在組件內用static navigationOptions 設置(會覆蓋此處的設置) navigationOptions: { header: { // 導航欄相關設置項 backTitle: '返回', // 左上角返回鍵文字 style: { backgroundColor: '#fff' }, titleStyle: { color: 'green' } }, cardStack: { gesturesEnabled: true } }
注意:
RouteConfigs
中配置 navigationOptions屬性,咱們也能夠在單獨頁面配置navigationOptionsRouteConfigs
和StackNavigatorConfig
對象中的navigationOptions屬性裏面的對應屬性在RouteConfigs
中配置 navigationOptions
const RouteConfigs = { Home: { screen: TabBar }, Home2: { screen: Home2, path:'app/Home2', // 此處設置了, 會覆蓋組件內的`static navigationOptions`設置. 具體參數詳見下文 navigationOptions: { title: '這是在RouteConfigs中設置的title', headerTitleStyle: { fontSize: 10 } } }, Home3: { screen: Home3 }, Home4: { screen: Home4 }, Home5: {screen: Home5}, Home6: {screen: Home6}, Home7: {screen: Home7}, Setting2: {screen: Setting2}, Setting3: {screen: Setting3}, }
在具體頁面中配置 navigationOptions
import {StackNavigator, TabNavigator} from "react-navigation" const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig) export default class Main extends Component { // 配置頁面導航選項 static navigationOptions = { title: 'homeThree', header: (navigation, defaultHeader) => ({ ...defaultHeader, // 默認預設 visible: true // 覆蓋預設中的此項 }), cardStack: { gesturesEnabled: false // 是否能夠右滑返回 } } // 或這樣 static navigationOptions = { // title: 'Two', // 固定標題 title: (navigation, childRouter) => { // 動態標題 if (navigation.state.params.isSelected) { return `${navigation.state.params.name}選中`; } else { return `${navigation.state.params.name}沒選中`; } }, header: ({ state, setParams, goBack }) => { let right; if (state.params.isSelected) { right = (<Button title="取消" onPress={() => setParams({ isSelected: false })}/>); } else { right = (<Button title="選擇" onPress={() => setParams({ isSelected: true })}/>); } let left = (<Button title="返回" onPress={() => goBack()}/>); let visible = false; // 是否顯示導航欄 return { right, left, visible }; }, // header: {left: <Button title="返回"/>}, } render() { return ( <Navigator/> ) } }
API
TabNavigator(RouteConfigs, TabNavigatorConfig)
從API上看,TabNavigator 和 StackNavigator 函數用法同樣,都是接受RouteConfigs和TabNavigatorConfig這兩個參數
RouteConfigs配置參數
路由配置和StackNavigator中同樣,配置路由以及對應的 screen 頁面,navigationOptions 爲對應路由頁面的配置選項
title
:Tab標題,可用做headerTitle 和 tabBarLabel 回退標題tabBarVisible
:Tab的是否可見,默認爲 truetabBarIcon
:Tab的icon組件,能夠根據 {focused: boolean, tintColor: string} 方法來返回一個icon組件tabBarLabel
:Tab中顯示的標題字符串或者組件,也能夠根據{ focused: boolean, tintColor: string } 方法返回一個組件配置RouteConfigs
const RouteConfigs = { Home: { screen: Home, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'Home', tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-home' : 'ios-home-outline'} size={26} style={{ color: tintColor }}/> ) }), }, People: { screen: People, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'People', tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-people' : 'ios-people-outline'} size={26} style={{ color: tintColor }}/> ) }), }, Chat: { screen: Chat, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'Chat', tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'} size={26} style={{ color: tintColor }}/> ) }), }, Setting: { screen: Setting, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'Settings', tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-settings' : 'ios-settings-outline'} size={26} style={{ color: tintColor }}/> ) }), } }
TabNavigatorConfig配置參數
tabBarComponent
: Tab選項卡組件,有TabBarBottom和TabBarTop兩個值,在iOS中默認爲 TabBarBottom ,在Android中默認爲 TabBarTop
tabBarPosition
:Tab選項卡的位置,有top或bottom兩個值
swipeEnabled
:是否能夠滑動切換Tab選項卡animationEnabled
:切換界面是否須要動畫lazy
:是否懶加載頁面initialRouteName
:初始顯示的Tab對應的頁面路由名稱order
:用路由名稱數組來表示Tab選項卡的順序,默認爲路由配置順序paths
: 路徑配置backBehavior
:androd點擊返回鍵時的處理,有initialRoute 和 none 兩個值
tabBarOptions
:Tab配置屬性,用在TabBarTop和TabBarBottom時有些屬性不一致
用在TabBarTop時對應的屬性:
用在TabBarBottom時對應的屬性:
使用TabBarTop代碼示例
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 ( <Navigator/> ); } } const TabRouteConfigs = { Home: { screen: HomeScreen, navigationOptions: ({navigation}) => ({ tabBarLabel: '首頁', }), }, NearBy: { screen: NearByScreen, navigationOptions: { tabBarLabel: '附近', }, } , Mine: { screen: MineScreen, navigationOptions: { tabBarLabel: '個人', }, } }; const TabNavigatorConfigs = { initialRouteName: 'Home', tabBarComponent: TabBarTop, tabBarPosition: 'top', lazy: true, tabBarOptions: {} }; const Tab = TabNavigator(TabRouteConfigs, TabNavigatorConfigs); const StackRouteConfigs = { Tab: { screen: Tab, } }; const StackNavigatorConfigs = { initialRouteName: 'Tab', navigationOptions: { title: '標題', headerStyle: {backgroundColor: '#5da8ff'}, headerTitleStyle: {color: '#333333'}, } }; const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);
使用TabBarBottom代碼示例
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 ( <Navigator/> ); } } const TabRouteConfigs = { Home: { screen: HomeScreen, navigationOptions: ({navigation}) => ({ tabBarLabel: '首頁', tabBarIcon: ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_homepage_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_homepage_selected_2x.png')} /> ), }), }, NearBy: { screen: NearByScreen, navigationOptions: { tabBarLabel: '附近', tabBarIcon: ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_merchant_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_merchant_selected_2x.png')} /> ), }, } , Mine: { screen: MineScreen, navigationOptions: { tabBarLabel: '個人', tabBarIcon: ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_mine_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected_2x.png')} /> ), }, } }; const TabNavigatorConfigs = { initialRouteName: 'Home', tabBarComponent: TabBarBottom, tabBarPosition: 'bottom', lazy: true, }; const Tab = TabNavigator(TabRouteConfigs, TabNavigatorConfigs); const StackRouteConfigs = { Tab: { screen: Tab, } }; const StackNavigatorConfigs = { initialRouteName: 'Tab', navigationOptions: { title: '標題', headerStyle: {backgroundColor: '#5da8ff'}, headerTitleStyle: {color: '#333333'}, } }; const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs);
API
DrawerNavigator(RouteConfigs, DrawerNavigatorConfig)
DrawerNavigator
與StackNavigator
和 TabNavigator
函數的使用方式同樣,參數配置也相似
路由配置和StackNavigator中同樣,配置路由以及對應的 screen 頁面,navigationOptions 爲對應路由頁面的配置選項
RouteConfigs參數配置
title
:抽屜標題,和headerTitle 、 drawerLabel同樣drawerLabel
:標籤字符串,或者自定義組件,能夠根據{ focused: boolean, tintColor: string } 函數來返回一個自定義組件做爲標籤drawerIcon
:抽屜icon,能夠根據 { focused: boolean, tintColor: string } 函數來返回一個自定義組件做爲iconDrawerNavigatorConfig參數配置
drawerWidth
:抽屜寬度,可使用Dimensions獲取屏幕的寬度動態計算drawerPosition
:抽屜位置,能夠是 left 或者 rightcontentComponent
:抽屜內容組件,能夠自定義側滑抽屜中的全部內容,默認爲 DrawerItemscontentOptions
:用來配置抽屜內容的屬性。當用來配置 DrawerItems 是配置屬性選項
initialRouteName
:初始化展現的頁面路由名稱order
:抽屜導航欄目順序,用路由名稱數組表示paths
:路徑backBehavior
:androd點擊返回鍵時的處理,有initialRoute和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"; const RouteConfigs = { Home: { screen: HomeScreen, navigationOptions: ({navigation}) => ({ drawerLabel : '首頁', drawerIcon : ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_homepage_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_homepage_selected_2x.png')} /> ), }), }, NearBy: { screen: NearByScreen, navigationOptions: { drawerLabel : '附近', drawerIcon : ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_merchant_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_merchant_selected_2x.png')} /> ), }, }, Mine: { screen: MineScreen, navigationOptions: { drawerLabel : '個人', drawerIcon : ({focused, tintColor}) => ( <TabBarItem tintColor={tintColor} focused={focused} normalImage={require('./img/tabbar/pfb_tabbar_mine_2x.png')} selectedImage={require('./img/tabbar/pfb_tabbar_mine_selected_2x.png')} /> ), }, } }; const DrawerNavigatorConfigs = { initialRouteName: 'Home', tabBarComponent: TabBarBottom, tabBarPosition: 'bottom', lazy: true, tabBarOptions: {} }; const Drawer = DrawerNavigator(RouteConfigs, DrawerNavigatorConfigs) const StackRouteConfigs = { Drawer: { screen: Drawer, } }; const StackNavigatorConfigs = { initialRouteName: 'Drawer', navigationOptions: { title: '標題', headerStyle: {backgroundColor: '#5da8ff'}, headerTitleStyle: {color: '#333333'}, } } const Navigator = StackNavigator(StackRouteConfigs, StackNavigatorConfigs) export default class Main extends Component { render() { return ( <Navigator/> ) } }
在StackNavigator中註冊過的組件都有navigation這個屬性,navigation有5個主要參數
咱們平時使用react-navigation做爲導航組件來開發時,常用到的也就是這5個屬性的功能
navigate
導航到下一個頁面
<Button buttonStyle={{marginVertical: 10}} title={'跳轉到Home2界面'} onPress={() => this.props.navigation.navigate('Home2')} />
<Button buttonStyle={{marginVertical: 10}} title={'跳轉到Home3界面,並傳遞參數'} // 這裏傳遞了參數`id` onPress={() => this.props.navigation.navigate('Home3', {id: 123})} />
navigation中的navigate函數能夠接受三個參數
routeName
:註冊過的目標路由名稱,也就是準備跳轉到的頁面路由地址(例如上面的Home3
)params
:跳轉到下一個頁面,傳遞的參數(例如上面的id
)action
:下文有講到state
state
屬性包含有傳遞過來的三個參數 params、key 、routeName
routeName
:註冊過的目標路由名稱key
:路由身份標識params
:跳轉時傳遞的參數獲取state中的參數:this.props.navigation.state.params.id
這樣就能拿到上一個頁面傳遞的參數:id
setParams
this.props.navigation.setParams()
: 該方法容許界面更改router中的參數,能夠用來動態的更改導航欄的內容。好比能夠用來更新頭部的按鈕或者標題等
使用場景:重寫導航按鈕的返回按鈕,自定義返回事件
export default class Home5 extends Component { static navigationOptions = ({navigation, screenProps}) => ({ title: 'Home5', headerLeft: ( <Button title={'customAction'} onPress={() => navigation.state.params.customAction()} /> ) } ) componentDidMount() { const {setParams} = this.props.navigation setParams({customAction: () => this.tempAction()}) } tempAction() { alert('在導航欄按鈕上調用Component內中的函數,由於static修飾的函數爲靜態函數,內部不能使用this') } render() { return ( <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}> <Text>Home5</Text> </View> ) } }
goBack
退出當前頁面,返回到上一個頁面,能夠不傳參數,也能夠傳參數,還能夠傳 null
<Button title={'返回上一頁面'} onPress={() => goBack()} />
dispatch
this.props.navigation.dispatch
: 能夠dispatch一些action,主要支持的action有一下幾種
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)
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)
爲指定的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)
NavigationActions.back()
const initialState = Navigator.router.getStateForAction(NavigationActions.init()); export default (state = initialState, actions) => { const nextState = Navigator.router.getStateForAction(actions, state); return nextState || state; }
注意: 若是你的項目中使用了與Redux框架結合,這裏的dispatch就能夠派發任何你想dispatch的Action了
使用場景:Counter 計數器
class Counter extends Component { static navigationOptions = () => ({ title: 'Counter加減計數器' }) render() { const {dispatch} = this.props.navigation return ( <View> <Text>Counter</Text> <Text style={{marginVertical: 20, color: 'red', fontSize: 30}}>{this.props.counterValue}</Text> <Button buttonStyle={{marginVertical: 10}} title={'+'} onPress={() => dispatch(increaseAction())} /> <Button buttonStyle={{marginVertical: 10}} title={'-'} onPress={() => dispatch(decreaseAction())} /> </View> ) } } const mapStateToProps = state => { return { counterValue: state.home.counter.counterValue } } export default connect(mapStateToProps)(Counter)
頁面跳轉與傳值
<Button buttonStyle={{marginVertical: 10}} title={'跳轉到Home2界面'} onPress={() => this.props.navigation.navigate('Home2')} /> <Button buttonStyle={{marginVertical: 10}} title={'跳轉到Home3界面,並傳遞參數'} onPress={() => this.props.navigation.navigate('Home3', {id: 123})} />
在下一界面接收參數,經過this.props.navigation.state.params
接收參數
export default class Home3 extends Component { render() { const {navigate} = this.props.navigation return ( <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}> <Text>Home3</Text> <Text style={{marginVertical: 20}}>{`Home界面傳遞的參數爲:${this.props.navigation.state.params.id}`}</Text> <Button buttonStyle={{marginVertical: 10}} title={'跳轉到Home4界面'} onPress={() => navigate('Home4')} /> </View> ) } }
回調傳參
當前界面進行跳轉,並傳遞參數
<Button buttonStyle={{marginVertical: 10}} title={'跳轉到Home6界面,回調傳參'} onPress={() => this.props.navigation.navigate('Home6',{callback: (response) => alert(response)})} />
下一界面在返回以前執行函數回調傳參給上一個頁面
const {state, goBack} = this.props.navigation <Button title={'回調傳參'} onPress={() => { state.params.callback && state.params.callback('這是回調參數') goBack() }} />
其餘app或瀏覽器使用url打開次app並進入指定頁面,相似於iOS中的URL導航同樣。如瀏覽器輸入url OneM://home/home2直接進入home2頁面
iOS平臺須要額外配置
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { return [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation]; }
在js中配置
js組件在註冊路由時設置惟一的路徑path, 例如Home2: { screen: Home2, path:'home/Home2'}
在手機瀏覽器訪問OneM://home/Home2, 彈窗選擇打開, 就能夠打開OneM app並進到Home2頁面了
{ drawerWidth:200, contentComponent:props => <ScrollView> <DrawerItems {... props}></DrawerItems> </ ScrollView> }
import {DrawerItems} from 'react-navigation'; const CustomDrawerContentComponent = (props) => ( <View style = {style.container}> <DrawerItems {... props} /> </View> )
測試中發現,在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 && <Text onLayout={this._onTextLayout} style={[styles.title, { color: tintColor }]} numberOfLines={1} > {backButtonTitle} </Text> }
將上述代碼刪除便可
當咱們在頭部設置左右按鈕時,確定避免不了要設置按鈕的單擊事件,可是此時會有一個問題,navigationOptions是被修飾爲static類型的,因此咱們在按鈕的onPress的方法中不能直接經過this來調用Component中的方法。如何解決呢?在官方文檔中,做者給出利用設置params的思想來動態設置頭部標題。那麼咱們能夠利用這種方式,將單擊回調函數以參數的方式傳遞到params,而後在navigationOption中利用navigation來取出設置到onPress便可:
export default class Home5 extends Component { static navigationOptions = ({navigation, screenProps}) => ({ title: 'Home5', headerRight: ( <Button title={'customAction'} onPress={() => navigation.state.params.customAction()} /> ) } ) componentDidMount() { const {setParams} = this.props.navigation setParams({customAction: () => this.tempAction()}) } tempAction() { alert('在導航欄按鈕上調用Component內中的函數,由於static修飾的函數爲靜態函數,內部不能使用this') } render() { return ( <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}> <Text>Home5</Text> </View> ) } }
點擊返回鍵兩次退出App效果的需求家常便飯。相信不少人在react-navigation下實現該功能都遇到了不少問題,例如,其餘界面不能返回。也就是手機自己返回事件在react-navigation以前攔截了。如何結合react-natigation實現呢?和你們分享兩種實現方式:
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; }
/** * 處理安卓返回鍵 */ 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); }
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<S: *>(navigation: NavigationProp<S, NavigationAction>) { // 添加點擊判斷 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, }), ), } }
有時咱們點擊tabBar上的tab來切換頁面,可是在切換頁面以前咱們想先作一些邏輯處理,而後在切換到tab頁面,這時咱們就須要先hook到這個tab的點擊事件,下面代碼塊就是告訴你如何hook到tab的點擊事件,處理完事件在打開tab頁面,這個使用具體使用方式在示例Demo中都有實際使用,不清楚的同窗們直接去運行示例項目瞭解便可
Chat: { screen: Chat, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'Chat', tabBarOnPress: () => { Alert.alert( '注意!', '這裏作了hook tabBar的點擊事件操做,咱們能夠hook到這個點擊事件,處理咱們想要處理的業務後再打開 Chat這個頁面', [ {text: '打開tab頁面', onPress: () => navigation.navigate('Chat')}, {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'}, ], { cancelable: false } ) }, tabBarIcon: ({ focused, tintColor }) => ( <Ionicons name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'} size={26} style={{ color: tintColor }}/> ) }), },
navigator與tabBar嵌套 具體的結合使用方式示例Demo中有給出具體示例,這個同窗們直接運行示例Demo查看便可
以前有很多同窗問我,怎麼給一個tabBar設置badge,前段時間因爲太忙,一直沒有去處理這個問題,後面去實現了下自定義badge,感受仍是挺簡單的,由於navigation的tabBarItem原本就是支持自定義的,既然可以自定義,那實現badge天然也是可行的了,下面就是具體實現代碼塊
People: { screen: People, navigationOptions: ({ navigation }) => ({ tabBarLabel: 'People', tabBarIcon: ({ focused, tintColor }) => ( <View style={{position: 'absolute', top: -10}}> <Ionicons name={focused ? 'ios-people' : 'ios-people-outline'} size={26} style={{ color: tintColor }}/> <View style={{backgroundColor: 'red', position: 'absolute', right: -10, top: -5, height: 15, width: 20, borderRadius: 8, overflow: 'hidden'}}> <Text style={{fontSize: 12, textAlign: 'center'}}>10</Text> </View> </View> ) }), },
有時候咱們在開發的時候,不免會遇到在點擊返回按鈕的時候,想直接返回到指定的某一個頁面,而不是返回上一級頁面,這時咱們就須要對goback()函數作些處理了,具體的代碼實現以下
const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig) const defaultStateAction = Navigator.router.getStateForAction; Navigator.router.getStateForAction = (action, state) => { if (state && action.key && action.type === 'Navigation/BACK') { const desiredRoute = state.routes.find((route) => route.routeName === action.key) if (desiredRoute) { const index = state.routes.indexOf(desiredRoute) const finalState = { ...state, routes: state.routes.slice(0, index + 1), index: index, }; return finalState } else { if (state.routes.length > action.key) { const stacksLength = state.routes.length - action.key const stacks = state.routes.slice(0, stacksLength) const finalState = { ...state, routes: stacks, index: stacksLength - 1, }; return finalState } } } return defaultStateAction(action, state) }
其實goback()函數,是很容易的就能夠作到返回到指定頁面,和返回指定層級的頁面的,並不像網上其餘的文章說的須要改源碼啊,或者是須要結合redux才能實現啊,並非這樣的,只須要咱們簡單的維護下導航的路由棧便可解決問題,這個其實和原生iOS中處理導航的棧管理是一個道理
const Navigator = StackNavigator(RouteConfigs, StackNavigatorConfig) const defaultStateAction = Navigator.router.getStateForAction; Navigator.router.getStateForAction = (action, state) => { if (state && action.key && action.type === 'Navigation/BACK') { const desiredRoute = state.routes.find((route) => route.routeName === action.key) if (desiredRoute) { const index = state.routes.indexOf(desiredRoute) const finalState = { ...state, routes: state.routes.slice(0, index + 1), index: index, }; return finalState } else { if (state.routes.length > action.key) { const stacksLength = state.routes.length - action.key const stacks = state.routes.slice(0, stacksLength) const finalState = { ...state, routes: stacks, index: stacksLength - 1, }; return finalState } } } return defaultStateAction(action, state) }
navigator與抽屜嵌套使用的方式,示例Demo中已經有具體實現了,這個比較簡單,就不作詳細解答了
簡書上面的 開發中遇到的問題及注意事項中有講解決辦法,不過做者仍是建議你們將導航欄封裝成一個組件,使用自定義的組件靈活性更高
這個懶加載Tab,這個也沒有什麼好解答的,官方已經給我提供了設置屬性,咱們只須要設置個屬性便可,具體代碼以下
const TabNavigatorConfigs = { initialRouteName: 'Home', lazy: true, tabBarOptions: { activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff', } }
react-navigation導航組件的API相對較多,若是小夥伴們看完講解仍是不清楚如何使用,建議直接運行Demo項目,邊調試邊理解。
針對以前不少同窗反映出關於react-navigation 使用上遇到的一些問題,上面基本上都逐一解答了,而且都在示例Demo實戰的測試過是可行方案,後期還有其餘的小夥伴遇到使用上的問題,歡迎進羣討論,或者是給我簡書留言,謝謝你們的支持。