React Native|使用flexbox实现类似小红书的「瀑布流」/「砌体」/「Masonry」布局

在这里插入图片描述

布局介绍

在很多内容/电商平台中,比如说小红书、Pinterest等,都会用到砌体布局。

砌体布局(Masonry Layout),又称「瀑布流布局」。这种布局的特征就是在垂直方向上按列排列,而每列的高度可以不同,给用户一种“错落有致”的视觉效果。

解决方案

那么如何在React Native中实现砌体布局呢?

实现的方法有很多,包括可以直接使用第三方库,如 react-native-masonry-list 。具体表现如何因为没有尝试过所以暂时不知道,想要使用第三方库进行快速集成的读者可以自行尝试。

在这篇文章中,我主要分享如何通过react native原生的方法进行Masonry布局的实现。

Flexbox方法实现

基础布局

根据观察砌体布局,我们可以发现布局的每一列宽度是固定的。因此,我们就可以给 ScrollView 的contentContainerStyle以下属性:

  • flexDirection: "row":使 ScrollView 中的两个 View 并排排列
  • gap: 8:给两个View之间留出一些空隙

具体的组件排列方式如下图所示:

  • 别忘了给 View flexGrow: 1 的样式

react-native-masonry-lis

图片填充View

接着,我们要做的事情就是用图片来填充这两个View。

假设我们有一个Images数组,包含了图片的uri数据。我们可以写一个splitImages2Columns函数,来把这一个图片数组分成两个数组leftColumnImgsrightColumnImgs,分别用于填充左边的View和右边的View:

const splitImages2Columns = (imgs) => {
    const leftColumnImgs = []; // 用于填充左边的View
    const rightColumnImgs = []; // 用于填充右边的View
    
    // 平均地将原数组的数据分配到两个数组中
    imgs.forEach((img, index) => {
        if (index % 2 === 0) {
            leftColumnImgs.push(img);
        } else {
            rightColumnImgs.push(img);
        }
    });

    return { leftColumnImgs, rightColumnImgs };
};

调用函数,获取左右两个数组:

const { leftColumnImgs, rightColumnImgs } = splitImages2Columns(Images);

得到数组之后,就能在左右两个View中分别使用Map映射到展示图片的组件中。

<View className="flex-1 gap-2">
	  {leftColumnImgs.map((item, index) => (
	    <PhotoCard key={`photo-${index}-l`} {...item} />
	  ))}
</View>
<View className="flex-1 gap-2">
	  {rightColumnImgs.map((item, index) => (
	    <PhotoCard key={`photo-${index}-r`} {...item} />
	  ))}
</View>

图片组件

如何在图片组件中,恰当地设置图片的尺寸呢?我们要的效果是能够使图片根据其原始的比例来展现,即:宽度固定,但是高度能够动态调整。

在我的项目中,由于要在图片上显示一些其它的数据,因此采用的是ImageBackground。并且由于图片资源是静态的,所以需要通过onLoad函数以及nativeEvent参数动态获取并设置图片的高度。

这部分如果有和我的需求不一样的,可以跳过;但是我还是记录一下自己的解决方法:

const PhotoCard = ({ imgsrc, stars, views, likes }: PhotoCardProps) => {
	  const [imgHeight, setImgHeight] = useState(0);
	  return (
		<ImageBackground
		  onLoad={({ nativeEvent }) => {
		    setImgHeight(nativeEvent.source.height);
		  }}
		  source={imgsrc}
		  imageStyle={{ width: '100%', height: imgHeight, borderRadius: 16 }}
		  className="justify-between"
		  style={{ height: imgHeight, width: '100%' }}>
		  <Views views={views} />
		  <View className="flex-row justify-between">
		    <Stars stars={stars} />
		    <Likes likes={likes} />
		  </View>
		</ImageBackground>
	  );
};

在这里的代码中,我通过使用ImageBackground的onLoad函数,来设置图片组件的高度。这里的高度是使用一个state来实现的。

别忘了,要把图片的宽度设置成100%,这样就能够在水平方向上填满两个50%的View容器。

实现效果

最终实现的效果是这样的:
在这里插入图片描述

为什么不用FlatList?

一开始,我是想用高性能的FlatList,通过设置 numColumns={2} 来实现2列的瀑布流布局。

但是使用的过程中发现,FlatList暂时不支持瀑布流布局,于是采用了ScrollView来代替。

React Native的官方文档中对于FlatList的numColumns属性的解释
在这里插入图片描述
如果想要通过FlatList来实现多列的高性能列表,还是能够行得通的;只不过每个元素的尺寸都是相同的,因此不能用于实现砌体布局。

本文使用的ScrollView中嵌入两个View的方法,在数据量极大的情况下应该不是很适用。不像高性能的FlatList,ScrollView会一次性把所有的子组件都渲染出来。如果对于性能有要求,需要在此基础上进行优化。

参考

实现方法的灵感来源于这个视频,但是自己在其基础上实现了保持图片原有比例,动态设置图片高度的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值