鸿蒙相机开发入门篇(官方实践版)

199 阅读2分钟

鸿蒙相机开发入门篇(官方实践版)

— 从系统能力声明到可运行的拍照预览应用


一、前言

很多刚接触鸿蒙开发的同学,在尝试使用相机时会遇到两个常见问题:

  1. 为什么明明引入了相机模块,却提示“系统能力缺失”?
  2. 为什么写了相机逻辑,却看不到预览画面?

这篇文章将带你从 0 到 1 完成一个能“预览 + 拍照”的相机应用,完全基于 HarmonyOS NEXT 官方最佳实践,并结合 UI 层和交互讲解。


二、基础准备

1. 必要权限与系统能力声明

鸿蒙系统使用 SystemCapability(系统能力标识) 来声明模块功能。
在相机开发中,你必须在 module.json5 文件中声明以下内容:

{
  "module": {
    "name": "entry",
    "requestPermissions": [
      { "name": "ohos.permission.CAMERA" },
      { "name": "ohos.permission.MICROPHONE" },
      { "name": "ohos.permission.WRITE_MEDIA" }
    ],
    "systemCapabilities": [
      "SystemCapability.Multimedia.Camera.Core",
      "SystemCapability.Multimedia.Camera.Basic",
      "SystemCapability.Multimedia.Camera.Metadata"
    ]
  }
}

⚠️ 注意:
鸿蒙要求编译时声明能力,否则构建期就会报错,这点和 Android 运行时动态声明完全不同。


三、创建相机基础结构

在鸿蒙中,Camera 模块的核心接口位于 @ohos.multimedia.camera
下面我们先构建一个基础拍照页面:

// pages/CameraPage.ets
import camera from '@ohos.multimedia.camera';
import media from '@ohos.multimedia.media';
import fileio from '@ohos.fileio';

@Entry
@Component
struct CameraPage {
  controller: camera.CameraManager = camera.getCameraManager(globalThis.context);
  previewOutput?: camera.PreviewOutput;
  photoOutput?: camera.PhotoOutput;
  cameraInput?: camera.CameraInput;

  build() {
    Column() {
      // 预览区域
      if (this.previewOutput) {
        // 使用 SurfaceId 渲染相机画面
        XComponent({ id: 'cameraView', type: XComponentType.SURFACE })
          .onLoad((context) => {
            this.startPreview(context);
          })
          .width('100%')
          .height('80%')
      }

      // 拍照按钮
      Button('拍照')
        .onClick(() => this.takePhoto())
        .width('50%')
        .height(50)
        .backgroundColor('#007DFF')
        .fontColor('#fff')
        .borderRadius(25)
    }
  }

  async startPreview(context) {
    const cameras = this.controller.getSupportedCameras();
    this.cameraInput = this.controller.createCameraInput(cameras[0]);
    this.previewOutput = this.controller.createPreviewOutput(context);
    this.photoOutput = this.controller.createPhotoOutput();

    const session = this.controller.createSession(camera.SessionType.SESSION_TYPE_NORMAL);
    await session.beginConfig();
    session.addInput(this.cameraInput);
    session.addOutput(this.previewOutput);
    session.addOutput(this.photoOutput);
    await session.commitConfig();
    session.start();

    await this.cameraInput.open();
    this.previewOutput.start();
  }

  async takePhoto() {
    if (!this.photoOutput) return;
    const photoPath = `/data/storage/el2/base/photos/${Date.now()}.jpg`;
    await this.photoOutput.capture(photoPath);
    prompt.showToast({ message: '拍照成功!', duration: 1500 });
  }
}

四、代码讲解

模块说明
CameraManager管理相机设备(前置/后置等)
CameraInput表示相机输入(光学传感器)
PreviewOutput负责输出实时预览帧(绑定 UI Surface)
PhotoOutput负责拍照输出(生成 JPEG 文件)
Session管理相机的输入输出会话,必须配置后才能启动

五、UI 层与交互优化

为了让拍照体验更自然,可以加入一个简洁的取景框样式与快门动效:

// 快门动画效果
@State shutterEffect: boolean = false

Button('拍照')
  .onClick(() => {
    this.shutterEffect = true
    setTimeout(() => (this.shutterEffect = false), 150)
    this.takePhoto()
  })
  .scale({ x: this.shutterEffect ? 0.9 : 1.0 })
  .animation({ duration: 150, curve: Curve.EaseInOut })