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