近期将项目中的reactnative升级到了0.60.x版本,同步的也将ReactNavigation升级到了4.x。这篇博客记录了4.x版本的一些基本用法以及在实现项目中一些常见功能的实现,其中rn基于0.60版本。
安装4.x版本从react-navigation
中移除了各类导航器,同时还依赖了一些其他的包需要手动安装。
npminstallreact-navigationreact-native-reanimatedreact-native-gesture-handlerreact-native-screensreact-native-safe-area-context
rn0.60版本之后,安装完成之后会自动link,低版本安装过程见官网说明。
Android端需要手动进行一些修改,编辑android/app/build.gradle
,在dependencies
中添加如下内容:
implementation'androidx.appcompat:appcompat:1.1.0-rc01'implementation'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'
编辑Android中的MainActivity.java
,添加如下内容:
packagecom.reactnavigation.example;importcom.facebook.react.ReactActivity;importcom.facebook.react.ReactActivityDelegate;importcom.facebook.react.ReactRootView;importcom.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;publicclassMainActivityextendsReactActivity{@OverrideprotectedStringgetMainComponentName(){return"Example";}@OverrideprotectedReactActivityDelegatecreateReactActivityDelegate(){returnnewReactActivityDelegate(this,getMainComponentName()){@OverrideprotectedReactRootViewcreateRootView(){returnnewRNGestureHandlerEnabledRootView(MainActivity.this);}};}}
最后在index.js
或者app.js
中导入react-native-gesture-handler
依赖即可。
import'react-native-gesture-handler';
基本使用4.x
版本移除了各类导航器,需要手动安装,这里安装一下StackNavigator
和BottomTabNavigator
。
npminstallreact-navigation-stack@react-native-community/masked-viewreact-navigation-tabs
添加三个页面组件:
constHome=props=>{const{navigation}=propsreturn(<View><TouchableOpacityonPress={()=>navigation.push('Second')}><View><Text>Secondpage</Text></View></TouchableOpacity></View>)}constMy=()=>{return(<View><Text>My</Text></View>)}constSecond=()=>{return(<View><Text>Second</Text></View>)}
创建导航器,这一部分写法和之前一样:
constMainTab=createBottomTabNavigator({Home,My,})constAppStack=createStackNavigator({Main:{screen:MainTab,},Second,})exportdefaultfunctionApp(){constAppContainer=createAppContainer(AppStack)return<AppContainer/>}
调整TabNavigatorHeader标题从上图可以看到,Home和My页面顶部标题都是现实的Main,因为这两个页面都在BottomTabNavigator
中,共用了一个Header。我们可以通过Main页面的navigationOptions来动态修改标题:
Main:{screen:MainTab,navigationOptions:({navigation})=>{const{routeName}=navigation.state.routes[navigation.state.index]//YoucandowhateveryoulikeheretopickthetitlebasedontheroutenameconstheaderTitle=routeNamereturn{headerTitle,}}}
这里直接使用了routeName作为标题,也可以根据routeName匹配其他的标题文字。效果如下:
添加Tabbar图标通过BottomTabNavigator
navigationOptions中的tabBarIcon
属性可以设置Tabbar的图标。
首先实现一个TabbarIcon组件,根据routeName返回相应的图片,同时图片会读取tintColor
属性设置颜色:
constIMAGES={Home:require('./assets/icons/home.png'),My:require('./assets/icons/my.png'),}constTabbarIcon=({routeName,tintColor})=>{return(<Imagesource={IMAGES[routeName]}style={[styles.image,{tintColor:tintColor}]}resizeMode="contain"/>)}conststyles=StyleSheet.create({image:{height:24,},})
然后通过BottomTabNavigator
的defaultNavigationOptions
属性设置不同页面的图标:
defaultNavigationOptions:({navigation})=>{const{routeName}=navigation.statereturn{tabBarIcon:props=><TabbarIcon{...props}routeName={routeName}/>,}}
还可以通过tabBarOptions
设置激活和未激活的颜色:
tabBarOptions:{inactiveTintColor:'rgba(0,0,0,0.45)',activeTintColor:'#722ed1',}
统一路由风格StackNavigator
在Android中的表现和iOS中存在一些差异,通过暴露的一些配置项我们可以统一风格,这里统一使用iOS的风格。
首先实现一个HeaderBackImage组件,统一返回图标:
constIS_IOS=Platform.OS==='ios'exportdefaultprops=>{return(<Viewstyle={styles.imgContainer}><Imagesource={require('./assets/icons/icon_back.png')}style={[styles.image,{tintColor:props.tintColor}]}{...props}/></View>)}conststyles=StyleSheet.create({imgContainer:{paddingRight:IS_IOS?6:15,paddingLeft:IS_IOS?15:0,},image:{backgroundColor:'transparent',height:16,width:10,resizeMode:'contain',},})
修改StackNavigator
配置:
defaultNavigationOptions:{headerStyle:{elevation:0,//移除AndroidHeader阴影shadowOpacity:0,//移除iOSHeader阴影},headerBackImage:HeaderBackImage,headerTitleAlign:'center',//Android标题居中headerBackTitleVisible:false,//隐藏iOS返回按钮标题headerPressColorAndroid:'transparent',//移除Android点击返回按钮效果cardStyleInterpolator:CardStyleInterpolators.forHorizontalIOS,//切换路由时水平动画headerStyleInterpolator:HeaderStyleInterpolators.forUIKit,//切换路时Header动画},headerMode:'float',//页面共用一个Header,切换时应用动画
沉浸式状态栏之前我写过一篇关于实现沉浸式状态栏的博客,在4.x版本中实现该功能变得更加方便。
纯色首先我们给Header添加一个背景色看看默认是什么效果。
headerStyle:{backgroundColor:'#722ed1',},headerTintColor:'#fff',
可以看到Android端状态栏依旧是灰色。我们可以设置StatusBar
的属性来实现沉浸式:
<><StatusBarbackgroundColor="transparent"translucent/><AppContainer/></>
可以看到设置了translucent
之后,内容并没有往上移动到状态,4.x版本默认处理了这种情况。
有时页面顶部有一张背景图或者整个页面有个全屏背景,这时我们就需要Header透明并且内容能衍生到状态栏底部。
我们先修改Second页面,添加一个背景图片:
constSecond=()=>{return(<ImageBackgroundstyle={{flex:1}}source={require('../../assets/img/bg.jpg')}><Text>Ihaveafullscreenbackgroundimage</Text></ImageBackground>)}
可以看到图片并没有撑满整个屏幕,设置headerTransparent
属性为true
可以使Header透明并浮在页面上,这样内容就会撑满。
Second.navigationOptions={headerTransparent:true,}
此时虽然全屏了,但是内容却跑到Header底部除了,我们需要添加上边距预留出Header的位置,通过useHeaderHeight
可以获取到Header的高度。
import{useHeaderHeight}from'react-navigation-stack'constheaderHeight=useHeaderHeight()
处理状态栏文字RN中切换到不同的页面,可能需要显示不能颜色的文字(深色、浅色),在之前的博客中介绍了使用高阶组件的方法,监听navigation的willFocus
事件切换。4.x版本中仍需要使用此方案,不过这次通过hook来实现。
//useStatusBar.jsimport{useEffect}from'react'import{StatusBar}from'react-native'constuseStatusBar=(navigation,barStyle)=>{useEffect(()=>{constonWillFocus=()=>{StatusBar.setBarStyle(barStyle)}StatusBar.setBarStyle(barStyle)constlistener=navigation.addListener('willFocus',onWillFocus)return()=>listener.remove()},[])}exportdefaultuseStatusBar
当页面挂载之后设置状态栏的风格,后续当从其他页面返回时会触发willFocus
事件。