React Native 学习之组件Navigator

作者: BlainPeng | 来源:发表于2016-09-25 16:36 被阅读425次

    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就学习完了。

    相关文章

      网友评论

        本文标题:React Native 学习之组件Navigator

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