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.tsxindex.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 优化长列表性能
  • 合理设置 windowSizeinitialNumToRender
  • 对于大量图片,考虑使用分页加载

3. 常见问题

问题 1: 图片不显示

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

问题 2: 滑动不流畅

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

问题 3: 内存占用过高

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

📝 总结

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

相关推荐
清汤饺子2 小时前
Cursor 从 0 到 1 系列《基础篇》:从零上手 Cursor
前端·javascript·后端
时寒的笔记2 小时前
逆向入门1整理2025.3.18
javascript·python
执行部之龙2 小时前
js手写——防抖
开发语言·前端·javascript
DEMO派2 小时前
JavaScript数据存储三剑客:Object、Map与WeakMap完全指南
开发语言·前端·javascript
芭拉拉小魔仙2 小时前
Vue v-html 中事件绑定失效问题及解决方案
javascript·vue.js·html
_果果然2 小时前
除了防抖和节流,还有哪些 JS 性能优化手段?
javascript·vue.js·性能优化
console.log('npc')2 小时前
git代码冲突reset,如何回退到冲突之前提交之前的版本
javascript·git·react.js
早點睡3903 小时前
ReactNative项目OpenHarmony三方库集成实战:@react-native-community/geolocation
javascript·react native·react.js
数据潜水员3 小时前
解决el-carousel 前后图片切换闪烁问题
前端·javascript·vue.js