1. 项目背景与react-native-camera库简介
在移动应用开发中,相机功能已经成为许多应用的核心需求。无论是社交应用中的拍照分享、电商平台的扫码购物,还是企业应用中的文档扫描,都需要强大的相机功能支持。react-native-camera作为React Native生态中最流行的相机组件库,为开发者提供了丰富的相机控制功能。
react-native-camera的主要功能包括:
- 拍照功能(支持多种分辨率和质量设置)
- 视频录制(支持音频录制)
- 前后摄像头切换
- 闪光灯控制(开/关/自动模式)
- 自动对焦和手动对焦
- 二维码/条形码扫描
- 白平衡和曝光控制
- 缩放控制
- 人脸检测(部分支持)
在OpenHarmony平台上,react-native-camera通过@react-native-ohos/react-native-camera适配层提供了完整的HarmonyOS支持,使得React Native开发者可以轻松在OpenHarmony设备上实现相机功能。
2. 环境准备与安装配置
2.1 基础环境要求
在开始集成react-native-camera之前,需要确保开发环境满足以下要求:
- RNOH版本:0.72.90
- SDK版本:HarmonyOS 6.0.0 Release SDK
- IDE:DevEco Studio 6.0.2
- ROM版本:6.0.0
2.2 安装依赖
在项目根目录执行以下命令安装react-native-camera:
bash复制# 使用npm安装
npm install @react-native-ohos/react-native-camera@3.40.1-rc.2
# 或者使用yarn
yarn add @react-native-ohos/react-native-camera@3.40.1-rc.2
安装完成后,检查package.json文件确认依赖已正确添加:
json复制{
"dependencies": {
"@react-native-ohos/react-native-camera": "^3.40.1-rc.2",
// ... 其他依赖
}
}
2.3 OpenHarmony平台配置
由于OpenHarmony不支持AutoLink,需要手动配置原生端代码。配置步骤如下:
- 在工程根目录的oh-package.json5中添加overrides字段:
json复制{
// ... 其他配置
"overrides": {
"@rnoh/react-native-openharmony": "0.72.90"
}
}
- 在entry/oh-package.json5中添加依赖:
json复制"dependencies": {
"@rnoh/react-native-openharmony": "0.72.90",
"@react-native-ohos/react-native-camera": "file:../../node_modules/@react-native-ohos/react-native-camera/harmony/reactNativeCamera.har"
}
- 同步依赖:
bash复制cd harmony/entry
ohpm install
3. 原生代码集成与配置
3.1 CMakeLists.txt配置
打开harmony/entry/src/main/cpp/CMakeLists.txt,添加以下配置:
cmake复制# 添加Camera模块
add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-camera/src/main/cpp" ./reactNativeCamera)
# 在目标链接库中添加相机库
target_link_libraries(rnoh_app PUBLIC rnoh_native_camera)
3.2 PackageProvider.cpp修改
打开harmony/entry/src/main/cpp/PackageProvider.cpp,添加相机包的引入和注册:
cpp复制#include "NativeCameraPackage.h"
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
return {
std::make_shared<RNOHGeneratedPackage>(ctx),
std::make_shared<NativeCameraPackage>(ctx),
};
}
3.3 ArkTS侧组件映射
在entry/src/main/ets/pages/index.ets或entry/src/main/ets/rn/LoadBundle.ets中,添加相机组件的映射:
typescript复制import { ReactCameraView } from '@react-native-ohos/react-native-camera';
@Builder
export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
if (ctx.componentName === ReactCameraView.NAME) {
ReactCameraView({
ctx: ctx.rnComponentContext,
tag: ctx.tag,
})
}
}
同时,在arkTsComponentNames数组中添加组件名:
typescript复制const arkTsComponentNames: Array<string> = [
// ...其他组件
ReactCameraView.NAME
];
3.4 权限配置
在entry/src/main/module.json5中添加相机和麦克风权限:
json复制"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
"reason": "$string:camera_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:microphone_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
在string.json中添加权限说明:
json复制{
"string": [
{
"name": "camera_reason",
"value": "使用相机进行拍照和录像"
},
{
"name": "microphone_reason",
"value": "使用麦克风录制视频声音"
}
]
}
4. API详解与使用示例
4.1 基础相机功能
4.1.1 相机类型控制
javascript复制import { RNCamera } from '@react-native-ohos/react-native-camera';
<RNCamera
type="back" // 或 "front" 使用前置摄像头
style={styles.camera}
/>
4.1.2 闪光灯控制
javascript复制<RNCamera
flashMode="on" // 可选值: 'off', 'on', 'auto', 'torch'
style={styles.camera}
/>
4.1.3 自动对焦
javascript复制<RNCamera
autoFocus="on" // 或 "off"
style={styles.camera}
/>
4.2 拍照功能实现
javascript复制const takePicture = async () => {
if (cameraRef.current) {
const options = {
quality: 80, // 图片质量 0-100
base64: false, // 是否返回 base64
width: 1920, // 图片宽度
exif: false, // 是否包含 EXIF 信息
doNotSave: false, // 是否不保存到相册
};
const data = await cameraRef.current.takePictureAsync(options);
console.log('照片路径:', data.uri || data.path);
}
};
注意:在OpenHarmony平台上,返回的路径字段为path而非uri,建议使用data.path || data.uri来兼容不同平台。
4.3 视频录制功能
javascript复制const startRecording = async () => {
if (cameraRef.current) {
const options = {
quality: 2, // 鸿蒙版本必须使用数值
maxDuration: 60, // 最大录制时长(秒)
maxFileSize: 100 * 1024 * 1024, // 最大文件大小(字节)
mute: false, // 是否静音
};
const data = await cameraRef.current.recordAsync(options);
console.log('视频路径:', data.uri);
}
};
const stopRecording = () => {
if (cameraRef.current) {
cameraRef.current.stopRecording();
}
};
重要提示:在OpenHarmony平台上,quality参数必须使用数值而非字符串,否则会报错。建议定义如下映射关系:
javascript复制const VIDEO_QUALITY = { '2160p': 0, '1080p': 1, '720p': 2, '480p': 3, '4:3': 4, };
4.4 二维码扫描功能
javascript复制const [isScanning, setIsScanning] = useState(true);
const [lastScannedData, setLastScannedData] = useState(null);
const handleBarCodeRead = (event) => {
if (!isScanning) return;
if (!event || !event.data) return;
if (lastScannedData === event.data) return;
setIsScanning(false);
setLastScannedData(event.data);
Alert.alert(
'扫描结果',
`类型: ${event.type}\n内容: ${event.data}`,
[{ text: '继续扫描', onPress: () => setIsScanning(true) }]
);
};
// 在RNCamera组件中使用
<RNCamera
barCodeScannerEnabled={true}
onBarCodeRead={handleBarCodeRead}
/>
注意事项:
- 扫码模式和拍照/录像模式是互斥的,不能同时使用
- barCodeScannerEnabled只在组件初始化时生效,无法动态切换
- 建议添加防抖逻辑防止重复弹窗
5. 完整示例:多功能相机应用
以下是一个集成了拍照、录像和扫码功能的完整示例:
javascript复制import React, { useRef, useState } from 'react';
import { View, StyleSheet, TouchableOpacity, Text, Alert } from 'react-native';
import { RNCamera } from '@react-native-ohos/react-native-camera';
const App = () => {
const cameraRef = useRef(null);
const [type, setType] = useState('back');
const [flashMode, setFlashMode] = useState('off');
const [isRecording, setIsRecording] = useState(false);
const [mode, setMode] = useState('photo'); // 'photo', 'video', 'qr'
const toggleCameraType = () => {
setType(type === 'back' ? 'front' : 'back');
};
const toggleFlashMode = () => {
setFlashMode(
flashMode === 'auto' ? 'on' :
flashMode === 'on' ? 'off' :
flashMode === 'off' ? 'torch' : 'auto'
);
};
const takePicture = async () => {
if (cameraRef.current) {
try {
const options = { quality: 0.85, base64: true };
const data = await cameraRef.current.takePictureAsync(options);
Alert.alert('拍照成功', `图片已保存到: ${data.path || data.uri}`);
} catch (error) {
Alert.alert('错误', '拍照失败: ' + error.message);
}
}
};
const startRecording = async () => {
if (cameraRef.current) {
setIsRecording(true);
try {
const { uri } = await cameraRef.current.recordAsync();
Alert.alert('录像完成', `视频已保存到: ${uri}`);
} catch (error) {
Alert.alert('错误', '录像失败: ' + error.message);
} finally {
setIsRecording(false);
}
}
};
const stopRecording = () => {
if (cameraRef.current) {
cameraRef.current.stopRecording();
}
};
const handleBarCodeRead = ({ data }) => {
Alert.alert('二维码扫描', `内容: ${data}`);
};
return (
<View style={styles.container}>
<RNCamera
ref={cameraRef}
style={styles.preview}
type={type}
flashMode={flashMode}
autoFocus="on"
zoom={0}
barCodeScannerEnabled={mode === 'qr'}
onBarCodeRead={mode === 'qr' ? handleBarCodeRead : undefined}
>
<View style={styles.controls}>
<TouchableOpacity onPress={toggleCameraType} style={styles.button}>
<Text style={styles.text}>切换摄像头</Text>
</TouchableOpacity>
<TouchableOpacity onPress={toggleFlashMode} style={styles.button}>
<Text style={styles.text}>闪光灯: {flashMode}</Text>
</TouchableOpacity>
<View style={styles.modeSelector}>
<TouchableOpacity
onPress={() => setMode('photo')}
style={[styles.modeButton, mode === 'photo' && styles.activeMode]}
>
<Text style={styles.text}>拍照</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => setMode('video')}
style={[styles.modeButton, mode === 'video' && styles.activeMode]}
>
<Text style={styles.text}>录像</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => setMode('qr')}
style={[styles.modeButton, mode === 'qr' && styles.activeMode]}
>
<Text style={styles.text}>扫码</Text>
</TouchableOpacity>
</View>
{mode === 'photo' && (
<TouchableOpacity onPress={takePicture} style={styles.captureButton}>
<Text style={styles.text}>拍照</Text>
</TouchableOpacity>
)}
{mode === 'video' && (
<TouchableOpacity
onPress={isRecording ? stopRecording : startRecording}
style={[styles.captureButton, isRecording && styles.recording]}
>
<Text style={styles.text}>
{isRecording ? '停止' : '开始'}
</Text>
</TouchableOpacity>
)}
</View>
</RNCamera>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
preview: {
flex: 1,
justifyContent: 'flex-end',
},
controls: {
backgroundColor: 'rgba(0,0,0,0.5)',
padding: 20,
alignItems: 'center',
},
button: {
padding: 10,
marginVertical: 5,
backgroundColor: 'rgba(255,255,255,0.3)',
borderRadius: 5,
},
text: {
color: 'white',
textAlign: 'center',
},
modeSelector: {
flexDirection: 'row',
marginVertical: 10,
},
modeButton: {
padding: 10,
marginHorizontal: 5,
backgroundColor: 'rgba(255,255,255,0.2)',
borderRadius: 5,
},
activeMode: {
backgroundColor: 'rgba(255,255,255,0.5)',
},
captureButton: {
padding: 15,
marginVertical: 10,
backgroundColor: 'rgba(255,255,255,0.3)',
borderRadius: 40,
width: 80,
height: 80,
justifyContent: 'center',
},
recording: {
backgroundColor: 'rgba(255,0,0,0.5)',
},
});
export default App;
6. 常见问题与解决方案
6.1 拍照返回的路径问题
在OpenHarmony平台上,拍照返回的路径字段为path而非uri,建议使用以下方式处理:
javascript复制const data = await cameraRef.current.takePictureAsync(options);
const photoPath = data.path || data.uri;
if (!photoPath.startsWith('file://')) {
photoPath = 'file://' + photoPath;
}
6.2 录像质量参数问题
OpenHarmony版本的recordAsync方法中,quality参数必须使用数值而非字符串:
javascript复制const VIDEO_QUALITY = {
'2160p': 0,
'1080p': 1,
'720p': 2,
'480p': 3,
'4:3': 4,
};
const options = {
quality: VIDEO_QUALITY['720p'], // 使用数值2
maxDuration: 30,
};
6.3 扫码模式与媒体模式冲突
扫码模式和拍照/录像模式是互斥的,切换时需要重新挂载组件:
javascript复制const [cameraKey, setCameraKey] = useState(0);
const switchMode = (newMode) => {
setMode(newMode);
setCameraKey(prev => prev + 1); // 强制重新挂载组件
};
// 在RNCamera组件中使用key属性
<RNCamera
key={`camera-${cameraKey}-${mode}`}
// ...其他属性
/>
6.4 权限处理
OpenHarmony平台上的权限处理与Android/iOS有所不同,需要注意:
- 确保在module.json5中正确声明了权限
- 首次使用时系统会自动弹出权限请求对话框
- 如果用户拒绝了权限,需要引导用户到设置中手动开启
6.5 录像完成事件监听
在OpenHarmony平台上,录像完成和错误事件通过DeviceEventEmitter监听:
javascript复制useEffect(() => {
const recordingFinishedListener = DeviceEventEmitter.addListener(
'onRecordingFinished',
(video) => {
console.log('录像完成:', video);
setIsRecording(false);
}
);
const recordingErrorListener = DeviceEventEmitter.addListener(
'onRecordingError',
(error) => {
console.log('录像错误:', error);
setIsRecording(false);
}
);
return () => {
recordingFinishedListener.remove();
recordingErrorListener.remove();
};
}, []);
7. 性能优化建议
7.1 相机预览优化
- 根据实际需要设置合适的分辨率,不要盲目追求最高分辨率
- 在不需要时及时释放相机资源
- 避免频繁切换相机模式(拍照/录像/扫码)
7.2 内存管理
- 及时释放不再使用的图片和视频资源
- 对于大尺寸图片,考虑使用缩略图预览
- 定期清理缓存文件
7.3 扫码性能优化
- 限制扫码区域大小,不要全屏扫描
- 添加适当的防抖逻辑,避免重复处理同一二维码
- 在不需要扫码时关闭扫码功能
7.4 视频录制优化
- 根据设备性能选择合适的视频质量
- 设置合理的maxDuration和maxFileSize
- 考虑使用硬件编码加速
8. 兼容性说明
8.1 支持的功能
在OpenHarmony平台上,react-native-camera支持以下核心功能:
- 拍照和录像
- 前后摄像头切换
- 闪光灯控制
- 自动对焦
- 缩放控制
- 曝光和白平衡控制
- 二维码/条形码扫描
8.2 不支持的功能
目前OpenHarmony平台上不支持以下功能:
- 人脸检测
- 未授权视图定制
- 授权中视图定制
- 特定相机比例设置
8.3 设备兼容性
react-native-camera在OpenHarmony平台上的兼容性与设备硬件相关,建议:
- 在应用启动时检测相机可用性
- 提供备用方案处理不支持的功能
- 在不同设备上进行充分测试
通过本文的详细指南,开发者应该能够在OpenHarmony平台上成功集成react-native-camera,并实现丰富的相机功能。如果在实际使用中遇到问题,可以参考官方文档或社区资源寻求帮助。
