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 
版权声明:本文为博主原创文章,转载请附上博文链接!

上一篇:java – 如何向导航抽屉中的项添加图标


下一篇:Unity Navigation 导航、寻路系统