首页 > 解决方案 > 如何在与 ScrollView 的本机反应中实现 scrollspy?

问题描述

我有一个具有不同元素的 ScrollView 组件说:

<ScrollView>

    <Item1/>

    <Item2/>

    <Item3/>

</ScrollView>

我现在有一个选项卡,我需要根据用户在滚动视图中查看的当前部分突出显示每个选项卡项。例如,如果用户<Item2/>在滚动视图中查看/滚动到,那么我应该突出显示选项卡中的 item2 图标。

此外,如果用户单击选项卡项目,它应该自动使滚动视图滚动到相应项目的部分。

有没有办法在本机反应中实现这个滚动间谍功能?

标签: javascriptreactjsreact-native

解决方案


要实现所需的行为,您需要结合几件事。

  1. ScrollView有一个名为的方法scrollTo,可以让您滚动到内容的所需位置。
  2. 为了能够检测到ScrollView您需要使用onScroll属性的当前位置是什么。您可以将此属性与 结合使用scrollEventThrottle以获得更好的准确性。
  3. 要获取每个项目在其中的位置,ScrollView您需要onLayout为每个项目实现属性。

下面是一个小示例应用程序,展示了如何实现上述步骤。

const Item = props => (
  <View
    style={{ minHeight: 500 }}
    onLayout={e => props.onItemLayout(e, props.index)}>
    <Text>{`Item ${props.index}`}</Text>
  </View>
);

export default class App extends Component {
  constructor() {
    super();
    this.state = {
      sections: [1, 2, 3],
      currentSection: 1
    };
  }
  moveToSetion = section => {
    // scroll view to section
    this.scrollView.scrollTo({ x: 0, y: this.state[section], animated: true });
    // set state to current section
    this.setState({ currentSection: section });
  };
  onItemLayout = ({ nativeEvent: { layout: { x, y, width, height } } }, section) => {
    // setting each items position
    this.setState({ [section]: y });
  };
  onScroll = ({ nativeEvent: { contentOffset: { y, x } } }) => {
    let _currentSection;
    // loop sections to calculate which section scrollview is on
    this.state.sections.forEach((section) => {
      // adding 15 to calculate Text's height
      if((y + 15) > this.state[section]) _currentSection = section
    })
    // settint the currentSection to the calculated current section
    this.setState({ currentSection: _currentSection })
  }
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.buttonContainer}>
          {this.state.sections.map(section => (
            <Button
              key={section}
              title={`Section ${section}`}
              color={this.state.currentSection === section ? 'red' : 'blue'}
              onPress={() => this.moveToSetion(section)}
            />
          ))}
        </View>
        <ScrollView
          style={styles.scrollView}
          ref={ref => (this.scrollView = ref)}
          scrollEventThrottle={100}
          onScroll={this.onScroll}>
          {this.state.sections.map(section => (
            <Item
              key={section}
              index={section}
              onItemLayout={this.onItemLayout}
            />
          ))}
        </ScrollView>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
  },
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'center',
    position: 'fixed',
    top: 0,
  },
  scrollView: {
    paddingLeft: 15,
    paddingRight: 15
  }
});

推荐阅读