显示键盘后滚动到 FlatList 的末尾

Scroll to end of FlatList after displaying the keyboard

我在 KeyboardAvoidingView 中有一个 FlatList。当显示键盘时,我想滚动到 FlatList 的末尾。

我正在监听确实被触发的 'keyboardDidShow' 事件,但它可能被触发得太早,因为 FlatList 在调用 scrollToEnd 后没有滚动到末尾。

我研究了 KeyboardAvoidingView 的 onLayout 事件,但是仅仅设置 onLayout 事件来触发一个函数似乎阻止了 KeyboardAvoidingView 在显示键盘时调整它的大小。

<KeyboardAvoidingView behavior='padding' style={{ flex: 1}} onLayout={this._scrollEnd}>

代码:

import React from 'react';
import {Image, Linking, Platform, ScrollView, StyleSheet, Text, TouchableOpacity, View, Button, Alert, FlatList, TextInput, KeyboardAvoidingView, Keyboard} from 'react-native';
import { MonoText } from '../components/StyledText';

export default class HomeScreen extends React.Component {
  constructor() {
    super();
    this.state = {
      messages: getMessages()
    };

    this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._scrollEnd);
    this.keyboardDidShowListener = Keyboard.addListener('keyboardDidHide', this._scrollEnd);
  }

  _scrollEnd = (evt) => {
    this.refs.flatList1.scrollToEnd();
  }

  render() {
    return (
      <KeyboardAvoidingView behavior='padding' style={{ flex: 1}} >
        <FlatList
          style={{ flex:1}}
          ref="flatList1"
          data={this.state.messages}
          renderItem={({ item }) => <Text>{item.text}</Text>}
        />
      </KeyboardAvoidingView>
    );
  }
}

如上 所述,getItemLayout 应该可以解决您的问题。

根据Reactive FlatList documentation

getItemLayout is an optional optimizations that let us skip measurement of dynamic content if you know the height of items a priori. getItemLayout is the most efficient, and is easy to use if you have fixed height items, for example:

getItemLayout={(data, index) => (
  {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}

如果您使用 ItemSeparatorComponent,请不要忘记在计算结果偏移量时包含分隔符高度或宽度。

实际上如果你总是想滚动到最后,意味着你总是想看到最新的消息,对吗?

然后使用新版本的 react-native。并添加 inverted 来改变平面列表的颠倒。

<FlatList
      inverted
      style={{ flex:1}}
      ref="flatList1"
      data={this.state.messages}
      renderItem={({ item }) => <Text>{item.text}</Text>}
    />

然后重新排列您的 this.state.messages。那么您的最新消息将始终显示在 flatlist

的底部

对于我的情况,我不需要使用 KeyboardAvoidingView

我正在制作一个聊天组件,我想要同样的东西。是不是这样:

<FlatList
   ref={ref => this.flatList = ref}
   onContentSizeChange={() => this.flatList.scrollToEnd({animated: true})}
   onLayout={() => this.flatList.scrollToEnd({animated: true})}
   ...
/>

弹出键盘会触发布局,已修复。新的聊天消息到达会触发内容更改,因此它也会滚动到底部(这正是我聊天想要的 window)

我一直在使用我制作的这个小组件来通过键盘管理平面列表高度。这使用了 renderProps 模式,所以你可以重用它:)

import { PureComponent } from 'react';
import { Keyboard, Dimensions, Animated } from 'react-native';

const DURATION = 200;

class ListSpacer extends PureComponent {
  state = {
    screenHeight: Dimensions.get('window').height,
    flatListHeight: new Animated.Value(Dimensions.get('window').height),
  };

  componentDidMount() {
    this._keyboardDidShowListener = Keyboard.addListener(
      'keyboardDidShow',
      this._keyBoardDidShow,
    );
    this._keyboardDidHideListener = Keyboard.addListener(
      'keyboardDidHide',
      this._keyBoardDidHide,
    );
  }

  componentWillUnmount() {
    this._keyboardDidShowListener.remove();
    this._keyboardDidHideListener.remove();
  }

  _keyBoardDidShow = e => {
    Animated.timing(this.state.flatListHeight, {
      toValue: Dimensions.get('window').height - e.endCoordinates.height,
      duration: DURATION,
    }).start();
  };

  _keyBoardDidHide = () => {
    Animated.timing(this.state.flatListHeight, {
      toValue: Dimensions.get('window').height,
      duration: DURATION,
    }).start();
  };

  render() {
    const renderProps = {
      flatListHeight: this.state.flatListHeight,
    };

    if (this.props.children) {
      return this.props.children(renderProps);
    }

    return this.props.render(renderProps);
  }
}

export default ListSpacer;

这里我们监听键盘事件,endcoordinates 给你键盘高度。这样你就可以用它来制作平面列表高度。

import {
  FlatList,
  KeyboardAvoidingView,
  Animated,
} from 'react-native';

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);

return (
  <ListSpacer>
    {({ flatListHeight }) => (
      <KeyboardAvoidingView
        behavior="padding"
        keyboardVerticalOffset={INPUT_HEIGHT}
      >
        <AnimatedFlatList
          inverted
          style={{ height: flatListHeight }}
          data={data.comments}
          keyExtractor={this._keyExtractor}
          renderItem={this._renderItem}
          contentContainerStyle={styles.contentList}
        />
      </KeyboardAvoidingView>
    )}
  </ListSpacer>
);

这里我有这个教程,如果你更直观的话,我会展示它的作用:)

https://youtu.be/2QnPZXCIN44?t=28m43s

一些用户 (@Nathileo) 要求使用基于挂钩的方法来滚动到 FlatList 的末尾。

  1. 首先,你需要实现React的useRef钩子:

    import {useRef} from 'react';

    const yourRef = useRef(null);

  2. 其次,FlatList标签必须带有引用和需要的功能:

    <FlatList
      ref={yourRef}
      onContentSizeChange={() => yourRef.current.scrollToEnd() }
      onLayout={() => yourRef.current.scrollToEnd() }
    />