React Native 学习之组件Navigator

Demo展示

4.gif

一个App由很多界面组成,在原生Android中是通过Activity来显示界面内容,用Intent来实现Activity之间的跳转。而在React Native中是通过Navigator(导航器)来管理整个界面中的routeStack(路由栈)。一个route对应着一个Scene(场景),不同的Scene对应着界面不同的内容。

简单使用Navigator

//Step 1 定义变量,记录每一个route信息。这里route信息包括title和index,当然还可以自定义更多的属性,这些属性可以作为一个route的标识
const routes = [{title: 'First Scene', index: 0}, {title: 'Second Scene', index: 1}];
class RNStudySix extends Component {
    render() {
        return (
                <Navigator
                    // Step 2 初始化第一个route
                    initialRoute={routes[0]}
                    // Step 3 给route渲染一个Scene
                    renderScene={(route, navigator)=>
                        // 每个Scene渲染的内容,这里仅仅是一个可点击的Text
                        <TouchableOpacity onPress={()=> {
                            if (route.index === 0) {
                                //目前界面为第一个route,此时按点击按钮,进入第二个route
                                navigator.push(routes[1]);
                            } else {
                                // 点击按钮,回到第一个route
                                navigator.pop();
                            }
                        }}>
                            <Text>Hello {route.title}!</Text>

                        </TouchableOpacity>
                    }
                    style={{padding: 100}}
                />);
    }
}

演示效果

1.gif

演示效果非常简单。在Step 3中的renderScene属性,查看它的源码:

/**
 * Required function which renders the scene for a given route. Will be
 * invoked with the `route` and the `navigator` object.
 *
 * ```
 * (route, navigator) =>
 *   <MySceneComponent title={route.title} navigator={navigator} />
 * ```
 */
renderScene: PropTypes.func.isRequired,

源码注释当中写得非常详细,属性类型必须是一个function,而且我们还可以得一个route和 navigator对象,以便我们后面的其他操作。Navigator中的push方法是将目标route压入routeStack中,并且是在栈顶,那么第一个route就被压入到了栈底。而pop方法是直接将routeStack中位于栈顶的route清除掉,因为栈的特点就是后进先出。

上图中界面切换的动画是从右往左变化的,那么我们可不可以改变他的切换方向了?答案是可以的。RN为我们提供了configureScene属性

<Navigator
             ...
             configureScene={(route, routeStack)=>
                Navigator.SceneConfigs.FloatFromBottom
             }
  />

演示效果:

2.gif

查看configureScene的源码,对于Scene之间的切换动画还可以有以下选择:

/**
 *  ...
 * ```
 * (route, routeStack) => Navigator.SceneConfigs.FloatFromRight
 * ```
 *
 * Available scene configutation options are:
 *
 *  - Navigator.SceneConfigs.PushFromRight (default)
 *  - 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
 *
 */
configureScene: PropTypes.func,

NavigationBar

既然Navigator是一个导航器,那么一个导航器肯定有一个导航栏,就好比原生Android有一个ToolBar一样,可以添加一些额外的功能,比如说返回键之类。在RN中提供了NavigationBar,我们来看看如何使用它

<Navigator
            
            ...
            initialRouteStack={routes}
            ...
            navigationBar={
                <Navigator.NavigationBar
                    style={styles.navContainer}
                    routeMapper={{
                        LeftButton: (route, navigator, index, navState)=> {
                            if (index != 0) {
                                return (
                                    <View style={styles.titleContainer}>
                                        <TouchableOpacity

                                            onPress={()=>navigator.jumpBack()}>
                                            <Text style={[styles.text, {marginLeft: 10}]}>Back</Text>
                                        </TouchableOpacity>
                                    </View>
                                );
                            }
                        },
                        RightButton: (route, navigator, index, navState)=> {
                            if (index == 0) {
                                return (
                                    <View style={styles.titleContainer}>
                                        <TouchableOpacity
                                            onPress={()=>navigator.jumpForward()}>
                                            <Text style={[styles.text, {marginRight: 10}]}>Next</Text>
                                        </TouchableOpacity>
                                    </View>);
                            }
                        },
                        Title: (route, navigator, index, navState)=> {
                            return (
                                <View style={styles.titleContainer}>
                                    <Text style={[styles.text, {marginLeft: 50}]}>Navigation Bar</Text>
                                </View>
                            );
                        }
                    }}
                />
            }
        />
-----------------------------
navContainer: {
    backgroundColor: 'blue'
},

titleContainer: {
    justifyContent: 'center',
    flex: 1,
},

text: {
    color: '#ffffff',
    fontSize: 20
},

