ReactNative项目OpenHarmony三方库集成实战:react-native-image-gallery

欢迎加入开源鸿蒙跨平台社区 :https://openharmonycrossplatform.csdn.net

📋 前言

在移动应用开发中,图片画廊(Gallery)是一种常见的 UI 组件,广泛应用于图片预览、商品展示、相册浏览等场景。react-native-image-gallery 是一个功能强大的图片画廊组件,支持手势滑动、缩放、自定义渲染等特性,是实现图片浏览体验的理想选择。

🎯 库简介

基本信息

为什么选择 image-gallery?

特性 自定义实现 react-native-image-gallery
滑动浏览 ⚠️ 需手动实现 ✅ 内置支持
手势缩放 ❌ 需额外库 ✅ 内置支持
错误处理 ⚠️ 需配置 ✅ 内置支持
滚动事件 ✅ ✅
自定义渲染 ✅ ✅
HarmonyOS支持 ✅ ✅

支持的属性

属性 说明 HarmonyOS 支持
images 图片数组 ✅
initialPage 初始显示页 ✅
imageComponent 自定义图片渲染 ✅
errorComponent 错误处理组件 ✅
flatListProps FlatList 属性 ✅
pageMargin 页面间距 ✅
onPageSelected 页面选中回调 ✅
onPageScrollStateChanged 滚动状态回调 ✅
onPageScroll 滚动事件回调 ✅
scrollViewStyle 滚动视图样式 ✅
onSingleTapConfirmed 单击回调 ✅
onLongPress 长按回调 ✅

兼容性验证

请到三方库相应的 Releases 发布地址查看 Release 配套的版本信息:@react-native-oh-tpl/react-native-image-gallery Releases

📦 安装步骤

1. 安装依赖

在项目根目录执行以下命令,本文基于 RN 0.72.90 版本开发:

bash 复制代码
npm install @react-native-oh-tpl/react-native-image-gallery

# 或者使用 yarn
yarn add @react-native-oh-tpl/react-native-image-gallery

2. 验证安装

安装完成后,检查 package.json 文件,应该能看到新增的依赖:

json 复制代码
{
  "dependencies": {
    "@react-native-oh-tpl/react-native-image-gallery": "^2.1.5-0.0.1",
    // ... 其他依赖
  }
}

💡 提示 :react-native-image-gallery 是纯 JS 组件,安装后无需额外配置原生代码,直接使用即可。

3. 添加 TypeScript 类型声明(可选)

如果项目使用 TypeScript,需要添加类型声明文件。

步骤 1 :在项目根目录创建 react-native-image-gallery.d.ts:

typescript 复制代码
declare module 'react-native-image-gallery' {
  import { Component } from 'react';
  import { ViewStyle, FlatListProps } from 'react-native';

  export interface ImageSource {
    source: { uri: string } | number;
    dimensions?: { width: number; height: number };
  }

  export interface GalleryProps {
    style?: ViewStyle;
    images?: ImageSource[];
    initialPage?: number;
    imageComponent?: (imageProps: any, dimensions: any) => JSX.Element;
    errorComponent?: () => JSX.Element;
    flatListProps?: Partial<FlatListProps<any>>;
    pageMargin?: number;
    onPageSelected?: (index: number) => void;
    onPageScrollStateChanged?: (state: 'idle' | 'dragging' | 'settling') => void;
    onPageScroll?: (event: any) => void;
    scrollViewStyle?: ViewStyle;
    onSingleTapConfirmed?: (index: number) => void;
    onLongPress?: (index: number) => void;
  }

  export default class Gallery extends Component<GalleryProps> {}
}

步骤 2 :确保 tsconfig.json 包含该文件。打开 tsconfig.json,检查是否有以下配置:

