ScrollView

一个包装了平台的ScrollView(滚动视图)的组件,同时还集成了触摸锁定的“响应者”系统。

记住ScrollView必须有一个确定的高度才能正常工作,因为它实际上所做的就是将一系列不确定高度的子组件装进一个确定高度的容器(通过滚动操作)。要给一个ScrollView确定一个高度的话,要么直接给它设置高度(不建议),要么确定所有的父容器都已经绑定了高度。在视图栈的任意一个位置忘记使用{flex:1}都会导致错误,你可以使用元素查看器来查找问题的原因。

ScrollView内部的其他响应者尚无法阻止ScrollView本身成为响应者。

ScrollViewListView/FlatList应该如何选择?ScrollView会简单粗暴地把所有子元素一次性全部渲染出来。其原理浅显易懂,使用上自然也最简单。然而这样简单的渲染逻辑自然带来了性能上的不足。想象一下你有一个特别长的列表需要显示,可能有好几屏的高度。创建和渲染那些屏幕以外的JS组件和原生视图,显然对于渲染性能和内存占用都是一种极大的拖累和浪费。

这就是为什么我们还有专门的ListView组件。ListView会惰性渲染子元素,只在它们将要出现在屏幕中时开始渲染。这种惰性渲染逻辑要复杂很多,因而API在使用上也更为繁琐。除非你要渲染的数据特别少,否则你都应该尽量使用ListView,哪怕它们用起来更麻烦。

FlatList是0.43版本开始新出的改进版的ListView,性能更优,但可能不够稳定,尚待时间考验。

属性#

contentContainerStyle StyleSheetPropType(ViewStylePropTypes) #

这些样式会应用到一个内层的内容容器上,所有的子视图都会包裹在内容容器内。例子:

return (
    <ScrollView contentContainerStyle={styles.contentContainer}>
    </ScrollView>
  );
  ...
  var styles = StyleSheet.create({
    contentContainer: {
      paddingVertical: 20
    }
  });

horizontal bool #

当此属性为true的时候,所有的子视图会在水平方向上排成一行,而不是默认的在垂直方向上排成一列。默认值为false。

keyboardDismissMode enum('none', "interactive", 'on-drag') #

用户拖拽滚动视图的时候,是否要隐藏软键盘。

  • none(默认值),拖拽时不隐藏软键盘。

  • on-drag 当拖拽开始的时候隐藏软键盘。

  • interactive 软键盘伴随拖拽操作同步地消失,并且如果往上滑动会恢复键盘。安卓设备上不支持这个选项,会表现的和none一样。

keyboardShouldPersistTaps enum('always', 'never', 'handled', false, true) #

如果当前界面有软键盘,那么点击scrollview后是否收起键盘,取决于本属性的设置。(译注:很多人反应TextInput无法自动失去焦点/需要点击多次切换到其他组件等等问题,其关键都是需要将TextInput放到ScrollView中再设置本属性)

  • 'never'(默认值),点击TextInput以外的子组件会使当前的软键盘收起。此时子元素不会收到点击事件。
  • 'always',键盘不会自动收起,ScrollView也不会捕捉点击事件,但子组件可以捕获。
  • 'handled',当点击事件被子组件捕获时,键盘不会自动收起。这样切换TextInput时键盘可以保持状态。多数带有TextInput的情况下你应该选择此项。
  • false,已过期,请使用'never'代替。
  • true,已过期,请使用'always'代替。

onContentSizeChange function #

此函数会在ScrollView内部可滚动内容的视图发生变化时调用。

调用参数为内容视图的宽和高: (contentWidth, contentHeight)

此方法是通过绑定在内容容器上的onLayout来实现的。

onMomentumScrollStart?: function #

滚动动画开始时调用此函数。

onMomentumScrollEnd?: function #

滚动动画结束时调用此函数。

onScroll function #

在滚动的过程中,每帧最多调用一次此回调函数。调用的频率可以用scrollEventThrottle属性来控制。

refreshControl element #

指定RefreshControl组件,用于为ScrollView提供下拉刷新功能。

removeClippedSubviews bool #

(实验特性):当此属性为true时,屏幕之外的子视图(子视图的overflow样式需要设为hidden)会被移除。这个可以提升大列表的滚动性能。默认值为true。

showsHorizontalScrollIndicator bool #

当此属性为true的时候,显示一个水平方向的滚动条。

showsVerticalScrollIndicator bool #

当此属性为true的时候,显示一个垂直方向的滚动条。

style style #

backfaceVisibility enum('visible', 'hidden')
backgroundColor string
borderColor string
borderTopColor string
borderRightColor string
borderBottomColor string
borderLeftColor string
borderRadius number
borderTopLeftRadius number
borderTopRightRadius number
borderBottomLeftRadius number
borderBottomRightRadius number
borderStyle enum('solid', 'dotted', 'dashed')
borderWidth number
borderTopWidth number
borderRightWidth number
borderBottomWidth number
borderLeftWidth number
opacity number
overflow enum('visible', 'hidden')
shadowColor string
shadowOffset {width: number, height: number}
shadowOpacity number
shadowRadius number

