ViewPager是Android中比较常见的页面切换控件,同时,在UIExplorerApp
中也有ViewPagerAndroid
的示例. 通过使用这个控件,理解ReactNative的实现逻辑. 我们现在来分析一下ViewPager的使用方式和ReactNative的编程要点,本文注释也很清晰.
1. 准备
新建ReactNative的项目.
- npm install -g react-native-cli
- react-native init [TestViewPager]
-g
是全局,react-native-cli
是react-native的命令行工具(command line interface). 安装需要管理员权限(sudo),安装一次即可,使用react-native命令. 参考.
- 'use strict';
-
- var React = require('react-native');
-
- var {
- AppRegistry,} = React;
-
- var ViewPagerModule = require('./ViewPagerModule/index')
-
- AppRegistry.registerComponent('TestViewPager',() => ViewPagerModule);
2. 概述
ViewPager包含若干滑动页面; 点赞选项-模拟页面交互; 按键和滚动条-模拟滚动监听.
- 'use strict'
-
- var React = require('react-native');
-
- var {
- View,Text,Image,TouchableNativeFeedback,// 触碰响应
- TouchableOpacity,// 触碰更换透明度的属性
- ViewPagerAndroid,// Android的ViewPager
- } = React;
-
- // Styles
- var styles = require('./style');
-
- var PAGES = 5; // 页数
-
- // 颜色
- var BGCOLOR = ['#8ad3da','#eecde2','#e682b4','#b7badd','#f1c7dd'];
-
- // 本地图片地址
- var IMAGE_URIS = [
- require('./images/jessicajung.png'),require('./images/tiffany.png'),require('./images/SEOhyun.png'),require('./images/taeyeon.png'),require('./images/yoona.png'),];
-
- // 名称
- var NAMES = ['Jessica','Tiffany','SEOhyun','Taeyeon','Yoona'];
-
- /** * 点赞功能页面 * @param {likes: 点赞数} * @return {点赞视图} [点赞按钮,动态增加点赞数] */
- var LikeCount = React.createClass({
- // 初始化状态
- getInitialState: function() {
- return {
- likes: 0,};
- },// 点击增加
- onClick: function() {
- this.setState({likes: this.state.likes + 1});
- },render: function() {
- var thumbsUp = '\uD83D\uDC4D'; // 图标
- return (
- <View style = {styles.likeContainer}>
- <TouchableOpacity
- onPress={this.onClick}
- style={styles.likeButton}>
- <Text style={styles.likesText}>
- {thumbsUp}
- </Text>
- </TouchableOpacity>
- <Text style={styles.likesText}>
- {this.state.likes + ' 喜欢'}
- </Text>
- </View>
- );
- },});
-
- /** * 按钮: 添加点击状态(enabled)和文本(text) * @param {enabled:点击状态} {text:显示文本} {onPress:点击事件} * @return {TouchableNativeFeedback} [触摸反馈的视图] */
- var Button = React.createClass({
- _handlePress: function() {
- if (this.props.enabled && this.props.onPress) {
- this.props.onPress();
- }
- },render: function() {
- return (
- <TouchableNativeFeedback onPress={this._handlePress}>
- <View style={[styles.button,this.props.enabled ? {} : styles.buttonDisabled]}>
- <Text style={styles.buttonText}>
- {this.props.text}
- </Text>
- </View>
- </TouchableNativeFeedback>
- );
- }
- });
-
- /** * 滚动条,fractionalPosition滚动条长度,progressBarSize当前大小 * @param {size:滚动条大小} {progress:过程} * @return {View} [里外两层视图,背景白框黑底,显示白框] */
- var ProgressBar = React.createClass({
- render: function() {
- var fractionalPosition = (this.props.progress.position + this.props.progress.offset);
- var progressBarSize = (fractionalPosition / (PAGES - 1)) * this.props.size;
- return (
- <View style={[styles.progressBarContainer,{width: this.props.size}]}>
- <View style={[styles.progressBar,{width: progressBarSize}]}/>
- </View>
- );
- }
- });
-
-
- var ViewPagerModule = React.createClass({
-
- /** * 初始化状态 * @return {状态} [页面] */
- getInitialState: function() {
- return {
- page: 0,// 当前位置
- progress: { // Progress位置
- position: 0,offset: 0,}
- };
- },// 页面选择
- onPageSelected: function(e) {
- this.setState({page: e.nativeEvent.position});
- },// 页面滚动
- onPageScroll: function(e) {
- this.setState({progress: e.nativeEvent});
- },// 移动页面
- move: function(delta) {
- var page = this.state.page + delta;
- this.go(page);
- },// @R_926_404@面
- go: function(page) {
- this.viewPage.setPage(page);
- this.setState({page});
- },render: function() {
- var pages = [];
-
- for (var i=0; i<PAGES; i++) {
- // 背景
- var pageStyle = {
- backgroundColor: BGCOLOR[i % BGCOLOR.length],alignItems: 'center',padding: 20,}
-
- pages.push(
- <View
- key={i}
- style={pageStyle}
- collapsable={false}>
- <Image
- style={styles.image}
- resizeMode={'cover'}
- source={IMAGE_URIS[i%PAGES]}
- />
- <Text style={styles.nameText}>
- {NAMES[i%PAGES]}
- </Text>
- <LikeCount />
- </View>
- );
- }
- var {page} = this.state;
-
- return (
- <View style={styles.container}>
- <ViewPagerAndroid
- style={styles.viewPager}
- initialPage={0}
- onPageScroll={this.onPageScroll}
- onPageSelected={this.onPageSelected}
- ref={viewPager => {this.viewPage = viewPager;}}>
- {pages}
- </ViewPagerAndroid>
-
- <View style={styles.buttons}>
- <Button
- text="首页"
- enabled={page > 0}
- onPress={() => this.go(0)}/>
- <Button
- text="上一页"
- enabled={page > 0}
- onPress={() => this.move(-1)}/>
- <Text style={styles.buttonText}>
- 页 {page+1} / {PAGES}
- </Text>
- {/*进度条*/}
- <ProgressBar
- size={80}
- progress={this.state.progress}/>
- <Button
- text="下一页"
- enabled={page < PAGES - 1}
- onPress={() => this.move(1)}/>
- <Button
- text="尾页"
- enabled={page < PAGES - 1}
- onPress={() => this.go(PAGES -1)}/>
- </View>
- </View>
- );
- },});
-
- module.exports = ViewPagerModule;
引入RN的原生模块
- var {
- View,// Android的ViewPager
- } = React;
TouchableNativeFeedback
,接触时会受到原生的响应;TouchableWithoutFeedback
,接触时无响应.TouchableOpacity
,接触时会改变透明度. 在加入onclick
方法时,可以模拟按钮视图.
定义控件: LikeCount
点赞,Button
按钮,ProgressBar
滚动条.
3. 点赞(LikeCount)控件
likes
存储喜欢的数量. 点击时,更新数量状态,刷新页面. 其中,喜欢按钮
是触碰控件,在接触时会改变透明度(Opacity).
直接调用,即可使用.
- <LikeCount />
state的like属性,仅仅作用于当前页面,每次更新时刷新.
注意: 一定要谨慎使用全局state
,这样会刷新整个页面,影响效率.
4. 按钮(Button)控件
添加使用状态
,是否可以点击,根据状态修改视图样式和点击回调.
- /** * 按钮: 添加点击状态(enabled)和文本(text) * @param {enabled:点击状态} {text:显示文本} {onPress:点击事件} * @return {TouchableNativeFeedback} [触摸反馈的视图] */
- var Button = React.createClass({
- _handlePress: function() {
- if (this.props.enabled && this.props.onPress) {
- this.props.onPress();
- }
- },this.props.enabled ? {} : styles.buttonDisabled]}>
- <Text style={styles.buttonText}>
- {this.props.text}
- </Text>
- </View>
- </TouchableNativeFeedback>
- );
- }
- });
在RN中,并没有提供Button视图,可以使用
Touchable
类的视图,设置点击事件,如TouchableNativeFeedback
,根据状态,修改按钮的样式和点击. 注意:props
表示属性,在使用视图时提供.
使用Button,设置text
显示,enabled
状态,onPress
点击事件.
- <Button
- text="首页"
- enabled={page > 0}
- onPress={() => this.go(0)}/>
5. 滚动条(ProgressBar)控件
主要监听ViewPager的滚动事件,这和Android的滚动非常类似,fractionalPosition
是偏移比例,progressBarSize
是偏移量.
- /** * 滚动条,{width: progressBarSize}]}/>
- </View>
- );
- }
- });
控件需要设置
ProgressBar
的属性progress
(滚动位置)和size
(大小).
使用控件,属性: size大小; progress进度状态,其中position是位置,offset是偏移.
- progress: { // Progress位置
- position: 0,}
- ...
- <ProgressBar
- size={80}
- progress={this.state.progress}/>
6. ViewPager页面
- for (var i=0; i<PAGES; i++) { // 背景 var pageStyle = { backgroundColor: BGCOLOR[i % BGCOLOR.length],alignItems: 'center',padding: 20,} pages.push( <View key={i} style={pageStyle} collapsable={false}> <Image style={styles.image} resizeMode={'cover'} source={IMAGE_URIS[i%PAGES]} /> <Text style={styles.nameText}> {NAMES[i%PAGES]} </Text> <LikeCount /> </View> ); }
注意设置图片资源时,
使用IMAGE_URIS =['xxx.png']
和require(IMAGE_URIS[i%PAGES])
会显示模块丢失.
在数组IMAGE_URIS
使用require('./images/jessicajung.png')
加载source就可以.
思考很久的问题…
ViewPager页面
- <View style={styles.container}> <ViewPagerAndroid style={styles.viewPager} initialPage={0} onPageScroll={this.onPageScroll} onPageSelected={this.onPageSelected} ref={viewPager => {this.viewPage = viewPager;}}> {pages} </ViewPagerAndroid> <View style={styles.buttons}> <Button text="首页" enabled={page > 0} onPress={() => this.go(0)}/> <Button text="上一页" enabled={page > 0} onPress={() => this.move(-1)}/> <Text style={styles.buttonText}> 页 {page+1} / {PAGES} </Text> {/*进度条*/} <ProgressBar size={80} progress={this.state.progress}/> <Button text="下一页" enabled={page < PAGES - 1} onPress={() => this.move(1)}/> <Button text="尾页" enabled={page < PAGES - 1} onPress={() => this.go(PAGES -1)}/> </View> </View>
使用方法,监听ViewPagerAndroid
的事件,onPageScroll
页面滚动,onPageSelected
页面选择,按钮关联页面. 核心在于go
方法,设置state
的viewPage
页.
- // @R_926_404@面
- go: function(page) {
- this.viewPage.setPage(page);
- this.setState({page});
- },
7. Styles
引入styles
,使用独立文件.
- // Styles
- var styles = require('./style');
样式
- 'use strict';
-
- var React = require('react-native');
-
- var {
- StyleSheet,} = React;
-
- var styles = StyleSheet.create({
- buttons: {
- flexDirection: 'row',height: 40,backgroundColor: 'pink',justifyContent: 'space-between',},// 按钮可点击状态
- button: {
- flex: 1,width: 0,margin: 2,borderColor: 'gray',borderWidth: 1,backgroundColor: 'gray',// 按钮非点击装
- buttonDisabled: {
- backgroundColor: 'black',opacity: 0.5,buttonText: {
- fontSize: 12,color: 'white',textAlign: 'center',// 文字显示
- nameText: {
- fontSize: 16,margin: 4,container: {
- flex: 1,backgroundColor: 'white',image: {
- flex: 1,width: 300,likeButton: {
- backgroundColor: 'rgba(0,0.1)',borderColor: '#333333',borderRadius: 5,flex: 1,margin: 8,padding: 8,likeContainer: {
- flexDirection: 'row',likesText: {
- flex: 1,fontSize: 18,alignSelf: 'center',progressBarContainer: {
- height: 10,margin: 5,borderColor: '#eeeeee',borderWidth: 2,progressBar: {
- alignSelf: 'flex-start',backgroundColor: '#eeeeee',viewPager: {
- flex: 1,});
-
- module.exports = styles;
里面包含各个控件的样式,主要使用了Flex+RN的Style.
动画效果
Github下载地址
OK,通过实现简单的ViewPagerAndroid学习了很多知识.