json 复制代码
{
  "compilerOptions": {
    // ... 其他配置
  },
  "include": [
    "react-native-image-gallery.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ]
}

⚠️ 注意 :修改完成后,请重启 IDE 或 TypeScript 服务使配置生效。在 VS Code 中可以使用 Ctrl+Shift+P,输入 TypeScript: Restart TS Server。

📖 API 详解

🔷 images - 图片数组 ⭐

images 是必填属性,定义画廊中要显示的图片。

属性 类型 必填 说明
source object | number ✅ 图片源,可以是 { uri: 'url' } 或 require()
dimensions object ❌ 图片尺寸 { width, height }

应用场景:

typescript 复制代码
import Gallery from 'react-native-image-gallery';

// 场景1:基础图片画廊
const images = [
  { source: require('./image1.png'), dimensions: { width: 150, height: 150 } },
  { source: { uri: 'http://i.imgur.com/XP2BE7q.jpg' } },
  { source: { uri: 'http://i.imgur.com/5nltiUd.jpg' } },
];

<Gallery style={{ flex: 1, backgroundColor: 'black' }} images={images} />

// 场景2:混合本地和网络图片
const mixedImages = [
  { source: require('./assets/image1.png'), dimensions: { width: 800, height: 600 } },
  { source: { uri: 'https://example.com/image2.jpg' } },
];

<Gallery style={{ flex: 1 }} images={mixedImages} />

🔷 initialPage - 初始显示页

设置画廊初始显示的图片索引。

属性 类型 默认值 说明
initialPage number 0 初始显示的图片索引

应用场景:

typescript 复制代码
// 场景:从第三张图片开始显示
<Gallery
  style={{ flex: 1 }}
  images={images}
  initialPage={2}
/>

🔷 imageComponent - 自定义图片渲染

自定义图片的渲染方式,可以实现添加水印、标签等效果。

应用场景:

typescript 复制代码
import { Image, View, Text } from 'react-native';

// 场景:添加自定义水印
const customImageComponent = (imageProps: any, dimensions: any) => {
  return (
    <View style={{ flex: 1 }}>
      <Image {...imageProps} style={[imageProps.style, { opacity: 0.9 }]} />
      <View style={{ position: 'absolute', bottom: 20, left: 20 }}>
        <Text style={{ color: 'white' }}>自定义水印</Text>
      </View>
    </View>
  );
};

<Gallery
  style={{ flex: 1 }}
  images={images}
  imageComponent={customImageComponent}
/>

🔷 errorComponent - 错误处理组件

当图片加载失败时显示的自定义组件。

应用场景:

typescript 复制代码
import { View, Text, StyleSheet } from 'react-native';

// 场景:自定义错误提示
const errorComponent = () => {
  return (
    <View style={styles.errorContainer}>
      <Text style={styles.errorText}>图片加载失败</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  errorContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f0f0f0',
  },
  errorText: {
    fontSize: 16,
    color: '#999',
  },
});

<Gallery
  style={{ flex: 1 }}
  images={images}
  errorComponent={errorComponent}
/>

🔷 flatListProps - FlatList 属性

传递到底层 FlatList 的属性,可以优化性能或自定义滚动行为。

应用场景:

typescript 复制代码
// 场景:性能优化配置
<Gallery
  style={{ flex: 1 }}
  images={images}
  flatListProps={{
    windowSize: 5,           // 渲染窗口大小
    initialNumToRender: 3,   // 初始渲染数量
    maxToRenderPerBatch: 2,  // 每批渲染数量
    removeClippedSubviews: true,  // 移除不可见子视图
  }}
/>

🔷 pageMargin - 页面间距

设置图片之间的间距。

属性 类型 默认值 说明
pageMargin number 0 图片之间的间距

应用场景:

typescript 复制代码
// 场景:设置图片间距
<Gallery
  style={{ flex: 1 }}
  images={images}
  pageMargin={10}
/>

🔷 onPageSelected - 页面选中回调

当用户滑动到新页面时触发。

应用场景:

typescript 复制代码
// 场景:更新当前页面索引
const [currentIndex, setCurrentIndex] = useState(0);

<Gallery
  style={{ flex: 1 }}
  images={images}
  onPageSelected={setCurrentIndex}
/>

🔷 onPageScrollStateChanged - 滚动状态回调

当页面滚动状态改变时触发。

状态 说明
idle 空闲状态,没有滚动
dragging 用户正在拖动
settling 滚动到最终位置中

应用场景:

typescript 复制代码
// 场景:监听滚动状态
const handleScrollStateChanged = (state: 'idle' | 'dragging' | 'settling') => {
  console.log('滚动状态:', state);
};

<Gallery
  style={{ flex: 1 }}
  images={images}
  onPageScrollStateChanged={handleScrollStateChanged}
/>

🔷 onSingleTapConfirmed - 单击回调

当用户单击图片时触发。

应用场景:

typescript 复制代码
// 场景:单击显示/隐藏工具栏
const handleSingleTap = (index: number) => {
  console.log('单击图片:', index);
  // 切换工具栏显示状态
};

