基于官方 flatlist 实现的 多列 不定高 瀑布流组件 不依赖任何第三方
- 组件基于 flatlist 实现 几乎支持 flatlist 所有属性 个别属性不支持 例如 horizontal 目前仅支持垂直方向
- 基于 ts + hooks 实现 有较好的类型提示
- 支持不定高 item 内部通过布局自动计算 所以 getItemLayout 设置无效
- 关于 ref 的支持 默认取到的是 WaterFallList 的 ref 内部包括自定义的属性和 flatlistRef. 如果想获取内部 flatlist 的 ref 对象 可以通过 WaterFallList 内部转发的的 flatListRef 对象
import WaterFallList from "react-native-waterfall-list-view"
import React, { memo, useEffect, useRef, useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import Item from "./item";
const colors = [
"#5A479A",
"#001694",
"#32296B",
"#D1D1D1",
"#641024",
"#FE3666",
"#292647",
"#B0E38F",
"#6195FC",
"#444444",
"#FFD283",
"#52210D",
"#FFE8ED",
"#3C325F",
"#19191E",
];
let index = 0;
const getList = (length = 15) => {
return Array.from({ length }, () => {
index++;
return {
h: Math.floor(Math.random() * 80) + 100,
bg: colors[Math.floor(Math.random() * colors.length)],
index,
key: index,
name: index,
};
});
};
const App = () => {
const [list, changeList] = useState([]);
const waterfallRef = useRef<IWaterFallList>();
const refresh = () => {
index = 0;
waterfallRef.current?.refreshList();
changeList(getList(20));
console.log(
"test scrollToOffset",
waterfallRef.current?.flatListRef.current?.scrollToOffset
);
};
const onEndReached = () => {
const nList = [...list, ...getList(20)];
console.log("test onEndReached", nList);
changeList(nList);
};
const onScroll = () => {
console.log("test onScroll");
};
useEffect(() => {
changeList(getList(20));
}, []);
return (
<View
style={{
height: "100%",
width: "100%",
backgroundColor: "#FAB5B5",
paddingHorizontal: 5,
}}
>
<TouchableOpacity onPress={refresh}>
<View
style={{
width: "100%",
backgroundColor: "blue",
height: 50,
marginTop: 20,
justifyContent: "center",
alignItems: "center",
}}
>
<Text style={{ color: "white" }}>刷新列表</Text>
</View>
</TouchableOpacity>
<WaterFallList
ref={waterfallRef}
ItemSeparatorComponent={() => {
return <View style={{ width: "100%", height: 10 }}></View>;
}}
initialNumToRender={10}
windowSize={10}
ListHeaderComponent={() => {
return (
<View
style={{
width: "100%",
backgroundColor: "orange",
height: 300,
marginBottom: 20,
}}
></View>
);
}}
onScroll={onScroll}
renderItem={({ item }) => <Item data={item}></Item>}
data={list}
contentContainerStyle={{ flexGrow: 1 }}
onEndReachedThreshold={0.5}
onEndReached={onEndReached}
numColumns={3}
showsVerticalScrollIndicator={false}
ListEmptyComponent={() => {
return (
<View
style={{
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center",
backgroundColor: "pink",
}}
>
<Text style={{ fontSize: 30, color: "red" }}>empty</Text>
</View>
);
}}
ListFooterComponent={() => {
return (
<View
style={{
width: "100%",
height: 50,
justifyContent: "center",
alignItems: "center",
backgroundColor: "pink",
}}
>
<Text style={{ fontSize: 30, color: "red" }}>正在加载中</Text>
</View>
);
}}
/>
</View>
);
};
export default memo<typeof App>(App);
- item 最大高度与最小高度差值不宜过大 建议:
最小高度>=最大高度的30%
- item 渲染完成后
不建议动态改变item的高度
会引起布局抖动 - 内部封装了 refreshList 函数 在刷新列表前 建议先调用此函数(刷新调用即可 列表项增加不需要调用)
- 仅支持垂直瀑布流
- 首先更改数据源 将单列表数组转换为 N 维数组确定每行的具体 item 数
- 首先渲染一次当前列表 获取到每个元素的真实高度信息(获取定位信息的方式可以参考这篇文章),并通过_itemHeightsRef 记录下来。
- 当高度信息收集完成 触发强制刷新 再次渲染一次列表
- 决策当前元素应该放在第几列 每行的高度是多少
同理第四个元素也加在最短的一列 第二列
然后计算第二行的行高:
因为 flatlist 的一行是以最长的元素高度为准 所以我们需要计算出最长的元素高度是多少 并且还要减去上一行的高度这个偏移量。
所以高度如下
然后不断循环列表 直到结束。