一、描述

rax-listview 是 rax 提供的内置组件,官方文档地址:

rax-listview 是基于 RecyclerView 实现的,而 RecyclerView 实现了列表元素的回收和重复利用,因此性能比较高。

有两个 prop 比较关键,分别是通过 dataSource 来定义每行渲染的数据,通过 renderRow 来定义每行渲染的模板。

rax-listview 本身是针对比较特定的 list 场景的,如果需要更灵活的列表视图,可以基于 RecyclerView 定制(虽然我觉得 rax 中很多东西都需要重新定制)

具体的使用参数不再重复,看文档即可。

二、 源码

rax-listview 源码主要引入了三个依赖(不包括 rax 本身引入):

  • import {isWeex} from 'universal-env';
  • import View from 'rax-view';
  • import RecyclerView from 'rax-recyclerview';

本质上就是渲染了一个 <RecyclerView {...props}>,而其常用的 prop 主要如下:

  renderHeader={() => {
    return <span />;
  }}
  renderFooter={() => {
    return <span />;
  }}
  renderRow={(num) => {
    return <span>{num}</span>;
  }}
  dataSource={[1, 2, 3]}
  onEndReached={() => {}}

有一点是我没搞明白的,<RecyclerView> 是有 <RecyclerView.Cell><RecyclerView.Header> 下属子组件的(会单独研究一下 RecyclerView 源代码),但是我在 ListView 上没有发现 HeaderCell

传入给 RecyclerView 的 prop 是使用下面的方式生成的:

    let {
      renderScrollComponent,
      renderHeader,
      renderFooter,
      renderRow,
      dataSource,
    } = this.props;

    let header = typeof renderHeader == 'function' ? renderHeader() : null;
    let footer = typeof renderFooter == 'function' ? renderFooter() : null;
    let body = dataSource.map((i, index) => {
      return renderRow(i, index);
    });

    let props = {
      ...this.props,
      ...{
        ref: SCROLLVIEW_REF,
        children: [].concat(header, body, footer),
        _autoWrapCell: true,
      },
    };
 return renderScrollComponent(props);

renderScrollComponent 方法中将 props 传入了真正的 RecyclerView:

  static defaultProps = {
    renderScrollComponent: props => <RecyclerView {...props} />,
    dataSource: [],
  };

三、使用

ListView 源码中给的测试文件是如下使用方式:

render() {
    return (
      <ListView
        ref="scrollview"
        renderHeader={() => {
          return <span />;
        }}
        renderFooter={() => {
          return <span />;
        }}
        renderRow={(num) => {
          return <span>{num}</span>;
        }}
        dataSource={[1, 2, 3]}
        onEndReached={() => {}}
      />
    );
  }

如果自己要去使用 ListView 渲染数据,则首先关键的几部分的内容渲染需要做好。

1、renderHeader

  renderHeader = () => {
    return (
      <View style={styles.headerTtitle}>
        <Text style={styles.headerText}>Header</Text>
      </View>
    );
  }

2、renderRow

renderRow 承载了单个 List 内容的渲染,实际上是一个渲染回调,传入 item 和 当前的 index,最终每个列表单项就是渲染出来的内容。

  renderRow = (item,index) => {
    return (
      <View style={styles.item}>
        <Text style={styles.text}>{item.name}</Text>
      </View>
    )
  }

 3、renderFooter

  renderFooter = () => {
    return (
      <View style={styles.header}>
        <Text style={styles.text}>Footer</Text>
      </View>
    )
  }

上面三部分都有了之后,就可以直接传入 <ListView> 的 props 了

4、传入 ListView

<ListView
        style={styles.list}
        renderHeader={this.renderHeader}
        renderFooter={this.renderFooter}
        renderRow={this.renderRow}
        onEndReached = {this.onEndReached}
        dataSource={this.state.data}
      />

5、效果

1111.jpg

6、onEndReached 方法实现无限滚动

上面对 ListView 的使用中, onEndReached 事件是当组件滚动到底部的时候自动触发,因此在业务中,往往使用 onEndReached 来加载更多的数据。

下面的 onEndReached 方法能够实现无限加载(在 weex 容器中运行,跑 jsbundle,所以性能还不错):

  onEndReached = () => {
    console.log(1);
    const list = [];
    for(let i=0; i < 10; i++){
        list.push({name:`newname-${i}`});
    }
    this.setState({
      data:this.state.data.concat(list)
    });
  }

7、onScroll 方法监听每次滚动事件

  onScrollHandle = (event) => {
    console.log(event);
  }

event 参数的基本内容如下:

1.jpg

8、scrollTo 滚动到某个 item

如果需要滚动到列表中的某个位置,则可以使用 scrollTo 方法。

<ListView> 组件需要指定 ref,如 ref="listView"scrollTo 方法的参数是一个 Object,形如 {x:0,y:0} 分别控制水平和垂直方向的滚动,如果返回顶部直接使用 scrollTo({y:0})

  pressHandle = () => {
      console.log(this.refs.listView);
      this.refs.listView.scrollTo({y:0});
  }

gif 动画存在卡顿:

GIF.gif