androidendFillColor color #

有时候滚动视图会占据比实际内容更多的空间。这种情况下可以使用此属性,指定以某种颜色来填充多余的空间,以避免设置背景和创建不必要的绘制开销。一般情况下并不需要这种高级优化技巧。

androidoverScrollMode enum('auto', 'always', 'never') #

覆盖默认的overScroll模式

可选的值有:

  • 'auto' - 默认值,允许用户在内容超出视图高度之后可以滚动视图。
  • 'always' - 无论内容尺寸,用户始终可以滚动视图。
  • 'never' - 始终不允许用户滚动视图。

androidscrollPerfTag string #

Tag used to log scroll performance on this scroll view. Will force momentum events to be turned on (see sendMomentumEvents). This doesn't do anything out of the box and you need to implement a custom native FpsListener for it to be useful.

iosalwaysBounceHorizontal bool #

当此属性为true时,水平方向即使内容比滚动视图本身还要小,也可以弹性地拉动一截。当horizontal={true}时默认值为true,否则为false。

iosalwaysBounceVertical bool #

当此属性为true时,垂直方向即使内容比滚动视图本身还要小,也可以弹性地拉动一截。当horizontal={true}时默认值为false,否则为true。

iosautomaticallyAdjustContentInsets bool #

当滚动视图放在一个导航条或者工具条后面的时候,iOS系统是否要自动调整内容的范围。默认值为true。(译注:如果你的ScrollView或ListView的头部出现莫名其妙的空白,尝试将此属性置为false)

iosbounces bool #

当值为true时,如果内容范围比滚动视图本身大,在到达内容末尾的时候,可以弹性地拉动一截。如果为false,尾部的所有弹性都会被禁用,即使alwaysBounce属性为true。默认值为true。

iosbouncesZoom bool #

当值为true时,使用手势缩放内容可以超过min/max的限制,然后在手指抬起之后弹回min/max的缩放比例。否则的话,缩放不能超过限制。

ioscanCancelContentTouches bool #

当值为false时,一旦有子节点响应触摸操作,即使手指开始移动也不会拖动滚动视图。默认值为true(在以上情况下可以拖动滚动视图。)

ioscenterContent bool #

当值为true时,如果滚动视图的内容比视图本身小,则会自动把内容居中放置。当内容比滚动视图大的时候,此属性没有作用。默认值为false。

ioscontentInset {top: number, left: number, bottom: number, right: number} #

内容范围相对滚动视图边缘的坐标。默认为{0, 0, 0, 0}

ioscontentOffset PointPropType #

用来手动设置初始的滚动坐标。默认值为{x: 0, y: 0}

iosdecelerationRate number #

一个浮点数,用于决定当用户抬起手指之后,滚动视图减速停下的速度。常见的选项有:

  • Normal: 0.998 (默认值)

  • Fast: 0.9

iosdirectionalLockEnabled bool #

当值为真时,滚动视图在拖拽的时候会锁定只有垂直或水平方向可以滚动。默认值为false。

iosindicatorStyle enum('default', 'black', 'white') #

设置滚动条的样式。

  • default,默认值,等同black.
  • black,黑色滚动条。
  • white,白色滚动条。

iosmaximumZoomScale number #

允许的最大缩放比例。默认值为1.0。

iosminimumZoomScale number #

允许的最小缩放比例。默认值为1.0。

iosonRefreshStart function #

已过期

请使用refreshControl 属性代替。

pagingEnabled bool #

当值为true时,滚动条会停在滚动视图的尺寸的整数倍位置。这个可以用在水平分页上。默认值为false。

scrollEnabled bool #

当值为false的时候,内容不能滚动,默认值为true。

iosscrollEventThrottle number #

这个属性控制在滚动过程中,scroll事件被调用的频率(单位是每秒事件数量)。更大的数值能够更及时的跟踪滚动位置,不过可能会带来性能问题,因为更多的信息会通过bridge传递。默认值为0,意味着每次视图被滚动,scroll事件只会被调用一次。

iosscrollIndicatorInsets {top: number, left: number, bottom: number, right: number} #

决定滚动条距离视图边缘的坐标。这个值应该和contentInset一样。默认值为{0, 0, 0, 0}

iosscrollsToTop bool #

当此值为true时,点击状态栏的时候视图会滚动到顶部。默认值为true。

iossnapToAlignment enum('start', "center", 'end') #

当设置了snapToIntervalsnapToAlignment会定义停驻点与滚动视图之间的关系。

  • start (默认) 会将停驻点对齐在左侧(水平)或顶部(垂直)

  • center 会将停驻点对齐到中间

  • end 会将停驻点对齐到右侧(水平)或底部(垂直)

