【问题标题】:Prevent inverted Flatlist from scrolling to bottom when new items are added添加新项目时防止倒置的平面列表滚动到底部
【发布时间】:2020-05-26 12:44:38
【问题描述】:

我正在构建一个聊天应用程序,使用倒置的Flatlist。当onEndReached 被调用并且一切正常时,我将新项目添加到列表顶部。

问题是如果将项目添加到底部,它会立即滚动到列表的底部。这意味着用户必须向上滚动才能阅读刚刚添加的消息(这很糟糕)。

我尝试在onContentSizeChange 中调用scrollToOffset,但这会有一秒钟的延迟,滚动条来回跳跃。

如何让列表在将项目添加到顶部和底部时以相同的方式运行,通过在屏幕上保留相同的消息而不是显示新消息?

【问题讨论】:

    标签: react-native react-native-flatlist


    【解决方案1】:

    这里是演示:https://snack.expo.io/@nomi9995/flatlisttest

    解决方案 1:

    使用maintainVisibleContentPosition props 来防止在IOS 中自动滚动,但不幸的是,它不适用于android。但这里是 android Pull Request 的 PR。在合并此 PR 之前,您可以通过此 PR 自行修补

    <FlatList
      ref={(ref) => { this.chatFlatList = ref; }}
      style={styles.flatList}
      data={this.state.items}
      renderItem={this._renderItem}
      maintainVisibleContentPosition={{
         minIndexForVisible: 0,
      }}
    />
    

    解决方案 2:

    我找到了另一种解决方法,通过使用onScroll 保持最新的 y 偏移量,并在使用 onContentSizeChange 添加新项目之前和之后保存内容高度并计算内容高度的差异,并将新的 y 偏移量设置为最新的 y 偏移量 +内容高度差!

    【讨论】:

    • 不幸的是,我需要一些适用于 iO 和 Android 的东西。在对我的问题的描述中,我指出我尝试使用 onContentSizeChange,但这会首先引发跳转到底部,然后跳转回我想要滚动的位置。这种方法似乎只适用于轻量级组件列表,没有复杂的分隔符(就像我一样)。
    • 您可以添加有问题的 gif 或视频吗? maintainVisibleContentPosition 在 android 和 ios 上工作,我们必须制作原生代码
    • 如果聊天消息添加到列表顶部,那么您想移到顶部,如果聊天消息添加到列表的 bootm,那么您希望 tom 移到底部?
    • 无论如何,我都不想移动到顶部或底部。我希望屏幕上的内容始终保持在屏幕上,无论我是否将数据附加或预先添加到我的列表中。基本上是每个聊天应用程序的行为。
    • 我有更新演示,你可以测试 ios snack.expo.io/@nomi9995/flatlisttest 。我会尝试在晚上制作本地代码来实现这一点
    【解决方案2】:

    我在 inverted Flatlist 的顶部和底部添加一个新项目。

    我希望您可以将您的要求与提供的示例代码进行比较:)

    完整代码:

    
    import React, {Component} from 'react';
    import {
      SafeAreaView,
      View,
      FlatList,
      StyleSheet,
      Text,
      Button,
      Platform,
      UIManager,
      LayoutAnimation,
    } from 'react-native';
    
    if (Platform.OS === 'android') {
      if (UIManager.setLayoutAnimationEnabledExperimental) {
        UIManager.setLayoutAnimationEnabledExperimental(true);
      }
    }
    
    const getRandomColor = () => {
      var letters = '0123456789ABCDEF';
      var color = '#';
      for (var i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)];
      }
      return color;
    };
    
    const DATA = [
      getRandomColor(),
      getRandomColor(),
      getRandomColor(),
      getRandomColor(),
      getRandomColor(),
    ];
    
    export default class App extends Component {
      scrollValue = 0;
      append = true;
    
      state = {
        data: DATA,
      };
    
      addItem = (top) => {
        const {data} = this.state;
        let newData;
        LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
        if (top) {
          newData = [...data, getRandomColor()];
          this.setState({data: newData});
        } else {
          newData = [getRandomColor(), ...data];
          this.setState({data: newData});
        }
      };
    
      shouldComponentUpdate() {
        return this.scrollValue === 0 || this.append;
      }
    
      onScrollBeginDrag = () => {
        this.append = true;
        LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
        this.setState({});
      };
    
      render() {
        const {data} = this.state;
        return (
          <SafeAreaView style={styles.container}>
            <Button title="ADD ON TOP" onPress={() => this.addItem(true)} />
            <FlatList
              data={data}
              onScrollBeginDrag={this.onScrollBeginDrag}
              renderItem={({item}) => <Item item={item} />}
              keyExtractor={(item) => item}
              inverted
              onScroll={(e) => {
                this.append = false;
                this.scrollValue = e.nativeEvent.contentOffset.y;
              }}
            />
            <Button title="ADD ON BOTTOM" onPress={() => this.addItem(false)} />
          </SafeAreaView>
        );
      }
    }
    
    function Item({item}) {
      return (
        <View style={[styles.item, {backgroundColor: item}]}>
          <Text style={styles.title}>{item}</Text>
        </View>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
      },
      item: {
        backgroundColor: '#f9c2ff',
        padding: 20,
        height: 100,
      },
      title: {
        fontSize: 32,
      },
    });
    
    

    【讨论】:

    • 当添加的内容大小未知时,您是否有解决方案?因为它是一个聊天应用程序,所以所有消息的高度都不相同。我尝试使用“onContentSizeChange”回调,但这有一个延迟,它会跳到底部然后回到它应该去的地方。
    • 我认为唯一的方法是在滚动位置不为 0 时停止渲染 flatlist(使用 shouldComponentUpdate)。
    • 刚刚编辑了代码部分。您可以设置最小滚动位置来附加数据。例如,将 this.scrollValue === 0 更改为 this.scrollValue
    【解决方案3】:

    这晚了一年,但效果很好:

            <FlatList
              inverted
              initialScrollIndex={1}
              {...}
            />
    

    由于倒置渲染平面列表但使用inverted: 1,因此您需要将1传递给initialScrollIndex,以便它滚动到正常列表的底部并滚动到倒置列表的顶部

    【讨论】:

      【解决方案4】:

      您是否尝试过使用 keyExtractor? 它可能有助于避免重新渲染,因此请尝试为每个项目使用唯一键。 你可以在这里阅读更多信息:https://reactnative.dev/docs/flatlist#keyextractor

      【讨论】:

      • 是的,我正在使用 keyExtractor 和每个项目的唯一键。当我将项目添加到顶部时,不会发生跳跃,只会发生在底部。我相信这可能是我想要阻止的倒排列表的预期行为。
      猜你喜欢
      • 2012-04-08
      • 1970-01-01
      • 1970-01-01
      • 2021-02-15
      • 1970-01-01
      • 2017-04-09
      • 1970-01-01
      • 2017-12-21
      相关资源
      最近更新 更多