如何在React Native的ListView中居中一个项目?

7

我正在尝试在水平listView中选中一个项目后将其居中。我的当前策略是先测量该项目,然后滚动到视图内参考项目的x坐标。

enter image description here

目前,每当我按下一个项目时,ListView会滚动到最末尾x: 538

是否有更简单的方法来实现这个功能并保持代码无状态/函数式?

const ItemScroll = (props) => {

  const createItem = (obj, rowID) => {

    const isCurrentlySelected = obj.id === props.currentSelectedID

    function scrollToItem(_scrollViewItem, _scrollView) {
      // measures the item coordinates
      _scrollViewItem.measure((fx) => {
        console.log('measured fx: ', fx)
        const itemFX = fx;
        // scrolls to coordinates
        return _scrollView.scrollTo({ x: itemFX });
      });
    }
    return (
      <TouchableHighlight
        ref={(scrollViewItem) => { _scrollViewItem = scrollViewItem; }}
        isCurrentlySelected={isCurrentlySelected}
        style={isCurrentlySelected ? styles.selectedItemContainerStyle : styles.itemContainerStyle}
        key={rowID}
        onPress={() => { scrollToItem( _scrollViewItem, _scrollView); props.onEventFilterPress(obj.id, rowID) }}>
        <Text style={isCurrentlySelected ? styles.selectedItemStyle : styles.itemStyle} >
          {obj.title}
        </Text>
      </TouchableHighlight>
    )
  };
  return (
    <View>
      <ScrollView
        ref={(scrollView) => { _scrollView = scrollView; }}
        horizontal>
        {props.itemList.map(createItem)}
        {props.onItemPress}
      </ScrollView>
    </View>
  );
};

更新

在@Ludovic的建议下,我现在已经转换为FlatList,但我不确定如何在功能组件中触发scrollToIndex。以下是我的新ItemScroll:

const ItemScroll = (props) => {

  const {
      itemList,
      currentSelectedItem
      onItemPress } = props

  const renderItem = ({item, data}) => {
    const isCurrentlySelected = item.id === currentSelectedItem

    const _scrollToIndex = () => { return { viewPosition: 0.5, index: data.indexOf({item}) } }

    return (
      <TouchableHighlight
        // Below is where i need to run onItemPress in the parent
        // and scrollToIndex in this child.
        onPress={[() => onItemFilterPress(item.id), scrollToIndex(_scrollToIndex)]} >
        <Text style={isCurrentlySelected ? { color: 'red' } : { color: 'blue' }} >
          {item.title}
        </Text>
      </TouchableHighlight>
    )
  }
  return (
    <FlatList
      showsHorizontalScrollIndicator={false}
      data={itemList}
      keyExtractor={(item) => item.id}
      getItemLayout={(data, index) => (
          // Max 5 items visibles at once
          { length: Dimensions.get('window').width / 5, offset: Dimensions.get('window').width / 5 * index, index }
      )}
      horizontal        
      // Here is the magic : snap to the center of an item
      snapToAlignment={'center'} 
      // Defines here the interval between to item (basically the width of an item with margins)
      snapToInterval={Dimensions.get('window').width / 5}
      renderItem={({item, data}) => renderItem({item, data})} />
  );
};
3个回答

7

在我看来,你应该使用FlatList

FlatList具有一个方法scrollToIndex,可以直接跳转到您的数据中的某个项目。它几乎与ScrollView相同,但更加智能。遗憾的是文档非常贫乏。

这里是我做的一个FlatList示例:

let datas = [{key: 0, text: "Hello"}, key: 1, text: "World"}]

<FlatList
    // Do something when animation ended
    onMomentumScrollEnd={(e) => this.onScrollEnd(e)} 
    ref="flatlist"
    showsHorizontalScrollIndicator={false}
    data={this.state.datas}
    keyExtractor={(item) => item.key}
    getItemLayout={(data, index) => (
        // Max 5 items visibles at once
        {length: Dimensions.get('window').width / 5, offset: Dimensions.get('window').width / 5 * index, index}   
    )}
    horizontal={true}
    // Here is the magic : snap to the center of an item
    snapToAlignment={'center'}  
    // Defines here the interval between to item (basically the width of an item with margins)
    snapToInterval={Dimensions.get('window').width / 5}    
    style={styles.scroll}
    renderItem={ ({item}) =>
        <TouchableOpacity
            onPress={() => this.scrollToIndex(/* scroll to that item */)}
            style={styles.cell}>
            <Text>{item.text}</Text>
        </TouchableOpacity>
    }
/>

关于FlatList的更多信息: https://facebook.github.io/react-native/docs/flatlist#__docusaurus


我该如何指定 scrollToIndex 的索引?是键、可见项数组中的索引还是整个数组中的索引? - Chris
数组中的索引 - Ludovic
2
onPress={() => scrollToIndex({viewPosition: 0.5, index: data.indexOf({item})})} 这样写可以解决问题吗? - Chris
也许在你的情况下是更好的选择!但是在滚动时应该发生某些操作。 - Ludovic
我的FlatList可以工作,但由于我正在使用函数组件,所以无法使用this.scrollToIndex进行滚动。我还需要在onPress中将函数传递给父组件。您有关于如何实现scrollToIndex的任何建议吗? - Chris

3

viewPosition属性将使您的项目居中显示。

flatListRef.scrollToIndex({ index: index, animated: true, viewPosition: 0.5 })

1
flatlistRef.current.scrollToIndex({
                    animated: true,
                    index,
                    viewOffset: Dimensions.get('window').width / 2.5,
                  });

你可以尝试将viewOffset放在scrollToIndex属性中,它将处理你组件的偏移量。我使用2.5使其位于屏幕中央,因为我每个部分显示5个项目,其中一个聚焦项目将位于屏幕中央。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接