在 HarmonyOS 中开发相机应用,开发者可以根据业务需求选择两种不同的技术路线:如果仅需获取即时拍摄的照片或视频,推荐使用 CameraPicker(相机选择器) ;如果需要构建高度自定义的相机应用或实现实时预览分析,则需要使用 Camera Kit 核心 API。
以下是这两种方式的完整开发流程与实战代码:
一、 方案一:使用 CameraPicker 快速调用系统相机
CameraPicker 由系统提供交互界面,用户主动点击拍摄并确认后,应用即可获取结果。此方案无需申请相机权限,开发极其简便。
核心代码示例:
javascript
import { camera, cameraPicker as picker } from '@kit.CameraKit';
import { fileIo, fileUri } from '@kit.CoreFileKit';
// 1. 配置 PickerProfile(指定保存路径或默认存入媒体库)
let pickerProfile: picker.PickerProfile = {
cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK,
// 若不配置 saveUri,照片/视频默认存入媒体库
};
// 2. 调用 picker 接口获取拍摄结果
async function getPickerResult(context: Context): Promise<picker.PickerResult> {
let result = await picker.pick(
context,
[picker.PickerMediaType.PHOTO, picker.PickerMediaType.VIDEO],
pickerProfile
);
console.info(`resultCode: ${result.resultCode}, resultUri: ${result.resultUri}`);
return result;
}
二、 方案二:使用 Camera Kit 实现自定义预览与拍照
构建自定义相机需要严格遵循"权限申请 -> 创建输入输出流 -> 会话管理 -> 启动预览"的流程。
1. 权限配置与动态申请
相机属于敏感权限,必须在 module.json5 中声明,并在运行时动态申请:
java
"requestPermissions": [
{ "name": "ohos.permission.CAMERA" }
]
在代码中使用 abilityAccessCtrl.createAtManager().requestPermissionsFromUser() 向用户申请授权。
2. 获取相机设备与创建输入流
javascript
import { camera } from '@kit.CameraKit';
// 获取相机管理器并获取支持的相机设备列表
let cameraManager = camera.getCameraManager(context);
let cameraDevices = cameraManager.getSupportedCameras();
// 获取后置摄像头并创建输入流
let cameraDevice = cameraDevices.find(device =>
device.cameraPosition === camera.CameraPosition.CAMERA_POSITION_BACK
);
let cameraInput = cameraManager.createCameraInput(cameraDevice);
await cameraInput.open(); // 打开相机
3. 创建预览 Surface 与输出流
预览画面必须通过 XComponent 组件渲染,并获取其 surfaceId 绑定到预览输出流:
javascript
// UI 层声明 XComponent
XComponent({
type: XComponentType.SURFACE,
controller: this.xComponentController
})
.onLoad(async () => {
// 获取 surfaceId
let surfaceId = this.xComponentController.getXComponentSurfaceId();
// 获取设备支持的预览分辨率(注意宽高比需与 XComponent 保持一致)
let cameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraDevice);
let previewProfiles = cameraOutputCapability.previewProfiles;
// 创建预览输出流
let previewOutput = cameraManager.createPreviewOutput(previewProfiles[0], surfaceId);
})
4. 会话管理与启动预览
相机的输入流和输出流必须通过 Session 进行绑定和调度:
javascript
// 创建拍照模式的会话
let photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
// 配置会话
photoSession.beginConfig();
photoSession.addInput(cameraInput);
photoSession.addOutput(previewOutput);
await photoSession.commitConfig();
// 启动会话,此时 XComponent 上即可看到实时预览画面
await photoSession.start();
三、 进阶:拍照输出与模式切换
当需要在预览的基础上实现拍照功能时,需在会话中额外添加 PhotoOutput:
javascript
// 创建拍照输出流
let photoOutput = cameraManager.createPhotoOutput();
// 在会话中添加拍照输出流(需先判断是否支持添加)
if (photoSession.canAddOutput(photoOutput)) {
photoSession.addOutput(photoOutput);
}
await photoSession.commitConfig();
// 调用拍照
photoOutput.capture();
如果需要从"拍照模式"切换到"录像模式",可以通过 stop() 停止当前会话,移除 photoOutput,添加 videoOutput 后重新 commitConfig() 并 start()。
1、 高级参数控制(闪光灯、对焦与变焦)
在会话处于 prepared 或运行状态下,开发者可以动态调整相机的各项参数:
- 闪光灯控制 :通过
photoSession.hasFlash()判断设备是否支持闪光灯,若支持,可调用setFlashMode()设置自动、开启或关闭模式。 - 对焦模式 :通过
isFocusModeSupported()检查是否支持连续自动对焦(FOCUS_MODE_CONTINUOUS_AUTO),若支持则调用setFocusMode()启用。 - 平滑变焦 :通过
getZoomRatioRange()获取设备支持的可变焦距比范围,然后调用setZoomRatio()设置具体的缩放比例。注意:传入的缩放值必须在支持范围内,否则系统会直接报错。
2、 折叠屏设备的深度适配
在折叠屏设备上,随着设备的展开或折叠,可用的摄像头可能会发生变化(例如前置镜头被折入内部)。
- 开启自动切换 :通过
isAutoDeviceSwitchSupported()检查设备是否支持自动切换镜头能力。若支持,调用enableAutoDeviceSwitch(true)。系统会自动完成镜头切换、会话配置和参数接续,避免黑屏。 - 监听能力变更 :当系统自动切换镜头时,由于不同镜头的变焦范围可能不一致,系统会通过
on('autoDeviceSwitchStatusChange')回调通知应用。应用需根据isDeviceCapabilityChanged字段重新获取变焦范围并更新 UI。 - UI 布局适配 :除了镜头切换,还需监听折叠状态变化(
foldStatusChange),并配合orientation: follow_desktop动态调整相机的 UI 布局。
3、 严格的资源释放与生命周期管理
相机是极其消耗系统资源的硬件。如果释放不当,极易导致内存泄漏或下次打开相机时黑屏。
- 安全释放顺序 :必须严格按照
stop()->output.release()->cameraInput.close()->session.release()的顺序进行,且每一步都必须包裹在try/catch中,确保即使某一步报错也不会中断后续的释放流程。 - 后台释放机制 :当应用退至后台时,应立即释放相机资源;当应用回到前台时,再重新创建和启动会话。可通过监听
applicationContext.on('applicationStateChange')来实现这一机制。 - 模式切换恢复 :每次切换拍照或录像模式(即创建新 Session)后,所有的参数设置都会回到默认值。必须在模式切换后主动调用类似
syncButtonSettings()的逻辑,将用户的自定义设置重新应用到新会话中。
4、 高级拍照特性与底层细节处理
- HDR 与颜色空间 :若需拍摄高动态范围照片,需在
commitConfig()之前调用session.setColorSpace()设置合适的颜色空间(如DISPLAY_P3或BT2020_HLG_LIMIT)。 - 动图(Moving Photo)支持 :实现类似 Live Photo 的动图功能需满足三个条件:调用
isMovingPhotoSupported()确认支持、调用enableMovingPhoto(true)开启、并在photoAssetAvailable回调中处理生成的资源。 - 图像 Stride 对齐处理 :在进行底层图像数据处理(如 AI 识别)时,需注意
image.Component.rowStride可能大于图像的实际width。如果不进行裁剪对齐处理,解析出的图像会出现竖条纹。