NavigationBar的配置可以通过routeMapper属性来完成,它由三个部分组成:左中右,至于这三个部分显示什么完全可以自定义,上面代码中我配置成的是左右两个button,中间一个Text。按Next键时,这里是用的Navigator中的jumpForward方法,它与push方法类似,而jumpBack方法与pop方法虽都是回到上一个route,但是它们的区别在于是否将route从routeStack里面ummount(卸载)。通过源码注释可知,jumpBack方法并没有将当前的route卸载掉,将当前route压入栈底,上一个route回到栈顶,而pop方法是将当前route从routeStack中删除掉了。效果图如下:

3.gif

StatusBar

在Anroid中,在4.4以上的系统都比较流行沉浸式状态栏,也就是ToolBar或者自定义标题栏的颜色与状态栏的颜色保持一致,看看上面图,两个颜色,确实比较丑。不过不用着急,RN给我们提供了StatusBar的组件,使用也非常简单。

<View style={{flex: 1}}>
            <StatusBar
                backgroundColor='blue'
            />

            <Navigator
            ...
            />
</View>

我们来看看效果图

4.png

参数传递

两个route之间传递参数时,我们可以通过push方法来增加一些需要传递的数据,而被接收的route可以在构造方法来获取传递过来的数据。最后将完整代码贴上,与上面的代码略有不同

  • index.android.js

      var PageOne = require('./pageOne');
      var PageTwo = require('./pageTwo');
      var routeMapper = {
      
          LeftButton: (route, navigator, index, navState)=> {
              if (index != 0) {
                  return (
                      <View style={styles.titleContainer}>
                          <TouchableOpacity
      
                              onPress={()=>navigator.pop()}>
                              <Text style={[styles.text, {marginLeft: 10}]}>Back</Text>
                          </TouchableOpacity>
                      </View>
                  );
              }
          },
          RightButton: (route, navigator, index, navState)=> {
              if (index == 0) {
                  return (
                      <View style={styles.titleContainer}>
                          <TouchableOpacity
                              onPress={()=>navigator.push({
                                  title: 'PageTwo',
                                  index: 1,
                                  component: PageTwo,
                                  passProps: {
                                      id: 'First Page'
                                  }
                              })}>
                              <Text style={[styles.text, {marginRight: 10}]}>Next</Text>
                          </TouchableOpacity>
                      </View>);
              }
          },
          Title: (route, navigator, index, navState)=> {
              return (
                  <View style={styles.titleContainer}>
                      <Text style={[styles.text, {marginLeft: 50}]}>{route.title}</Text>
                  </View>
              );
          }
      };
      
      class RNStudySix extends Component {
      
          /**
           * 配置场景动画
           * @param route 路由
           * @param routeStack 路由栈
           * @returns {*} 动画
           */
          configureScene(route, routeStack) {
      
              if (route.sceneConfig) {
                  return route.sceneConfig;
              }
              return Navigator.SceneConfigs.PushFromRight
      
          }
      
          /**
           * 渲染场景
           * @param route
           * @param navigator
           * @returns {XML}
           */
          renderScene(route, navigator) {
      
              return <route.component
                  navigator={navigator}
                  {...route.passProps}/>;
          }
      
          render() {
      
              return (
                  <View style={{flex: 1}}>
                      <StatusBar
                          backgroundColor='blue'
                      />
      
                      <Navigator
                          initialRoute={{title: 'PageOne', index: 0, component: PageOne}}
                          renderScene={this.renderScene}
                          navigationBar={
                              <Navigator.NavigationBar
                                  style={styles.navContainer}
                                  routeMapper={routeMapper}
                              />
                          }
                          configureScene={this.configureScene}
                      />
                  </View>
              );
          }
      }
      var styles = StyleSheet.create({
      
          navContainer: {
              backgroundColor: 'blue'
          },
          titleContainer: {
              justifyContent: 'center',
              flex: 1,
          },
      
          text: {
              color: '#ffffff',
              fontSize: 20
          },
      });
      
      AppRegistry.registerComponent('RNStudySix', () => RNStudySix);
    
  • PageOne.js和PageTwo.js

      class PageOneComponent extends Component {
      
          render() {
              return (
                  <View>
                      <Text style={[{fontSize: 18}, {padding: 100}]}>我是PageOne</Text>
                  </View>
              );
          }
      
      }
      module.exports = PageOneComponent;
      
      ----------------------------------
      
      class PageTwoComponent extends Component {
      
          constructor(props) {
              super(props);
              this.state = {
                  id: this.props.id
              };
          }
          render() {
              return (
                  <View>
                      <Text style={[{padding: 100}, {fontSize: 18}]}>我是PageTwo,收到的数据:{this.state.id}</Text>
                  </View>
              );
          }
      
      }
      
      module.exports = PageTwoComponent;
    

Navigator还有些其他API,自己可以查看官网API或者源码进行研究。好了,我们的Navigator就学习完了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容