React Native--导航

作者: fyales | 来源:发表于2016-06-12 19:02 被阅读2014次

    前面三篇文章我们分别介绍了React基础Flexbox模型手势响应,今天我们介绍任何软件,系统都很重要的导航部分。

    Navigator

    对于Android开发人员来讲(IOS猿请忽略),Android页面之间的跳转是由工作栈维护的,进入一个页面,一般的是入栈,而退出一个页面,就意味着出栈。我们在开发app的时候,之所以没有感觉到出栈入栈,是因为Android将这些都封装好了。React Native则比较粗暴,你从它页面的跳转方法就可以明显的看出,它是由栈实现的,归结到组件的话就是Navigator组件(IOS有NavigatorIOS组件,相较于Navigator,效率更高)。下面来看一个简单的例子:

    import FirstPageComponent from './FirstPageComponent';  
    
    class demoReact extends Component {
        render() {
          let defaultName = 'FirstPageComponent';
          let defaultComponent = FirstPageComponent;
          let defaultTitle = "First Page";
    
          return (
            <Navigator
                initialRoute={{
                  name: defaultName,
                  component: defaultComponent,
                  title: defaultTitle,
                }}
                configureScene={(route) => {
                           return Navigator.SceneConfigs.VerticalDownSwipeJump;
                         }}
                renderScene={(route, navigator) => {
                           let Component = route.component;
                           return <Component {...route.params} navigator={navigator} />
                         }}
            />
          );
        }
    
    };
    

    其中:

    • initialRoute:定义了启动加载时的路由,上面的示例定义了组件名称,组件和页面的标题。
    • configureScene:定义了页面切换时的过渡动画,可选的值有:
      • Navigator.SceneConfigs.PushFromRight (默认)
      • Navigator.SceneConfigs.FloatFromRight
      • Navigator.SceneConfigs.FloatFromLeft
      • Navigator.SceneConfigs.FloatFromBottom
      • Navigator.SceneConfigs.FloatFromBottomAndroid
      • Navigator.SceneConfigs.FadeAndroid
      • Navigator.SceneConfigs.HorizontalSwipeJump
      • Navigator.SceneConfigs.HorizontalSwipeJumpFromRight
      • Navigator.SceneConfigs.VerticalUpSwipeJump
      • Navigator.SceneConfigs.VerticalDownSwipeJump
    • renderScene:此方法时必要参数。用来渲染指定路由的场景。调用的参数是路由和导航器。

    FirstPageComponent.js的代码如下:

    import React from 'react';
    
    import {
        View,
        Navigator,
        TouchableOpacity,
        Text,
        StyleSheet
    } from 'react-native';
    
    import SecondPageComponent from './SecondPageComponent';
    
    export default class FirstPageComponent extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
              id:1,
              user:null,
            };
        }
    
        componentDidMount() {
        }
    
        _pressButton() {
            const { navigator } = this.props;
            if(navigator) {
                navigator.push({
                    name: 'SecondPageComponent',
                    component: SecondPageComponent,
                    title:'Second Page',
                    params:{
                      id:this.state.id,
                      getUser: (user) => {
                          this.setState({
                              user: user
                          })
                      }
                    }
    
                })
            }
        }
        render() {
              if(this.state.user) {
                      return(
                          <View style={styles.container}>
                              <Text>用户信息: { JSON.stringify(this.state.user) }</Text>
                          </View>
                      );
                  }else {
                      return(
                          <View style={styles.container}>
                              <TouchableOpacity onPress={this._pressButton.bind(this)} style={styles.userLayout}>
                                  <Text >查询ID为{ this.state.id }的用户信息</Text>
                              </TouchableOpacity>
                          </View>
                      );
                  }
        }
    }
    
    var styles = StyleSheet.create({
      container: {
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
        marginTop:100,
      },
      userLayout:{
        borderWidth:1,
        borderColor:'black',
        padding:10,
      }
    });
    

    SecondPageComponent.js的代码
    import React from 'react';

    import {
        View,
        Navigator,
        Text,
        TouchableOpacity,
        StyleSheet
    } from 'react-native';
    
    import FirstPageComponent from './FirstPageComponent';
    
    const USER_MODELS = {
        1: { name: 'fyales一号', age: 23 },
        2: { name: 'fyales二号', age: 25 }
    };
    
    export default class SecondPageComponent extends React.Component {
    
        constructor(props) {
            super(props);
            this.state = {
              id:null,
              title:"Second Page",
            };
        }
    
        componentDidMount(){
          this.setState({
            id:this.props.id,
            title:this.props.title,
          });
        }
    
        _pressButton() {
            const { navigator } = this.props;
    
            if(this.props.getUser) {
                   let user = USER_MODELS[this.props.id];
                   this.props.getUser(user);
               }
    
            if(navigator) {
                //出栈
                navigator.pop();
            }
        }
    
        render() {
        return (
                <View style = {styles.container}>
                <Text>获得的参数: id={ this.state.id }</Text>
                <TouchableOpacity onPress={this._pressButton.bind(this)} style={styles.backLayout}>
                    <Text>返回</Text>
                </TouchableOpacity>
                </View>
        );
        }
    }
    
    var styles = StyleSheet.create({
      container: {
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
        marginTop:100,
      },
      backLayout:{
        borderWidth:1,
        borderColor:'black',
        padding:10,
      }
    });
    

    我们可以先看看FirstPageComponent.js的跳转逻辑,navigator.push()方法就是将SecondPage入栈,同样的,我们通过定义组件名称,组件来找到相应的文件,并将下一个页面需要展示的title传到下一个页面。而且,我们通过params参数传了一些数据和一个回调函数给下一个页面。这里需要注意的是,states存储的一般的是当前页面的值,而props存储的一般是其他页面传给本页面的值。

    在我们从FirstPage跳到SecondPage之后,再点击返回按钮的时候,_pressButton方法首先执行了第一个页面的回调方法,从而刷新了第一个页面的状态,同时第二个页面出栈,返回到了第一个页面。

    除了最基本的push方法和pop方法,navigator还有许多其他方法进行导航:

    • getCurrentRoutes():获取当前栈里的路由,也就是push进来,没有pop掉的那些。
    • jumpBack():跳回之前的路由,当然前提是保留现在的,还可以再跳回来,会给你保留原样。
    • jumpForward():上一个方法不是调到之前的路由了么,用这个跳回来就好了。
    • jumpTo(route): 跳转到已有的场景并且不卸载。
    • push(route) :跳转到新的场景,并且将场景入栈,你可以稍后跳转过去
    • pop():跳转回去并且卸载掉当前场景
    • replace(route):用一个新的路由替换掉当前场景
    • replaceAtIndex(route, index):替换掉指定序列的路由场景
    • replacePrevious(route): 替换掉之前的场景
    • resetTo(route):跳转到新的场景,并且重置整个路由栈
    • immediatelyResetRouteStack(routeStack):用新的路由数组来重置路由栈
    • popToRoute(route):pop到路由指定的场景,在整个路由栈中,处于指定场景之后的场景将会被卸载。
    • popToTop():pop到栈中的第一个场景,卸载掉所有的其他场景。

    navigationBar(导航栏)

    同样的,为了实现类似Android导航栏的功能,React Native提供了NavigationBar(类似于Android的Toolbar,不过个人觉得不太好用,建议用第三方库),加了NavigationBar的index.android.js代码如下:

    import React, {
        Component,
    } from 'react';
    
    import {
        AppRegistry,
        StyleSheet,
        Text,
        View,
        Navigator,
        TouchableOpacity
    } from 'react-native';
    
    import FirstPageComponent from './FirstPageComponent';
    
        class demoReact extends Component {
    
            render() {
              let defaultName = 'FirstPageComponent';
              let defaultComponent = FirstPageComponent;
              let defaultTitle = "First Page";
    
              return (
                <Navigator
                    initialRoute={{
                      name: defaultName,
                      component: defaultComponent,
                      title: defaultTitle,
                    }}
                    configureScene={(route) => {
                               return Navigator.SceneConfigs.FadeAndroid;
                             }}
                    renderScene={(route, navigator) => {
                               let Component = route.component;
                               return <Component {...route.params} navigator={navigator} />
                             }}
                    navigationBar={
                               <Navigator.NavigationBar
                                 routeMapper={NavigationBarRouteMapper}
                                 style={styles.navBar}
                               />
                             }
                />
              );
            }
    
        };
    
    var NavigationBarRouteMapper = {
    
      LeftButton: function(route, navigator, index, navState) {
        if (index === 0) {
          return null;
        }
    
        return (
          <View style={styles.navBarLeftButton}>
          <TouchableOpacity
            onPress={() => navigator.pop()}
            >
            <Text style={styles.navBarText}>
              Return
            </Text>
          </TouchableOpacity>
          </View>
        );
      },
    
      RightButton: function(route, navigator, index, navState) {
        return (
          <View style={styles.navBarRightButton}>
          <TouchableOpacity
            >
            <Text style={styles.navBarText}>
              Next
            </Text>
          </TouchableOpacity>
          </View>
        );
      },
    
      Title: function(route, navigator, index, navState) {
        return (
          <View style={styles.navbarTitle}>
          <Text style={styles.navBarTitleText}>
            {route.title}
          </Text>
          </View>
        );
      },
    
    };
    
    var styles = StyleSheet.create({
      navBar: {
        flexDirection: 'row',  //只支持column和row两种属性
        flexWrap:'nowrap',  //只支持wrap和nowrap两种属性
        justifyContent:'center',  //主轴
        alignItems:'stretch',  //交叉轴
        backgroundColor: 'orange',
      },
      navBarText: {
        fontSize: 16,
        color:'white',
        textAlign:'center',
      },
      navbarTitle:{
        flex:2,
        alignSelf:'center',
        paddingTop:15,
      },
      navBarTitleText: {
        color: 'white',
        fontSize:18,
        textAlign:'left',
      },
      navBarLeftButton: {
        flex:1,
        paddingLeft: 10,
        paddingTop:18,
        alignSelf:'flex-start',
      },
      navBarRightButton: {
        flex:1,
        paddingRight: 10,
        paddingTop:18,
        alignSelf:'flex-end',
      },
    
    });
    
    AppRegistry.registerComponent('demoReact', () => demoReact);
    

    代码中的NavigationBarRouteMapper是导航栏路由映射器, 设置左边按钮,右边按钮和标题。完整的效果图如下:

    第一次进入页面
    进入第二页
    点击按钮返回

    第三方实现

    React Native Simple Router是一款第三方导航组件。你可以通过它进行合理的视图组织。

    参考

    新手理解navigator的教程
    React Native 的 Navigator 组件使用方式

    相关文章

      网友评论

        本文标题:React Native--导航

        本文链接:https://www.haomeiwen.com/subject/tbredttx.html