Реагировать на родной Android ScrollView scrollTo не работает

Я пытаюсь использовать горизонтальный ScrollView в React Native для Android, где начальная позиция находится в середине прокручиваемых изображений, а не (0,0).

Метод scrollTo, по-видимому, правильно называется внутри componentDidMount, но в приложении ничего не движется, он все еще показывает, как начать прокрутку полностью влево.

Поскольку это Android, у меня нет доступа к реквизитам contentOffset, или я бы установил это напрямую, согласно документации. Вот код:

'use strict';

var React = require('react-native');
var {
  StyleSheet,
  View,
  Text,
  ScrollView,
  Component,
} = React;
var precomputeStyle = require('precomputeStyle');

class Carousel extends Component {
  constructor(props, context) {
    super(props, context);
    //this.changeContent = this.changeContent.bind(this);
  }

  componentDidMount() {
    this.myScroll.scrollTo(100);
    console.log("called DidMount");
  }

  render() {
    return (
      <View style={{flex: 1}}>
        <ScrollView ref={(ref) => this.myScroll = ref}
          contentContainerStyle={styles.container}
          horizontal={true}
          pagingEnabled={true}
          showsHorizontalScrollIndicator={false}
          bounces={true}
          onMomentumScrollEnd={this.onAnimationEnd}
        >
          {this.props.children}
        </ScrollView>
      </View>
    );
  }

  onAnimationEnd(e) {
    console.log("curr offset: " + e.nativeEvent.contentOffset.x);
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  page: {
    alignItems: 'center',
    justifyContent: 'center',
    borderWidth: 1,
  },
});

module.exports = Carousel;

Ответ 1

У меня была та же проблема, и я потратил несколько часов впустую.

  • 1: в android ScrollView можно прокручивать только тогда, когда его размер <размер контента

  • 2: в реагировать на собственный Android, если вы вызываете ScrollView.scrollTo() в componentDidMount, он не будет работать, потому что ScrollView имеет анимацию макета при создании, вы можете найти его в ReactScrollView.java

protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // Call with the present values in order to re-layout if necessary
    scrollTo(getScrollX(), getScrollY());
}

Итак, вы должны отложить его после анимации

componentDidMount() {
    InteractionManager.runAfterInteractions(() => {
      this.myScroll.scrollTo(100);
        console.log("called DidMount");
    })  
}

Ответ 2

Это работает на React Native 0.44.0. Спасибо за подсказку @Eldelshell. Это также, кажется, работает с любым значением времени ожидания. По крайней мере, на эмуляторе. Я обнаружил, что ответ с участием InteractionManager.runAfterInteractions не сделал ничего, чтобы решить проблему, но, возможно, что разница в версиях.

componentDidMount() {
  setTimeout(() => {
    this._scroll.scrollTo({y: 100})
  }, 1)
}

Ответ 3

Я хотел избежать использования задержек и таймеров, поэтому после небольшой копки я обнаружил, что использование onLayout работает очень гладко:

scrollToInitialPosition = () => {
  this.scrollViewRef.scrollTo({ y: 100 });
}
...
<ScrollView
  ref={(ref) => { this.scrollViewRef = ref; }}
  onLayout={this.scrollToInitialPosition}
/>

Ответ 4

Спасибо @David Nathan, использование InteractionManager работает для меня.
также обратите внимание, что в отличие от setTimeout, runAfterInteractions не будет задерживать активную анимацию.

Из документов InteractionManager