欢迎加入开源鸿蒙跨平台社区 :https://openharmonycrossplatform.csdn.net
📋 前言
在移动应用开发中,图片画廊(Gallery)是一种常见的 UI 组件,广泛应用于图片预览、商品展示、相册浏览等场景。react-native-image-gallery 是一个功能强大的图片画廊组件,支持手势滑动、缩放、自定义渲染等特性,是实现图片浏览体验的理想选择。
🎯 库简介
基本信息
- 库名称 :
react-native-image-gallery - 版本信息 : 请到 Releases 查看配套版本
- 官方仓库: https://github.com/react-native-oh-library/react-native-image-gallery
- 主要功能 :
- 📱 流畅的图片滑动浏览
- 👆 支持手势缩放
- 🎨 自定义图片渲染
- ⚠️ 错误处理组件
- 📊 滚动事件回调
- 🔧 FlatList 属性配置
为什么选择 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,我们为项目添加了流畅的图片画廊功能。该库提供了完整的图片浏览体验,包括滑动浏览、手势交互、自定义渲染等,是实现图片预览、商品展示等场景的理想组件。