<Gallery
  style={{ flex: 1 }}
  images={images}
  onSingleTapConfirmed={handleSingleTap}
/>

🔷 onLongPress - 长按回调

当用户长按图片时触发。

应用场景:

typescript 复制代码
import { Alert } from 'react-native';

// 场景:长按显示保存选项
const handleLongPress = (index: number) => {
  Alert.alert('提示', '是否保存图片?', [
    { text: '取消', style: 'cancel' },
    { text: '保存', onPress: () => console.log('保存图片') },
  ]);
};

<Gallery
  style={{ flex: 1 }}
  images={images}
  onLongPress={handleLongPress}
/>

💻 完整代码示例

下面是一个完整的示例,展示了 react-native-image-gallery 的各种功能应用:

typescript 复制代码
import React, { useState, useCallback } from 'react';
import {
  View,
  Text,
  StyleSheet,
  SafeAreaView,
  TouchableOpacity,
  Alert,
  StatusBar,
} from 'react-native';
import Gallery from 'react-native-image-gallery';

function GalleryDemo() {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [isToolbarVisible, setIsToolbarVisible] = useState(true);

  const images = [
    { source: { uri: 'https://picsum.photos/800/600?random=1' }, dimensions: { width: 800, height: 600 } },
    { source: { uri: 'https://picsum.photos/800/600?random=2' }, dimensions: { width: 800, height: 600 } },
    { source: { uri: 'https://picsum.photos/800/600?random=3' }, dimensions: { width: 800, height: 600 } },
    { source: { uri: 'https://picsum.photos/800/600?random=4' }, dimensions: { width: 800, height: 600 } },
    { source: { uri: 'https://picsum.photos/800/600?random=5' }, dimensions: { width: 800, height: 600 } },
  ];

  const handlePageSelected = useCallback((index: number) => {
    setCurrentIndex(index);
  }, []);

  const handleSingleTap = useCallback((index: number) => {
    setIsToolbarVisible((prev) => !prev);
  }, []);

  const handleLongPress = useCallback((imageData: any) => {
    const imageInfo = imageData?.source?.uri || `第 ${currentIndex + 1} 张图片`;
    Alert.alert('操作', `是否保存图片?`, [
      { text: '取消', style: 'cancel' },
      { text: '保存', onPress: () => console.log('保存图片:', imageInfo) },
    ]);
  }, [currentIndex]);

  const handleScrollStateChanged = useCallback((state: 'idle' | 'dragging' | 'settling') => {
    console.log('滚动状态:', state);
  }, []);

  const errorComponent = useCallback(() => {
    return (
      <View style={styles.errorContainer}>
        <Text style={styles.errorIcon}>⚠️</Text>
        <Text style={styles.errorText}>图片加载失败</Text>
        <Text style={styles.errorHint}>请检查网络连接</Text>
      </View>
    );
  }, []);

  return (
    <SafeAreaView style={styles.container}>
      <StatusBar barStyle="light-content" backgroundColor="#000" />

      <Gallery
        style={styles.gallery}
        images={images}
        initialPage={0}
        pageMargin={10}
        errorComponent={errorComponent}
        flatListProps={{
          windowSize: 5,
          initialNumToRender: 2,
          maxToRenderPerBatch: 2,
          removeClippedSubviews: true,
        }}
        onPageSelected={handlePageSelected}
        onPageScrollStateChanged={handleScrollStateChanged}
        onSingleTapConfirmed={handleSingleTap}
        onLongPress={handleLongPress}
      />

      {isToolbarVisible && (
        <View style={styles.toolbar}>
          <TouchableOpacity style={styles.toolbarButton}>
            <Text style={styles.toolbarButtonText}>返回</Text>
          </TouchableOpacity>
          
          <View style={styles.pageIndicator}>
            <Text style={styles.pageText}>
              {currentIndex + 1} / {images.length}
            </Text>
          </View>
          
          <TouchableOpacity style={styles.toolbarButton}>
            <Text style={styles.toolbarButtonText}>保存</Text>
          </TouchableOpacity>
        </View>
      )}
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#000',
  },
  gallery: {
    flex: 1,
    backgroundColor: '#000',
  },
  errorContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#1a1a1a',
  },
  errorIcon: {
    fontSize: 48,
    marginBottom: 16,
  },
  errorText: {
    fontSize: 18,
    color: '#fff',
    fontWeight: 'bold',
    marginBottom: 8,
  },
  errorHint: {
    fontSize: 14,
    color: '#999',
  },
  toolbar: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingHorizontal: 20,
    paddingVertical: 16,
    backgroundColor: 'rgba(0, 0, 0, 0.7)',
  },
  toolbarButton: {
    paddingHorizontal: 16,
    paddingVertical: 8,
    borderRadius: 8,
    backgroundColor: 'rgba(255, 255, 255, 0.2)',
  },
  toolbarButtonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '500',
  },
  pageIndicator: {
    flex: 1,
    alignItems: 'center',
  },
  pageText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

