一、描述

ScrollView 是一个包装了滚动操作的组件。一般情况下需要一个确定的高度来保证 ScrollView 的正常展现。

上面是官方文档的介绍,实际在使用的时候,往往借助 ScrollView 来实现横向滚动或者是轮播图效果,列表的纵向滚动一般不会直接使用 ScrollView。

文档地址:

大量数据的列表滚动,还是使用 ListView 或者是自己定制 RecyclerView

二、源码简析

Rax 毕竟设计上还是 weex 的 DSL,所以在本质的渲染上 ScrollView 在 weex 容器环境下,还是渲染了 weex 的 scroller 组件

1、prop 设计

在设计上,props 主要是下面这几个:

  • scrollEventThrottle
  • horizontal
  • showsHorizontalScrollIndicator
  • showsVerticalScrollIndicator
  • onEndReachedThreshold
  • onEndReached
  • onScroll

默认的 prop 如下:

  static defaultProps = {
    scrollEventThrottle: DEFAULT_SCROLL_CALLBACK_THROTTLE,
    onEndReachedThreshold: DEFAULT_END_REACHED_THRESHOLD,
    showsHorizontalScrollIndicator: true,
    showsVerticalScrollIndicator: true,
    className: 'rax-scrollview',
  };

2、内容渲染设计

在 weex 容器环境下和 web 环境下渲染的内容是不同的,如果在 weex 环境下,return <scroller>

  if (isWeex) {
      return (
        <scroller
          {...this.props}
          style={scrollerStyle}
          showScrollbar={showsScrollIndicator}
          onLoadmore={onEndReached}
          onScroll={onScroll ? this.handleScroll : null}
          loadmoreoffset={onEndReachedThreshold}
          loadmoreretry={this.state.loadmoreretry}
          scrollDirection={this.props.horizontal ? 'horizontal' : 'vertical'}
        >
          {refreshContainer}
          {contentContainer}
        </scroller>
      );
    }

而在 web 环境中,直接通过 <View> 组件进行渲染,(View 组件本质上还是 div),web 上渲染主要是通过 ::-webkit-scrollbar 这种来控制诸如滚动条等内容。

没有仔细研究过,这种设计的机制是什么,因为 scroller 在 web 端表现也挺不错的。

3、scroll 事件处理 handleScroll

如果是 web 环境,实际上是借助 x: e.target.scrollLeft / y: e.target.scrollTop 实现,与此相关的属性还有 scrollWidthscrollHeight 等。

由于 在 iOS7 / 8 中,offsetHeight / Width 是不准确的,因此 web 环境下使用 scrollHeight / Width

而在 weex 环境下,直接使用 <scroller>contentSizecontentOffset 来辅助使用,如下:

    if (isWeex) {
      e.nativeEvent = {
        contentOffset: {
          // HACK: weex scroll event value is opposite of web
          x: -e.contentOffset.x,
          y: -e.contentOffset.y
        },
        contentSize: e.contentSize ? {
          width: e.contentSize.width,
          height: e.contentSize.height
        } : null
      };
      this.props.onScroll(e);
    }

4、 scrollTo 事件处理

weex 环境下的 scrollTo 借助的是 weex 的 dom 模块。

dom 自带方法就是 scrollToElement,通过 findDOMNode 进行节点查找 ,关于 dom 模块我之前也有写过文章,weex 的环境的 scrollTo 实现如下:(不是很建议这种引入方式)

    if (isWeex) {
      let dom = __weex_require__('@weex-module/dom');
      let contentContainer = findDOMNode(this.refs.contentContainer);
      dom.scrollToElement(contentContainer.ref, {
        offset: x || y || 0,
        animated
      });
    }

web 环境下的滚动,使用的是 rax-scrollview 目录下的 timer.js ,能够发现 new Timer({}) 这样的代码。

三、使用

主要是需要一个 render 方法来渲染列表,如下:

  renderList = () => {
    const list = [];
    for(let i=0; i< 10;i++){
      list.push(
        <View style={styles.listItem}>
          <Text>{i}</Text>
        </View>
      );
    }
    return list;
  }
  pressHandle = () => {
      this.refs.scrollView.scrollTo({x:0});
  }

效果:

GIF.gif

完整组件代码:

import {createElement, Component} from 'rax';
import View from 'rax-view';
import Text from 'rax-text';
import ScrollView from 'rax-scrollview';
import Touchable from 'rax-touchable';

import styles from './App.css';

class App extends Component {

  renderList = () => {
    const list = [];
    for(let i=0; i< 10;i++){
      list.push(
        <View style={styles.listItem}>
          <Text>{i}</Text>
        </View>
      );
    }
    return list;
  }
  pressHandle = () => {
      this.refs.scrollView.scrollTo({x:0});
  }

  render() {
    return (
      <View style={styles.app}>
          <View style={styles.scrollviewWrapper}>
            <ScrollView 
              ref="scrollView" 
              style={styles.scrollview}
              horizontal={true} 
              showsHorizontalScrollIndicator={false}
              showsVerticalScrollIndicator={false} 
            >
              {this.renderList()}
            </ScrollView>
          </View>
          <Touchable onPress={this.pressHandle} >返回起点</Touchable>
      </View>
    );
  }
}

export default App;