如何在React Native中获取contentOffset prop的ScrollView组件的内容高度?

时间:2016-06-20 08:24:41

标签: ios react-native

对于我当前的项目,我创建了一个日记/日历类型组件,显示当用户点击查看该视图时居中的当前日期(日记视图)或当前月份(日历视图)。

我使用ScrollView来保存我的内容:

_getInitialOffset(h) {
  const FIX = 75; //TODO: Make it less arbitrary
  let percentToScroll = (this.props.viewedDate.month()+1)/12; //12 = number of months per year
  let { height } = this.props;
  let centerFix = height/2;
  let contentHeight = h;
  let yPos = contentHeight*percentToScroll - centerFix - FIX;
  return (yPos > 0) ? yPos : 0;
}



render() {
  var year = this.props.viewedDate.year();
  var cal = Calendar.get(year);
  var contentHeight = this.contentHeight;
  return (
    <View>
      <View style={styles.daysOfWeek}>
        {
          Calendar.days().map(function(day, i) {
            return <Text key={i} style={styles.dayHeader}>{day}</Text>;
          })
        }
      </View>
      <ScrollView
        ref={ref => {this._scrollView = ref}}
        style={{
          height: this.props.height,
          paddingTop: 15
        }}
        onContentSizeChange={(w, h) => {
          this._scrollView.scrollTo({y: this._getInitialOffset(h), animated: false});
        }}>
        <Year year={year} calendar={cal}/>
      </ScrollView>
    </View>
  );
}

我试图让它在当前月份的渲染中居中,但因为我的当前方法(使用OnContentSizeChange)在渲染之后发生,所以有一个框架,用户看到它未被渲染,这是不好的用户经验。

有没有办法在渲染之前/期间获取ScrollView组件的内容高度,或者在onContentSizeChange方法触发之后延迟组件可见性?

2 个答案:

答案 0 :(得分:3)

onContentSizeChange在内部使用onLayout,一旦计算出布局就会触发。

  

计算布局后会立即触发此事件,但在收到事件时,新布局可能尚未反映在屏幕上,尤其是在布局动画正在进行中时。

在此之前无法获得尺寸。 因此,您可以执行的操作是将滚动视图的opacity设置为0,直到触发第一个onContentSizeChange

我建议您使用Animated.Value进行不透明度更改,以便它不会重新渲染整个组件。

答案 1 :(得分:0)

我的解决方法如下:

  1. 创建一个父容器,并使用scrollOffset动画跟踪滚动量。
  2. 获取ScrollView容器的高度(“ containerHeight”)
  3. 获取ScrollView中内容的高度(“ contentHeight”)
  4. 使用ScrollView的“ onScroll”事件获取滚动偏移量。使用它来更新scrollOffset动画。
  5. 基于scrollOffset动画值,containerHeight和contentHeight创建ScrollBar组件

以下内容摘自我的代码。它是不完整且未经测试的,但应该足以使它开始使用。

class CustomScrollView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      scrollOffsetAnim: new Animated.Value(0)
    };
  }

  renderScrollBar() {
    const {
      scrollOffsetAnim,
      contentHeight,
      containerHeight,
    } = this.state;

    //...
  }

  render() {
    return (
      <View>
          <ScrollView
              scrollEventThrottle={16}
              onScroll={(evt) => {
                  const {contentOffset} = evt.nativeEvent;
                  const {y} = contentOffset;
                  this.state.scrollOffsetAnim.setValue(y);
              }}
              onLayout={(evt) => {
                  const {height} = evt.nativeEvent.layout;
                  const {containerHeight} = this.state;
                  if(!containerHeight || containerHeight !== height) {
                      this.setState({containerHeight: height})
                  }
              }}
          >
              <View 
                  onLayout={(evt) => {
                      const {height} = evt.nativeEvent.layout;
                      const {contentHeight} = this.state;
                      if(!contentHeight || contentHeight !== height) {
                          this.setState({contentHeight: height})
                      }
                  }}
              >
                  {this.props.children}
              </View>
          </ScrollView>
          {this.renderScrollBar()}
      </View>
    );
  }
}