美文网首页
使用React Native写一个Demo

使用React Native写一个Demo

作者: _Joeyoung_ | 来源:发表于2018-03-08 11:52 被阅读100次

    babyShow

    基于React-Native框架练习的demo.

    初始化一个项目:

    $ react-native init babyShow --version 0.44.3
    

    划分模块

    在根目录新建一个 MyComponent 文件夹,存放实现App功能的文件。
    demo底部分为4个tabBar,创建四个文件夹与之对应:

    • List:视频
    • Edit:录制
    • Picture:图片
    • Account:我的

    开始demo的开发:

    MyComponent 文件夹下创建:

    • App.js:作为 androidiOS 公用入口文件,只需要在对应的 index.android.jsindex.ios.js 文件里引入 App.js 即可
    import React, { Component } from 'react';
    import {
      AppRegistry
    } from 'react-native';
    import App from './MyComponent/App';
    
    export default class babyShow extends Component {
      render() {
        return (
          <App/>
        );
      }
    }
    AppRegistry.registerComponent('babyShow', () => babyShow);
    
    • QYTabBar.js:处理底部自定义tabBar的文件。
      1.项目中使用的tabBar引入了一个优秀的第三方的库 react-native-scrollable-tab-view
      2.tabBar上的icon使用了一个库 ionicons.
    在项目路径下执行终端:
    // 引入tabBar的库
    $ npm install react-native-scrollable-tab-view --save 
    // icon库
    $ npm install react-native-vector-icons --save 
    // link,使ios项目关联icon需要的库,(不需要我们自己手动引入了)
    $ react-native link
    

    一、视频模块

    List 文件夹下创建 list.js

    实现逻辑:

    (1)使用 ListView 组件创建列表:(实现细节请看源码)
    (2)列表展示暂时没有真实接口,使用RAP模拟假的接口,借用Mock语法,模拟了假数据;在项目中 npm install mockjs --save 安装mockjs.
    (3)封装请求类:
    • a). 对请求类进行封装,在 MyComponent 文件夹下创建 Common 文件;
    • b). 在 Common 文件夹下创建 config.js :处理post请求的一些基类参数;
    • c). 在 Common 文件夹下创建 request.js :处理 POST/GET 请求;
    • d). npm isntall query-string --save:把参数拼接到get请求url上的工具;
    • e). npm install lodash --save:合并json工具.

    完成之后的效果:

    (4)添加下拉加载更多效果:
    为了实现分页效果,我在之前rap定义的接口中加入了两个字段。

    a). 加入加载更多方法

    // 代码中加入上拉加载更多
    <ListView
      dataSource={this.state.dataSource}
      renderRow={this._renderRow}
      style={styles.listView}
      onEndReached={this._fetchMoreData}
      onEndReachedThreshold={20}
    >
    </ListView>
    

    b). 初始化一个对象记录我们的参数:

     // 数据缓存
    let cachedResults={
      // 当前加载的页码
      nextPage:1,
      // 服务器返回的数据
      items:[],
      // 服务器数据总条数
      total:0
    };
    

    c). 实现加载更多的函数

    // 上拉加载更多
    _fetchMoreData = () => {
      // 没有更多的数据 || 正在加载
      if (!this._hasMore() || this.state.isLoading) {
         return
      }
      // 去服务器加载更多数据
      this._fetchData(cachedResults.nextPage)
    };
    // 是否还有更多的数据
    _hasMore() {
      return cachedResults.items.length < cachedResults.total
    }
    

    d). 数据回来之后逻辑处理

      // 修改状态机
      this.setState({
        isLoadingMore:true // 正在加载更多
      });
     ...
        if (data.success) {
              // 将服务器得到的数据缓存
              cachedResults.items = cachedResults.items.concat(data.data);
              cachedResults.total = data.total;
              cachedResults.nextPage += 1;
              this.setState({
                // 更新数据
                dataSource:this.state.dataSource.cloneWithRows(cachedResults.items),
                // 还原状态
                isLoadingMore:false,
              })
            }
     ...
    

    e). 添加底部加载动画
    引入 ActivityIndicator 组件;
    ListView 中加入 renderFooter={this._renderFooter}
    实现 _renderFooter函数;

    // 自定义Footer视图
    _renderFooter = ()=> {
      if(!this._hasMore()) {
        return(
          <View style={styles.loadingMore}>
            <Text style={styles.loadingText}>没有更多的数据了...</Text>
          </View>
        )
      }
      // 显示小菊花
      return(
        <ActivityIndicator style={styles.loadingMore}/>
      )
    }
    
    (5)添加下拉刷新逻辑:

    a). 在构造函数中给状态机添加参数 isRefreshing:false 记录是否正在下拉刷新;
    b). 加入下拉刷新组件 refreshControl

    在ListView中实现函数:
    ...
    // 下拉刷新
    refreshControl={
      <RefreshControl
        refreshing={this.state.isRefreshing}
        onRefresh={this._onRefresh}
      />
    }
    

    c). 实现下拉刷新的函数

    // 下拉刷新
    _onRefresh = ()=> {
      if (!this._hasMore() || this.state.isRefreshing){
        return
      }
      this._fetchData(1);
    };
    

    d). 数据回来之后逻辑处理(和上拉加载更多整合在了一起)

    // 修改状态机
    if (page === 1) {
      this.setState({
        isRefreshing:true  // 刷新
      })
    } else {
      this.setState({
        isLoadingMore:true // 正在加载更多
      })
    }
    ...
    // 将服务器得到的数据缓存
    // 拷贝对象生成一个新数组
    let items = cachedResults.items.slice(0);
    if (page === 1) {// 刷新
      items = data.data.concat(items);
      cachedResults.nextPage = 1;
    } else {
      items = items.concat(data.data);
      cachedResults.nextPage += 1;
    }
    
    cachedResults.items = items;
    cachedResults.total = data.total;
    if (page === 1) {// 刷新
      this.setState({
        // 更新数据
        dataSource:this.state.dataSource.cloneWithRows(cachedResults.items),
        // 还原状态
        isRefreshing:false
      })
    } else {
      this.setState({
        // 更新数据
        dataSource:this.state.dataSource.cloneWithRows(cachedResults.items),
        // 还原状态
        isLoadingMore:false
      })
    }
    ...
    

    效果如下:

    (6) item上面的逻辑处理:

    上面的功能实现完之后,整个 list.js 文件是相当冗余的。把 listView 里面渲染cell的模块单独抽取出来为 listItem.js,详情请看我代码中的该文件。

    a). 点赞功能的实现:

    • 在rap上面定义点赞接口 api/up
    • 添加接口到 config.js中:
    api:{
      baseUrl:'http://rapapi.org/mockjs/31504/',  // base
      list:'api/list',                            // 列表
      up:'api/up',                                // 点赞
    },
    
    • listItem.js 文件中给点赞的图标Icon和文字添加 onPress 事件,实现 _up 函数,根据样式的改变修改对应的style和图片的name属性;具体实现细节请查看代码。
      效果如下:
    (7)点击item进入视频详情页

    a). 之前的页面是没有加导航的。现在在 App.js 文件中加入 Navigator,实现导航的逻辑(根视图为 List 组件):

    <Navigator
      tabLabel="list"
      initialRoute={{component:List,name:'list',
        params:{
          title:'视频列表'
        }}}
      renderScene={
         (route, navigator) =>
           <route.component {...route.params} navigator={navigator} />
      }
      configureScene={
        (route, routeStack) =>
          Navigator.SceneConfigs.FloatFromRight
      }
    />
    

    b). 创建 detail.js 处理详情逻辑。
    c). 添加push到 Detail 组件(即 detail.js)的逻辑:

    • list.js 文件中给封装的组件添加属性
    <ListItem rowData={rowData} 
              onSelect={()=>this._loadPage(rowData)}
    />
    ...
    // 实现函数:
    // 通过导航控制器跳转到详情页
    _loadPage(rowData) {
       /*
         因为当前的List组件是在Navigator包裹下的,
         所以可以通过this.props.navigator取到导航控制器进行push
      */
      let {navigator} = this.props;
      if (navigator) {
        navigator.push({
          name:'detail',
          component:Detail
        })
      }
    }
    
    • listItem 中给item绑定 onPress 事件
    <TouchableOpacity onPress={this.props.onSelect}>
    

    效果如下:


    (8) 处理视频播放功能

    a). 引用第三方视频播放的库video,执行终端命令:

    $ npm i -S react-native-video
    $ react-native link
    

    b). 然后从新编译项目;
    c). 在 detail.js 中引入 'react-native-video' 组件,实现视频播放的功能

    // 导入播放器
    import Video from 'react-native-video';
    ...
    // 这里Video有一些参数设置
        this.state = {
          // 服务端返回的数据
          rowData:this.props.rowData,
          // 播放速度
          rate: 1,
          // 音量
          volume: 1,
          // 是否静音
          muted: false,
          // 展示模式: 'cover', 'contain', 'stretch'
          resizeMode: 'contain',
          // 是否暂停
          paused: false,
          // 处理视频进度
          duration: 0.0,
          currentTime: 0.0,
        }
    ... 
    

    具体实现细节请查看源码。

    • 给视频添加简单的加载动画:
      导入 ActivityIndicator 组件,
    // 在状态机中定义一个字段,标注是否加载完成视频资源
     videoLoaded:false
    
    ...
    
    // 根据状态在render()中确定需要绘制的内容
    {/* 视频加载动画 */}
    {!this.state.videoLoaded ?
      <ActivityIndicator
        color={'red'}
        size={'large'}
        style={styles.videoLoad}
      />
      : null
    }
    
    ... 
    
    // 当视频加载完之后修改状态机中字段的状态
    _onProgress = (data)=> {
      if (!this.state.videoLoaded) {
        this.setState({
          videoLoaded:true
        })
      }
    };
    
    • 给视频添加播放进度条:
    // 设置视频播放进度条(一个是当前时间,一个是剩余时间)
    const flexCompleted = this.getCurrentTimePercentage() * 100;
    const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
    
    ...
    
    {/* 视频播放进度条 */}
    <View style={styles.progress}>
      <View style={[styles.innerProgressCompleted, {flex: flexCompleted}]} />
      <View style={[styles.innerProgressRemaining, {flex: flexRemaining}]} />
    </View>
    
    ...
    
    // 计算视频播放进度
    getCurrentTimePercentage() {
      if (this.state.currentTime > 0) {
        return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
      } else {
        return 0;
      }
    }
    

    效果如下:


    • 给视频添加重新播放、暂停/继续的功能:
      逻辑和上面如出一辙,不累赘了,具体查看源码。
    (9) 处理视频信息的展示

    a). 给视频详情页添加了导航

    {/* 导航栏 */}
    <View style={styles.nav_navStyle}>
      {/* 返回按钮 */}
      <TouchableOpacity
        style={styles.nav_backBox}
        onPress={this._pop}
      >
        <Icon name={'ios-arrow-back'}
              style={styles.nav_backIcon}
        />
        <Text style={styles.nav_backText}>返回</Text>
      </TouchableOpacity>
      {/* 标题 */}
      <Text style={styles.nav_navText} numberOfLines={1}>视频详情页面</Text>
    </View>
    

    b). 给视频详情页添加作者信息

    • 我在之前的接口中又加入了几个字段来表示作者信息
    • 因为评论要以列表的形式展示出来,视频作者信息是要能跟着列表一起滚动的,所以把该模块放在列表的Header上,作者信息代码实现(css请查看源码):
    // 自定义Header视图
    _renderHeader = ()=> {
      let rowData = this.state.rowData;
      return (
        // 视频作者信息
        <View style={styles.info_infoBox}
        >
          <Image
            style={styles.info_avatar}
            source={{uri:rowData.author.avatar}}
          />
          <View style={styles.info_descBox}>
            <Text style={styles.info_nickname}>作者:{rowData.author.nickname}</Text>
            <Text style={styles.info_title}>标题:{rowData.title}</Text>
          </View>
        </View>
      )
    };
    
    (10) 处理评论信息的展示

    a). 在视频信息 render 函数内部加入评论信息的 ListView 组件(实现了加载更多功能,此处不再说明,同list.js 中的加载更多一样)

    {/* 评论信息 */}
    <ListView
      dataSource={this.state.dataSource}
      renderRow={this._renderRow}
      enableEmptySections={true}
      automaticallyAdjustContentInsets={false}
      renderHeader={this._renderHeader}
      onEndReached={this._fetchMoreData}
      onEndReachedThreshold={20}
      // 上拉加载更多底部动画
      renderFooter={this._renderFooter}
    />
    
    b). 创建评论列表的接口:

    c). 在 componentDidMount 生命周期函数中发送请求,绘制item.
    效果如下:





    (因为最近比较忙,等后续有时间了接着完成剩余的模块。。。)
    项目放在GitHub上了,→下载地址戳这里。

    相关文章

      网友评论

          本文标题:使用React Native写一个Demo

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