export default GalleryDemo;

📊 API 支持情况总览

属性 说明 HarmonyOS 支持
images 图片数组 ✅
initialPage 初始显示页 ✅
imageComponent 自定义图片渲染 ✅
errorComponent 错误处理组件 ✅
flatListProps FlatList 属性 ✅
pageMargin 页面间距 ✅
onPageSelected 页面选中回调 ✅
onPageScrollStateChanged 滚动状态回调 ✅
onPageScroll 滚动事件回调 ✅
scrollViewStyle 滚动视图样式 ✅
onSingleTapConfirmed 单击回调 ✅
onLongPress 长按回调 ✅

⚠️ 已知问题

Animated 警告

使用时可能会出现以下警告:

复制代码
Animated: 'useNativeDriver' was not specified. This is a required option and must be explicitly set to 'true' or 'false'

解决方案 :在应用入口文件(如 App.tsx 或 index.js)中添加以下代码,忽略此警告:

typescript 复制代码
import { LogBox } from 'react-native';

// 忽略 image-gallery 的 Animated 警告
LogBox.ignoreLogs([
  'Animated: `useNativeDriver` was not specified',
]);

或者在组件中添加:

typescript 复制代码
import React, { useEffect } from 'react';
import { LogBox } from 'react-native';

function GalleryDemo() {
  useEffect(() => {
    LogBox.ignoreLogs([
      'Animated: `useNativeDriver` was not specified',
    ]);
  }, []);
  
  // ... 其他代码
}

⚠️ 注意事项

1. 图片加载

  • 网络图片需要确保网络权限配置
  • 建议提供 dimensions 属性以优化布局
  • 使用 errorComponent 处理加载失败情况

2. 性能优化

  • 使用 flatListProps 优化长列表性能
  • 合理设置 windowSize 和 initialNumToRender
  • 对于大量图片,考虑使用分页加载

3. 常见问题

问题 1: 图片不显示

  • 检查图片 URL 是否正确
  • 确认网络权限是否配置
  • 检查是否设置了正确的样式

问题 2: 滑动不流畅

  • 使用 flatListProps 优化性能
  • 减少同时渲染的图片数量
  • 考虑图片压缩

问题 3: 内存占用过高

  • 使用 removeClippedSubviews: true
  • 适当减小 windowSize
  • 考虑使用缩略图

📝 总结

通过集成 react-native-image-gallery,我们为项目添加了流畅的图片画廊功能。该库提供了完整的图片浏览体验,包括滑动浏览、手势交互、自定义渲染等,是实现图片预览、商品展示等场景的理想组件。

相关推荐
admin and root1 小时前
「AI安全篇」实战AntiDebug自动化JS逆向加解密MCP
javascript·人工智能·网络安全·自动化·漏洞挖掘·cnvd·src赏金
wengad3 小时前
从 wangEditor 迁到 wangEditor-next
前端·javascript·vue.js
行者-全栈开发4 小时前
【前端/JS框架】CVE-2026-3854:GitHub Enterprise Server X-Stat注入XSS漏洞深度剖析与紧急修复指南
前端·javascript·github
怕浪猫6 小时前
2026年金九银十,我面了22个前端
前端·javascript·面试
前端的日常6 小时前
一个人+AI做情侣食谱小程序,30天纯赚
前端·javascript·后端
是你的小橘呀6 小时前
React Native 3:useEffect 自动请求接口 + 搜索功能 + 封装 Loading 组件
react native
qq_316837756 小时前
阿里云百炼 fun-asr-realtime uniapp vue3 调用
javascript·vue.js·uni-app
默_笙7 小时前
🚊 别再让 AI 的记忆写两遍了:PostgreSQL 把关系查询和语义检索装进一张表
前端·javascript
lzhdim7 小时前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#
是你的小橘呀7 小时前
React Native 2:列表渲染 + 后端接口搭建 + Fetch 请求数据
react native