/react-native-waterfall-list-view

基于flatlist实现的 多列 不定高 瀑布流组件

Primary LanguageTypeScript

react-native-waterfall-list-view

基于官方 flatlist 实现的 多列 不定高 瀑布流组件 不依赖任何第三方

效果展示

两列瀑布流

RPReplay_Final1726644535

三列瀑布流

RPReplay_Final1726645556

使用说明

  • 组件基于 flatlist 实现   几乎支持 flatlist 所有属性 个别属性不支持 例如 horizontal 目前仅支持垂直方向
  • 基于 ts + hooks 实现 有较好的类型提示
  • 支持不定高 item 内部通过布局自动计算 所以 getItemLayout 设置无效
  • 关于 ref 的支持 默认取到的是 WaterFallList 的 ref   内部包括自定义的属性和 flatlist 实例方法.    如果想获取内部 flatlist 的 ref 对象 可以通过 WaterFallList 内部转发的的 flatList 属性
const waterfallRef = useRef<IWaterFallList>(null);

<WaterFallList ref={waterfallRef} />;

waterfallRef.current?.flatList?.scrollToOffset;

接口类型定义

export type ItemData = {
  offsetTop: number; // !! 元素距离容器顶部的距离
  itemH: number; // 元素高度
  itemData: any; // 元素数据
  columnIndex: number; // 当前元素所在的列
  index: number; // 原始列表的位置索引
};

export type RowData = {
  rowIndex: number; // 第几行
  rowData: ItemData[]; // 每行的列数据
  rowH: number; // 行高度
  rowOffsetTop: number; // !! 每行距离容器顶部的距离
};

export type IRenderItemProps = {
  item: ItemData;
  index: number;
  row: RowData;
};

export interface IWaterFallListProps
  extends Omit<
    FlatListProps<RowData>,
    "renderItem" | "ItemSeparatorComponent"
  > {
  renderItem: ({
    item,
    index,
    row,
  }: IRenderItemProps) => React.ReactElement | null;
  ItemSeparatorComponent?: () => JSX.Element;
  children?: React.ReactNode; // 添加children类型
  rowStyle?: ViewStyle;
}

export interface IWaterFallList {
  refreshList: (offset?: number, animated?: boolean) => void;
  flatList: FlatList | null;
}

代码示例参考 example

注意事项

  1. item 最大高度与最小高度差值不宜过大 建议:  最小高度>=最大高度的30%
  2. item 渲染完成后 不建议动态改变item的高度 会引起布局抖动
  3. 内部封装了 refreshList 函数 在刷新列表前 建议先调用此函数(刷新调用即可 列表项增加不需要调用)
  4. 仅支持垂直瀑布流
  5. 因为 rn 官方组件在 Android 上超出父容器无法点击的问题,组件内部的触摸组件需要使用 react-native-gesture-handler 中的替换 rn 官方
import {
  TouchableNativeFeedback,
  TouchableHighlight,
  TouchableOpacity,
  TouchableWithoutFeedback,
} from "react-native";

// has to be replaced with:
import {
  TouchableNativeFeedback,
  TouchableHighlight,
  TouchableOpacity,
  TouchableWithoutFeedback,
} from "react-native-gesture-handler";

原理

  1. 首先更改数据源 将单列表数组转换为 N 维数组确定每行的具体 item 数
  2. 首先渲染一次当前列表 获取到每个元素的真实高度信息(获取定位信息的方式可以参考这篇文章),并通过_itemHeightsRef 记录下来。
  3. 当高度信息收集完成 触发强制刷新 再次渲染一次列表
  4. 决策当前元素应该放在第几列 每行的高度是多少

例如当前一行是这个形式:
截屏2024-09-18 19 48 46

第三个元素应该加在下一行的最短一列:
截屏2024-09-18 19 48 41

同理第四个元素也加在最短的一列 第二列
然后计算第二行的行高:
因为 flatlist 的一行是以最长的元素高度为准 所以我们需要计算出最长的元素高度是多少 并且还要减去上一行的高度这个偏移量。
所以高度如下
截屏2024-09-18 17 59 27

然后不断循环列表 直到结束。