我正在使用FlatList
,其中每行可以具有不同的高度(并且可以包含来自远程服务器的文本和零个或多个图像的混合)。
我不能使用getItemLayout
,因为我不知道每行的高度(也不是前面的那些)能够计算。
我面临的问题是我无法滚动到列表的末尾(当我尝试时会跳回几行)并且在尝试使用scrollToIndex
时遇到问题(我猜因为我错过了getItemLayout
)。
我写了一个示例项目来演示这个问题:
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View, Image, FlatList } from 'react-native';
import autobind from 'autobind-decorator';
const items = count => [...Array(count)].map((v, i) => ({
key: i,
index: i,
image: 'https://dummyimage.com/600x' + (((i % 4) + 1) * 50) + '/000/fff',
}));
class RemoteImage extends Component {
constructor(props) {
super(props);
this.state = {
style: { flex: 1, height: 0 },
};
}
componentDidMount() {
Image.getSize(this.props.src, (width, height) => {
this.image = { width, height };
this.onLayout();
});
}
@autobind
onLayout(event) {
if (event) {
this.layout = {
width: event.nativeEvent.layout.width,
height: event.nativeEvent.layout.height,
};
}
if (!this.layout || !this.image || !this.image.width)
return;
this.setState({
style: {
flex: 1,
height: Math.min(this.image.height,
Math.floor(this.layout.width * this.image.height / this.image.width)),
},
});
}
render() {
return (
<Image
onLayout={this.onLayout}
source={{ uri: this.props.src }}
style={this.state.style}
resizeMode='contain'
/>
);
}
}
class Row extends Component {
@autobind
onLayout({ nativeEvent }) {
let { index, item, onItemLayout } = this.props;
let height = Math.max(nativeEvent.layout.height, item.height || 0);
if (height != item.height)
onItemLayout(index, { height });
}
render() {
let { index, image } = this.props.item;
return (
<View style={[styles.row, this.props.style]}>
<Text>Header {index}</Text>
<RemoteImage src = { image } />
<Text>Footer {index}</Text>
</View>
);
}
}
export default class FlatListTest extends Component {
constructor(props) {
super(props);
this.state = { items: items(50) };
}
@autobind
renderItem({ item, index }) {
return <Row
item={item}
style={index&1 && styles.row_alternate || null}
onItemLayout={this.onItemLayout}
/>;
}
@autobind
onItemLayout(index, props) {
let items = [...this.state.items];
let item = { ...items[index], ...props };
items[index] = { ...item, key: [item.height, item.index].join('_') };
this.setState({ items });
}
render() {
return (
<FlatList
ref={ref => this.list = ref}
data={this.state.items}
renderItem={this.renderItem}
/>
);
}
}
const styles = StyleSheet.create({
row: {
padding: 5,
},
row_alternate: {
backgroundColor: '#bbbbbb',
},
});
AppRegistry.registerComponent('FlatListTest', () => FlatListTest);
答案 0 :(得分:1)
所以我认为你可以做的以及你已经拥有的东西是通过行布局onLayout
的索引来存储集合。您需要存储returned by getItemLayout
:{length: number, offset: number, index: number}
的属性。
然后,当您实施getItemLayout
passes an index时,您可以返回已存储的布局。这应解决scrollToIndex
的问题。没有测试过这个,但这似乎是正确的方法。
答案 1 :(得分:1)
改为使用scrollToOffset():
export default class List extends React.PureComponent {
// Gets the total height of the elements that come before
// element with passed index
getOffsetByIndex(index) {
let offset = 0;
for (let i = 0; i < index; i += 1) {
const elementLayout = this._layouts[i];
if (elementLayout && elementLayout.height) {
offset += this._layouts[i].height;
}
}
return offset;
}
// Gets the comment object and if it is a comment
// is in the list, then scrolls to it
scrollToComment(comment) {
const { list } = this.props;
const commentIndex = list.findIndex(({ id }) => id === comment.id);
if (commentIndex !== -1) {
const offset = this.getOffsetByIndex(commentIndex);
this._flatList.current.scrollToOffset({ offset, animated: true });
}
}
// Fill the list of objects with element sizes
addToLayoutsMap(layout, index) {
this._layouts[index] = layout;
}
render() {
const { list } = this.props;
return (
<FlatList
data={list}
keyExtractor={item => item.id}
renderItem={({ item, index }) => {
return (
<View
onLayout={({ nativeEvent: { layout } }) => {
this.addToLayoutsMap(layout, index);
}}
>
<Comment id={item.id} />
</View>
);
}}
ref={this._flatList}
/>
);
}
}
1)渲染时,获取列表中每个元素的大小并将其写入数组:
onLayout={({ nativeEvent: { layout } }) => this._layouts[index] = layout}
2)当需要将屏幕滚动到该元素时,我汇总其前面所有元素的高度,并获取滚动屏幕的量(getOffsetByIndex方法)。
3)我使用scrollToOffset方法:
this._flatList.current.scrollToOffset({ offset, animated: true });
(this._flatList是FlatList的引用)
答案 2 :(得分:0)
您是否尝试过scrollToEnd
?
http://facebook.github.io/react-native/docs/flatlist.html#scrolltoend
正如文档所述,如果没有getItemLayout
,它可能会很混乱,但对我来说,没有它就可以工作
答案 3 :(得分:0)
当行的高度可变时,我找不到任何使用getItemLayout
的方法,因此您不能使用initialScrollIndex
。
但是我有一个可能有点慢的解决方案:
您可以使用scrollToIndex
,但是在渲染项目时使用。因此,您需要initialNumToRender
。
您必须等待项目被渲染,并且在使用scrollToIndex
之后,才能在scrollToIndex
中使用componentDidMount
。
我想到的唯一解决方案是在scrollToIndex
中使用onViewableItemsChanged
。请注意以下示例:
在此示例中,我们希望在运行此组件后立即转到项目this.props.index
constructor(props){
this.goToIndex = true;
}
render() {
return (
<FlatList
ref={component => {this.myFlatList = component;}}
data={data}
renderItem={({item})=>this._renderItem(item)}
keyExtractor={(item,index)=>index.toString()}
initialNumToRender={this.props.index+1}
onViewableItemsChanged={({ viewableItems }) => {
if (this.goToIndex){
this.goToIndex = false;
setTimeout(() => { this.myFlatList.scrollToIndex({index:this.props.index}); }, 10);
}
}}
/>
);
}