React Native--使用React Navigation實現界面導航與跳轉
在瀏覽器中我們可以通過<a>標簽與url實現不同頁面之間的跳轉,利用瀏覽器的返回按鈕返回之前瀏覽的頁面,但是在React Native中卻沒有集成內嵌的全局棧來實現這個界面的跳轉,因此需要使用第三方庫來實現這個功能。React Navigation就是這樣一個源於ReactNative社區的用於實現界面導航的js庫。通過如下方法安裝react navigation到你的項目中:
yarn add react-navigation
# 或者通過npm安裝:
# npm install --save react-navigation
React Navigation常用有三個組件,其中StackNavigator用於實現頁面跳轉,TabNavigation用於標簽頁之間切換,DrawerNavigation用於實現抽屜式側邊欄。使用前需要先引入組件:
import {DrawerNavigator,TabNavigator,StackNavigator} from 'react-navigation'
1、StackNavigator
StackNavigator組件用於實現在不同的頁面之間的跳轉,並且對頁面歷史進行管理,就像瀏覽器那樣,用一個棧保存瀏覽頁面。當你打開一個新頁面時,將頁面壓入棧頂,當你需要返回時,從棧頂彈出頁面。不同的是,通過React Navigation可以在移動設備上獲得更加真實的用戶體驗,比如利用屏幕手勢操作,以及更加逼真的動畫切換效果。
1.1、定義路由
總體上看StackNavigator()接收兩個參數,分別是需要跳轉管理的路由模塊以及Navigator的基本設置,返回一個React Component組件。因此可以將你的所有頁面作為路由模塊放在StackNavigator中形成根路由RootStack,然后將其作為ReactNative的入口導出。
在每個路由模塊中通過screen關鍵字定義對應渲染的ReactNative組件
const RootStack = StackNavigator( {//定義路由 Home: { //定義Home對應HomeScreen組件 screen: HomeScreen, }, Details: { screen: DetailsScreen, }, }, {//定義配置 initialRouteName: 'Home', //設置初始路由為Home } ); export default class App extends Component { render() { //將Navigation作為根路徑導出 return <RootStack />; } }
嵌套路由:由於StackNavigator返回一個React組件,因此它也可以作為子組件添加到另一個StackNavigator路由中,形成層疊路由結構。例如Home Screen與DetailScreen構成MainStack,MainStack與ModalScreen構成RootStack:
1.2、路由跳轉
StackNavigator會為每個注冊的路由組件傳遞參數navigation屬性,通過this.props.navigation的navigate方法可以實現頁面間的跳轉,其參數為StackNavigator中已定義的路由,例如從主頁跳轉到詳情頁Details:
<Button title='跳轉到詳情' onPress={()=>this.props.navigation.navigate('Details')} />
返回:每個界面的頭部導航欄左邊默認設置了一個返回按鈕,通過它可以返回到之前一個頁面,如果你希望手動觸發返回,可以調用this.props.navigation.goBack()。
路由跳轉實際上就是新打開一個頁面並將路由壓入一個棧中,點擊返回時,從棧頂彈出一個頁面。與瀏覽器不同的是,當給navigate傳遞的參數是本頁面時,它依舊會壓入棧內,點擊返回時會彈出本頁面,而瀏覽器會辨別這是本頁面,不會再壓入棧頂。
1.3、參數傳遞
當頁面跳轉時,可以把需要傳遞的數據作為參數放在navigate方法的第二個參數中,例如:
<Button title='跳轉到詳情' onPress={()=>this.props.navigation.navigate('Details',{ userName:'Tory', userInfo:'Hello' })} />在路由頁面內,通過 this.props.navigation.state.params可以得到上一個頁面傳入的參數,例如在Details頁面獲取上面的數據:
class DetailsScreen extends Component { render() { const data=this.props.navigation.state.params; return ( <View style={styles.container}> <Text>你好,{data.userName}!</Text> </View> ); } }
1.4、導航欄設置
通過對組件內的靜態常量 navigationOptions可以對組件的導航欄進行設置。1.4.1、設置與修改
靜態對象:navigationOptions可以直接接收一個對象,例如設置詳情頁導航欄的標題
class DetailsScreen extends Component { static navigationOptions = { title: '詳情頁' }; }函數返回:由於上面通過static設置的靜態常量,它不是組件的一個實例,無法通過this.props訪問到navigation.state.params。但如果通過函數的方式返回navigationOptions對象,React Navigation在調用函數時會函數默認傳入參數props,就可以拿到params。例如:
static navigationOptions=((props)=>{ return { title:props.navigation.state.params.title } });
修改navigationOptions:通過this.props.navigation.setParams()方法可以對params進行修改,例如修改標題:
<Button title='修改標題' onPress={()=>{ this.props.navigation.setParams({title:'修改后的標題'}) }} />
1.4.2、navigationOptions屬性
static navigationOptions={ title:'詳情頁', header:HeaderComponent, //自定義頭部組件 headerTitle:TitleComponent, //自定義標題組件 headerLeft:LeftComponent, //自定義左邊組件,會替換掉默認返回按鈕 headerRight:<Text>右邊元素</Text>, //自定義右邊元素,注意這里不可以放組件 headerBackImage:{uri:'mipmap/ic_launcher'}, //自定義返回按鈕的圖片 headerStyle:{ //導航欄樣式設置 backgroundColor:'#8bc9ff', }, headerTintColor:'#fff', //按鈕、標題顏色設置 headerTitleStyle:{ //標題字體樣式設置 fontWeight:'bold', }, headerTransparent:true, //使頭部背景透明 gesturesEnabled:true, //開啟手勢操作 gestureDirection:'inverted', //修改返回手勢操作方向為從右到左,默認為從左到右 gestureResponseDistance:{ //定義手勢響應距離屏幕邊界的距離 horizontal:100, vertical:50 } };
如果在組件內部定義navigationOptions,它只會對當前頁面起作用,如果希望對所有組件設置通用options,可以把navigationOptions對象放在前面的1.1提到的路由定義中。頁面內的navigationOptions比通用設置具有更高的優先級。
注意自定義組件返回的是JSX元素,而不是React Component類。
const RootStack = StackNavigator( {//定義路由 Home: {screen: HomeScreen}, Details:{screen: DetailsScreen}, }, {//定義配置 initialRouteName: 'Home', navigationOptions:{ //導航欄通用設置 headerStyle:{ backgroundColor:'#7276ff' } } } );
1.5、頭部與組件通信
在navigationOptions中設置的組件無法通過this訪問到頁面組件DetailsScreen,如果希望二者之間進行通信,則需要借助navigation.params。例如:
class DetailsScreen extends React.Component { state={count:0}; static navigationOptions = (props) => { const params = props.navigation.state.params; return { headerRight: ( //通過params為按鈕綁定increase方法 <Button onPress={params.increase} title="+1" /> ), }; }; componentWillMount() { //通過setParams將increase方法綁定到_increase this.props.navigation.setParams({ increase: this._increase }); } _increase=()=>{ //設置state.count+1 this.setState(preState=>{return {count:preState.count+1}}); }; render() { return ( <View style={styles.container}> <Text>計數為:{this.state.count}</Text> </View> ); } }在navigationOptions中的<Button>想要修改DetailsScreen的state.count,是無法通過this的,需要先給按鈕綁定params.increase方法,然后在組件掛載前通過setParams將increase綁定到DetailsScree的內部方法this._increase,才可以訪問修改state.count。
2、TabNavigator
React Navigation提供了TabNavigator來實現不同標簽頁之間的跳轉。安卓的標簽欄默認顯示在頭部,IOS在底部。
2.1、定義路由組件
同StackNavigator一樣,使用TabNavigator首先需要定義每個路由頁面以及其對應的組件,Tabnavigator方法的第一個參數就是所有標簽頁的路由,第二個為設置選項,最后返回一個React Component,因此可以把它返回當作index.js的入口
//默認導出TabNavigator組件給index.js export default TabNavigator ( { Home:{screen:HomeScreen}, //標簽頁Home對應HomeScreen組件 Message:{screen:MessageScreen} }, { //TabNavigator的設置 } )
//index.js中引入TabNavigator並定義為入口 import { AppRegistry } from 'react-native'; import TabNavigator from './TabNavigation'; AppRegistry.registerComponent('Navigation', () => TabNavigator);
TabNavigator可以與StackNavigator嵌套使用,比如進入App后有兩個標簽頁Home與Message,點擊Home中的按鈕從Home跳轉到詳情頁Detail,因此HomeScreen與DetailScreen通過StackNavigator構成了一個HomeStack,然后與MessageScreen一起又構成了TabNavigator:
const HomeStack=StackNavigator( { Home:{screen:HomeScreen}, Detail:{screen:DetailScreen} } ); export default TabNavigator ( { Home:{screen:HomeStack}, //標簽頁Home對應HomeScreen組件 Message:{screen:MessageScreen} } )
2.2、TabNavigator的設置
在 TabNavigator()的第二個參數可以對標簽欄進行詳細的配置export default TabNavigator ( { Home:{screen:HomeStack}, //標簽頁Home對應HomeScreen組件 Message:{screen:MessageScreen} }, { tabBarComponent: TabBarBottom, //自定義標簽欄組件 tabBarPosition: 'bottom', //設置標簽欄位置 animationEnabled: true, //開啟標簽頁切換動畫 swipeEnabled: true, //允許標簽頁之間滑動切換 initialRouteName:'Home', //初始路由 tabBarOptions:{ //標簽欄的樣式設置如下↓ style:{ //整體標簽欄樣式設置 backgroundColor:'#49a9ff', }, tabStyle:{ //每個標簽的樣式 width:150 }, labelStyle:{ //標簽文字樣式 fontSize:16 }, iconStyle:{ //標簽圖標樣式 width:20, }, activeTintColor:'blue', //標簽激活時的前景色 activeBackgroundColor:'white', //標簽激活時的背景色 inactiveTintColor:'white', //標簽未激活時的前景色 inactiveBackgroundColor:'blue', //標簽未激活時的背景色 pressColor:'#9dbbff', //標簽被點擊時的顏色(僅安卓) showLabel:false, //將文字標簽隱藏,默認為true開啟 showIcon:true, //顯示圖標,默認為false隱藏 } } )
在每個路由可以分別通過navigationOptions對tabBar標簽進行其他設置:
Find:{ screen:FindScreen, //定義路由對應的組件 navigationOptions:{ title:'消息', //設置標題 tabBarVisible:false, //隱藏標簽欄,默認為true顯示 swipeEnabled:true, //是否允許滑動切換標簽頁,默認接收TabNavigator中的設置 tabBarIcon:(tab)=>renderIcon(tab,'message'), //定義渲染Icon的方法 tabBarLabel:'消息頁', //定義標簽文字或者渲染方法,如不設置默認渲染title tabBarOnPress:(obj)=>tapTab(obj) //標簽被點擊時觸發的方法 } },
渲染標簽的icon:首先需要在之前的TabNavigator()設置中開啟顯示icon,之后通過tabBarIcon屬性對應的方法來渲染icon,默認傳入參數{ focused: boolean, tintColor: string }
(我將它命名為tab),其中focused表示當前標簽是否被選中,tintColor為前景色。我定義了renderIcon方法來實現Icon的渲染,並傳入另外一個參數component代表不同組件,用於匹配對應的icon,例如當傳入'message'且未激活時,iconSrc為'tabbar_message':
function renderIcon(tab,component){ console.log('ictest'); let iconSrc=''; if (tab.focused){ //標簽激活狀態下icon的路徑 iconSrc=component+'_highlighted'; }else{ //未激活狀態下的icon iconSrc='tabbar_'+component; } return <Image source={{uri:'mipmap/'+iconSrc}} style={{width:30,height:30}} /> }標簽頁跳轉: 除了通過左右滑動切換標簽頁之外,還可以通過 this.props.navigation.navigate('組件名')手動跳轉。
3、DrawerNavigator
DrawerNavigator用於實現屏幕側邊欄拉出的導航效果,效果如下:
3.1、定義路由組件
在DrawerNavigator的各個路由之間實現跳轉,首先需要定義路由組件,其路由定義方式同以上兩種導航方式相同。DrawerNavigator()方法接收兩個參數,第一個為路由組件,第二個為參數設置,之后返回一個React組件,將它暴露給index.js,作為程序的默認入口。
3.2、打開側邊欄
除了通過在屏幕邊緣滑動外,還可以通過函數手動打開側邊欄:
this.props.navigation.navigate('DrawerOpen'); // 打開側邊欄 this.props.navigation.navigate('DrawerClose'); // 關閉側邊欄 this.props.navigation.navigate('DrawerToggle');//切換側邊欄打開/關閉
3.3、DrawerNavigator個性化設置
在其構造方法的第二個參數可以對組件進行一些常用的設置如下:
export default DrawerNavigator( { Home: { screen: HomeScreen }, Notifications: { screen: NotificationsScreen }, }, { drawerWidth:200, //側邊欄的寬度 drawerPosition:'right', //定義側邊欄位置右邊,默認left左邊 contentComponent:CustomDrawer, //自定義側邊欄組件 drawerBackgroundColor:'#c8eaff', //側邊欄背景色 contentOptions:{ //對側邊欄中的標簽詳細設置如下↓ activeTintColor:'#936eff', //標簽激活時的前景色 activeBackgroundColor:'#8fc3ff', //標簽激活時的背景色 inactiveTintColor:'#598dff', //標簽未激活時的前景色 inactiveBackgroundColor:'#c1e1ff', //標簽未激活時的背景色 itemsContainerStyle:{ //側邊欄整體樣式 borderTopWidth:2,borderTopColor:'#5153ff' }, itemStyle:{ //單個標簽樣式 borderBottomWidth:2,borderBottomColor:'#41a6ff' }, labelStyle:{ //標簽文字樣式 fontSize:16 }, iconContainerStyle:styles.icon, //標簽icon樣式 } } );
在每個組件內對側邊欄標簽的label、icon進行設置:
class HomeScreen extends React.Component { static navigationOptions = { drawerLabel: '主頁', //設置標簽label文字 drawerIcon: ({focused, tintColor}) => ( //設置標簽的icon <Image source={{uri: 'mipmap/tabbar_home'}} style={[styles.icon, {tintColor: tintColor}]} /> ), }; }
3.4、自定義側邊欄
通過上面的contentComponent來自定義DrawerNavigator組件為CustomDrawer:
class CustomDrawer extends Component{ constructor(props){ super(props); //通過super傳入上層調用的props } render(){ return ( <ScrollView> <SafeAreaView style={styles.container} forceInset={{ top: 'always', horizontal: 'never' }}> {/*自定義區域*/} <View style={{flex:1,alignItems:'center'}}> <Image source={{uri:'mipmap/user_icon'}} style={styles.userPic} /> </View> <DrawerItems {...this.props} /> </SafeAreaView> </ScrollView> ); } }
其中<SafeAreaView><DrawerItems>組件需要引入:
import {DrawerItems, SafeAreaView} from 'react-navigation'
把自定義的側邊欄內容放在<SafeAreaView>中。
如果希望保留導航標簽可以通過<DrawerItem>繪制出標簽列表,其中傳入props參數數組,並用操作符"..."展開。
代碼的GitHub鏈接如下:
https://github.com/SuperTory/ReactNativeNavigation
---------------------
作者:theVicTory
來源:CSDN
原文:https://blog.csdn.net/theVicTory/article/details/80059632
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!