You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native(Expo)横向圆角图片列表实现方法问询

React Native(Expo)实现不规则图片列表组件方案

你要实现的这种图片列表组件通常被称为瀑布流/ Masonry 图片列表,核心特点是图片高度自适应,按列排布并自动填充空白区域。以下是两种实现方案:

一、使用现成库快速实现

推荐使用适配Expo的成熟库,节省开发时间:

  • react-native-masonry-list:完全兼容Expo,支持自定义列数、图片间距、加载状态,自动处理图片高度适配。
    安装命令:
    npx expo install react-native-masonry-list
    
    基础使用示例:
    import MasonryList from 'react-native-masonry-list';
    import { Image, Dimensions } from 'react-native';
    
    const ImageGallery = () => {
      const images = [
        { uri: 'https://example.com/img1.jpg', width: 600, height: 800 },
        { uri: 'https://example.com/img2.jpg', width: 600, height: 400 },
        // 更多图片数据...
      ];
    
      return (
        <MasonryList
          data={images}
          numColumns={2}
          renderItem={({ item }) => (
            <Image
              source={{ uri: item.uri }}
              style={{ 
                width: '100%', 
                height: (item.height / item.width) * (Dimensions.get('window').width / 2 - 10), 
                marginBottom: 10 
              }}
              resizeMode="cover"
            />
          )}
        />
      );
    };
    

二、从零构建自定义组件

如果需要高度定制化,可以手动实现布局逻辑:

步骤1:获取图片尺寸

首先要获取每张图片的原始宽高,用于计算自适应高度:

import { Image } from 'react-native';

const getImageDimensions = (url) => {
  return new Promise((resolve, reject) => {
    Image.getSize(url, (width, height) => {
      resolve({ width, height });
    }, (error) => reject(error));
  });
};

步骤2:实现瀑布流布局逻辑

通过维护两列的总高度,将图片插入到当前高度较小的列中,保证布局紧凑:

import { View, ScrollView, Image, StyleSheet, Dimensions } from 'react-native';
import { useState, useLayoutEffect } from 'react';

const CustomMasonryGrid = ({ imageUrls }) => {
  const [columnData, setColumnData] = useState([[], []]);
  const [columnHeights, setColumnHeights] = useState([0, 0]);
  const columnWidth = (Dimensions.get('window').width - 30) / 2; // 左右边距10,列间距10

  useLayoutEffect(() => {
    const calculateLayout = async () => {
      const newColumns = [[], []];
      const newHeights = [0, 0];

      for (const url of imageUrls) {
        try {
          const { width, height } = await getImageDimensions(url);
          const imgHeight = (height / width) * columnWidth;
          // 选择高度更低的列插入图片
          const targetCol = newHeights[0] <= newHeights[1] ? 0 : 1;
          newColumns[targetCol].push({ url, height: imgHeight });
          newHeights[targetCol] += imgHeight + 10; // 图片间距10
        } catch (err) {
          console.error('获取图片尺寸失败:', err);
        }
      }

      setColumnData(newColumns);
      setColumnHeights(newHeights);
    };

    calculateLayout();
  }, [imageUrls]);

  return (
    <ScrollView style={styles.container}>
      <View style={styles.row}>
        <View style={[styles.column, { height: columnHeights[0] }]}>
          {columnData[0].map((img, idx) => (
            <Image
              key={idx}
              source={{ uri: img.url }}
              style={[styles.image, { height: img.height }]}
              resizeMode="cover"
              placeholderContent={<View style={[styles.image, { height: img.height }, styles.placeholder]} />}
            />
          ))}
        </View>
        <View style={[styles.column, { height: columnHeights[1] }]}>
          {columnData[1].map((img, idx) => (
            <Image
              key={idx}
              source={{ uri: img.url }}
              style={[styles.image, { height: img.height }]}
              resizeMode="cover"
              placeholderContent={<View style={[styles.image, { height: img.height }, styles.placeholder]} />}
            />
          ))}
        </View>
      </View>
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingHorizontal: 10,
    paddingVertical: 8,
  },
  row: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  column: {
    width: '48%',
  },
  image: {
    width: '100%',
    marginBottom: 10,
    borderRadius: 8,
  },
  placeholder: {
    backgroundColor: '#f0f0f0',
  },
});

优化建议

  • 用React.memo包裹图片组件减少不必要的重渲染
  • 配合expo-image替代原生Image,提升加载性能和占位符体验
  • 如需下拉刷新,给ScrollView添加RefreshControl组件

内容的提问来源于stack exchange,提问作者DarkSide77

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 22:25:41