相机开发:CameraKit调用摄像头与拍照预览(36)

📅 2026/7/24 22:16:31 👁️ 阅读次数 📝 编程学习
相机开发:CameraKit调用摄像头与拍照预览(36)

在 HarmonyOS 中开发相机应用,开发者可以根据业务需求选择两种不同的技术路线:如果仅需获取即时拍摄的照片或视频,推荐使用CameraPicker(相机选择器);如果需要构建高度自定义的相机应用或实现实时预览分析,则需要使用Camera Kit 核心 API

以下是这两种方式的完整开发流程与实战代码:

一、 方案一:使用 CameraPicker 快速调用系统相机

CameraPicker 由系统提供交互界面,用户主动点击拍摄并确认后,应用即可获取结果。此方案无需申请相机权限,开发极其简便。

核心代码示例:

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中声明,并在运行时动态申请:

"requestPermissions": [ { "name": "ohos.permission.CAMERA" } ]

在代码中使用abilityAccessCtrl.createAtManager().requestPermissionsFromUser()向用户申请授权。

2. 获取相机设备与创建输入流
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绑定到预览输出流:

// 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进行绑定和调度:

// 创建拍照模式的会话 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

// 创建拍照输出流 let photoOutput = cameraManager.createPhotoOutput(); // 在会话中添加拍照输出流(需先判断是否支持添加) if (photoSession.canAddOutput(photoOutput)) { photoSession.addOutput(photoOutput); } await photoSession.commitConfig(); // 调用拍照 photoOutput.capture();

如果需要从“拍照模式”切换到“录像模式”,可以通过stop()停止当前会话,移除photoOutput,添加videoOutput后重新commitConfig()start()

1、 高级参数控制(闪光灯、对焦与变焦)

在会话处于prepared或运行状态下,开发者可以动态调整相机的各项参数:

  1. 闪光灯控制:通过photoSession.hasFlash()判断设备是否支持闪光灯,若支持,可调用setFlashMode()设置自动、开启或关闭模式。
  2. 对焦模式:通过isFocusModeSupported()检查是否支持连续自动对焦(FOCUS_MODE_CONTINUOUS_AUTO),若支持则调用setFocusMode()启用。
  3. 平滑变焦:通过getZoomRatioRange()获取设备支持的可变焦距比范围,然后调用setZoomRatio()设置具体的缩放比例。注意:传入的缩放值必须在支持范围内,否则系统会直接报错。
2、 折叠屏设备的深度适配

在折叠屏设备上,随着设备的展开或折叠,可用的摄像头可能会发生变化(例如前置镜头被折入内部)。

  1. 开启自动切换:通过isAutoDeviceSwitchSupported()检查设备是否支持自动切换镜头能力。若支持,调用enableAutoDeviceSwitch(true)。系统会自动完成镜头切换、会话配置和参数接续,避免黑屏。
  2. 监听能力变更:当系统自动切换镜头时,由于不同镜头的变焦范围可能不一致,系统会通过on('autoDeviceSwitchStatusChange')回调通知应用。应用需根据isDeviceCapabilityChanged字段重新获取变焦范围并更新 UI。
  3. UI 布局适配:除了镜头切换,还需监听折叠状态变化(foldStatusChange),并配合orientation: follow_desktop动态调整相机的 UI 布局。
3、 严格的资源释放与生命周期管理

相机是极其消耗系统资源的硬件。如果释放不当,极易导致内存泄漏或下次打开相机时黑屏。

  1. 安全释放顺序:必须严格按照stop()->output.release()->cameraInput.close()->session.release()的顺序进行,且每一步都必须包裹在try/catch中,确保即使某一步报错也不会中断后续的释放流程。
  2. 后台释放机制:当应用退至后台时,应立即释放相机资源;当应用回到前台时,再重新创建和启动会话。可通过监听applicationContext.on('applicationStateChange')来实现这一机制。
  3. 模式切换恢复:每次切换拍照或录像模式(即创建新 Session)后,所有的参数设置都会回到默认值。必须在模式切换后主动调用类似syncButtonSettings()的逻辑,将用户的自定义设置重新应用到新会话中。
4、 高级拍照特性与底层细节处理
  1. HDR 与颜色空间:若需拍摄高动态范围照片,需在commitConfig()之前调用session.setColorSpace()设置合适的颜色空间(如DISPLAY_P3BT2020_HLG_LIMIT)。
  2. 动图(Moving Photo)支持:实现类似 Live Photo 的动图功能需满足三个条件:调用isMovingPhotoSupported()确认支持、调用enableMovingPhoto(true)开启、并在photoAssetAvailable回调中处理生成的资源。
  3. 图像 Stride 对齐处理:在进行底层图像数据处理(如 AI 识别)时,需注意image.Component.rowStride可能大于图像的实际width。如果不进行裁剪对齐处理,解析出的图像会出现竖条纹。

四、 架构分层:MVVM 模式下的相机模块设计

在大型应用中,直接在 UI 组件中处理复杂的相机逻辑会导致代码臃肿且难以维护。建议引入 MVVM 模式进行分层解耦:

  1. View 层(UI 组件)XComponent组件仅负责渲染相机预览画面,不包含任何业务逻辑。它通过@Link@Prop接收来自 ViewModel 的状态(如预览分辨率、闪光灯状态),并通过事件回调通知 ViewModel 用户的交互(如点击拍照、切换摄像头)。
  2. ViewModel 层(状态与逻辑):作为相机模块的核心管理者,ViewModel 持有CameraManagerPhotoSession等核心对象。它处理所有业务逻辑,如权限校验、设备切换、参数调整、模式切换等,并更新状态以驱动 UI 变化。
  3. Model 层(数据源):定义相机模块的数据结构和配置,如支持的分辨率列表、闪光灯模式等,可以来自本地配置或远程接口,实现相机模块的动态化配置。

五、 性能优化:渲染与交互体验

相机应用对性能要求极高,任何卡顿都会严重影响用户体验。

  1. XComponent 渲染优化XComponent的宽高比必须与预览输出的分辨率保持一致,否则会导致画面拉伸或黑边。建议在onLoad回调中动态计算并设置XComponent的尺寸,确保预览画面完美填充。
  2. 异步操作与线程管理:相机的初始化、参数设置、拍照等操作都是耗时的。必须在异步线程中执行这些操作,避免阻塞主线程。可以使用TaskPoolWorker来处理复杂的图像处理任务,确保 UI 的流畅性。
  3. 内存管理:相机预览和拍照会产生大量的图像数据。必须及时释放不再使用的Image对象,避免内存泄漏。在处理图像数据时,建议使用ArrayBuffer的零拷贝特性,减少内存分配和拷贝的开销。

六、 生命周期管理:严格的资源释放与状态恢复

相机是极其消耗系统资源的硬件。如果管理不当,极易导致内存泄漏或下次打开相机时黑屏。

  1. 安全释放顺序:必须严格按照session.stop()->output.release()->cameraInput.close()->session.release()的顺序进行,且每一步都必须包裹在try/catch中,确保即使某一步报错也不会中断后续的释放流程。
  2. 后台释放机制:当应用退至后台时,应立即释放相机资源;当应用回到前台时,再重新创建和启动会话。可通过监听applicationContext.on('applicationStateChange')来实现这一机制。
  3. 模式切换恢复:每次切换拍照或录像模式(即创建新 Session)后,所有的参数设置都会回到默认值。必须在模式切换后主动调用类似syncButtonSettings()的逻辑,将用户的自定义设置重新应用到新会话中。