ReactNative的ViewPagerAndroid简述

作者: SpikeKing | 来源:发表于2015-11-19 18:37 被阅读4201次

    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命令. 参考.

    修改项目架构, 主页直接跳转至ViewPager模块.

    '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);
      },
    
      // 跳转页面
      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,
      Text,
      Image,
      TouchableNativeFeedback, // 触碰响应
      TouchableOpacity, // 触碰更换透明度的属性
      ViewPagerAndroid, // Android的ViewPager
    } = React;
    

    TouchableNativeFeedback, 接触时会受到原生的响应; TouchableWithoutFeedback, 接触时无响应. TouchableOpacity, 接触时会改变透明度. 在加入onclick方法时, 可以模拟按钮视图.

    定义控件: LikeCount点赞, Button按钮, ProgressBar滚动条.

    3. 点赞(LikeCount)控件

    likes存储喜欢的数量. 点击时, 更新数量状态, 刷新页面. 其中, 喜欢按钮是触碰控件, 在接触时会改变透明度(Opacity).

    /**
     * 点赞功能页面
     * @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>
        );
      },
    });
    

    直接调用, 即可使用.

    <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();
        }
      },
    
      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>
        );
      }
    });
    

    在RN中, 并没有提供Button视图, 可以使用Touchable类的视图, 设置点击事件, 如TouchableNativeFeedback, 根据状态, 修改按钮的样式和点击. 注意: props表示属性, 在使用视图时提供.

    使用Button, 设置text显示, enabled状态, onPress点击事件.

              <Button
                text="首页"
                enabled={page > 0}
                onPress={() => this.go(0)}/>
    

    5. 滚动条(ProgressBar)控件

    主要监听ViewPager的滚动事件, 这和Android的滚动非常类似, fractionalPosition是偏移比例, progressBarSize是偏移量.

    /**
    * 滚动条, 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>
        );
      }
    });
    

    控件需要设置ProgressBar的属性progress(滚动位置)和size(大小).

    使用控件, 属性: size大小; progress进度状态, 其中position是位置, offset是偏移.

          progress: { // Progress位置
            position: 0,
            offset: 0,
          }
          ...
              <ProgressBar
                size={80}
                progress={this.state.progress}/>
    

    注意: progress是主页面属性, 重置状态时, 刷新全部页面.

    6. ViewPager页面

    根据页面定制属性, 根据页面ID设置不同图片和文字.

        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方法, 设置stateviewPage页.

      // 跳转页面
      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',
        alignItems: 'center',
        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,
        color: 'white',
        textAlign: 'center',
      },
    
      container: {
        flex: 1,
        backgroundColor: 'white',
      },
    
      image: {
        flex: 1,
        width: 300,
        padding: 20,
      },
    
      likeButton: {
        backgroundColor: 'rgba(0, 0, 0, 0.1)',
        borderColor: '#333333',
        borderWidth: 1,
        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',
        flex: 1,
        backgroundColor: '#eeeeee',
      },
    
      viewPager: {
        flex: 1,
      },
    });
    
    module.exports = styles;
    

    里面包含各个控件的样式, 主要使用了Flex+RN的Style.

    动画效果


    动画

    Github下载地址

    OK, 通过实现简单的ViewPagerAndroid学习了很多知识.

    相关文章

      网友评论

        本文标题:ReactNative的ViewPagerAndroid简述

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