iossnapToInterval number #

当设置了此属性时,会让滚动视图滚动停止后,停止在snapToInterval的倍数的位置。这可以在一些子视图比滚动视图本身小的时候用于实现分页显示。与snapToAlignment组合使用。

stickyHeaderIndices [number] #

一个子视图下标的数组,用于决定哪些成员会在滚动之后固定在屏幕顶端。举个例子,传递stickyHeaderIndices={[0]}会让第一个成员固定在滚动视图顶端。这个属性不能和horizontal={true}一起使用。

ioszoomScale number #

滚动视图内容初始的缩放比例。默认值为1.0。

方法#

scrollTo(y: number | { x?: number, y?: number, animated?: boolean }, x: number, animated: boolean) #

滚动到指定的x, y偏移处。第三个参数为是否启用平滑滚动动画。

使用示例:

scrollTo({x: 0, y: 0, animated: true})

scrollToEnd(options?) #

滚动到视图底部(水平方向的视图则滚动到最右边)。

加上动画参数 scrollToEnd({animated: true})则启用平滑滚动动画,或是调用 scrollToEnd({animated: false})来立即跳转。如果不使用参数,则animated选项默认启用。

例子#

'use strict';

var React = require('react');
var ReactNative = require('react-native');
var {
  ScrollView,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
  Image
} = ReactNative;

exports.displayName = (undefined: ?string);
exports.title = '<ScrollView>';
exports.description = 'Component that enables scrolling through child components';
exports.examples = [
{
  title: '<ScrollView>',
  description: 'To make content scrollable, wrap it within a <ScrollView> component',
  render: function() {
    var _scrollView: ScrollView;
    return (
      <View>
        <ScrollView
          ref={(scrollView) => { _scrollView = scrollView; }}
          automaticallyAdjustContentInsets={false}
          onScroll={() => { console.log('onScroll!'); }}
          scrollEventThrottle={200}
          style={styles.scrollView}>
          {THUMBS.map(createThumbRow)}
        </ScrollView>
        <TouchableOpacity
          style={styles.button}
          onPress={() => { _scrollView.scrollTo({y: 0}); }}>
          <Text>Scroll to top</Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.button}
          onPress={() => { _scrollView.scrollToEnd({animated: true}); }}>
          <Text>Scroll to bottom</Text>
        </TouchableOpacity>
      </View>
    );
  }
}, {
  title: '<ScrollView> (horizontal = true)',
  description: 'You can display <ScrollView>\'s child components horizontally rather than vertically',
  render: function() {
    var _scrollView: ScrollView;
    return (
      <View>
        <ScrollView
          ref={(scrollView) => { _scrollView = scrollView; }}
          automaticallyAdjustContentInsets={false}
          horizontal={true}
          style={[styles.scrollView, styles.horizontalScrollView]}>
          {THUMBS.map(createThumbRow)}
        </ScrollView>
        <TouchableOpacity
          style={styles.button}
          onPress={() => { _scrollView.scrollTo({x: 0}); }}>
          <Text>Scroll to start</Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.button}
          onPress={() => { _scrollView.scrollToEnd({animated: true}); }}>
          <Text>Scroll to end</Text>
        </TouchableOpacity>
      </View>
    );
  }
}];

class Thumb extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return false;
  }

  render() {
    return (
      <View style={styles.button}>
        <Image style={styles.img} source={{uri:this.props.uri}} />
      </View>
    );
  }
}

var THUMBS = ['https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851549_767334479959628_274486868_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851561_767334496626293_1958532586_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851579_767334503292959_179092627_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851589_767334513292958_1747022277_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851563_767334559959620_1193692107_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851593_767334566626286_1953955109_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851591_767334523292957_797560749_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851567_767334529959623_843148472_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851548_767334489959627_794462220_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851575_767334539959622_441598241_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-ash3/t39.1997/p128x128/851573_767334549959621_534583464_n.png', 'https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-prn1/t39.1997/p128x128/851583_767334573292952_1519550680_n.png'];
THUMBS = THUMBS.concat(THUMBS); // double length of THUMBS
var createThumbRow = (uri, i) => <Thumb key={i} uri={uri} />;

var styles = StyleSheet.create({
  scrollView: {
    backgroundColor: '#6A85B1',
    height: 300,
  },
  horizontalScrollView: {
    height: 120,
  },
  containerPage: {
    height: 50,
    width: 50,
    backgroundColor: '#527FE4',
    padding: 5,
  },
  text: {
    fontSize: 20,
    color: '#888888',
    left: 80,
    top: 20,
    height: 40,
  },
  button: {
    margin: 7,
    padding: 5,
    alignItems: 'center',
    backgroundColor: '#eaeaea',
    borderRadius: 3,
  },
  buttonContents: {
    flexDirection: 'row',
    width: 64,
    height: 64,
  },
  img: {
    width: 64,
    height: 64,
  }
});