HarmonyOS APP《画伴梦工厂》开发第9篇:相机开发实战——调用系统相机拍照
第2.1篇:相机开发实战——调用系统相机拍照
难度:⭐⭐ 进阶
前置知识:第 1.6 篇 页面路由与生命周期
涉及源文件:products/default/src/main/ets/components/CreationComponents.ets
概述
在"画伴梦工厂"中,用户需要拍摄自己的画作,然后将其转化为动画。HarmonyOS 提供了startAbilityForResult接口,让我们可以启动系统相机拍照并获取返回的图片 URI。本文将详细拆解这一流程,涵盖 Want 配置、权限申请、结果回调解析以及父子组件之间的数据同步。
一、相机 Want 动作常量
启动系统相机需要指定一个标准的 Want Action,告诉系统我们要执行"图像捕获"操作:
constCAMERA_WANT_ACTION:string='ohos.want.action.imageCapture';ohos.want.action.imageCapture是 HarmonyOS 预定义的系统公共事件,用于唤起系统相机应用进行拍照。
二、权限申请前置检查
在调用系统相机之前,必须先申请ohos.permission.CAMERA权限。项目中将权限申请抽取为独立的PermissionGuard服务:
import{PermissionGuard,PermissionResult}from'../services/PermissionGuard';privateasyncopenCamera(){constcontext=getContext(this)ascommon.UIAbilityContext;constpermissionResult:PermissionResult=awaitPermissionGuard.requestCamera(context);if(!permissionResult.granted){this.noticeText=permissionResult.message;return;}// ... 继续打开相机}PermissionGuard.requestCamera内部调用abilityAccessCtrl.createAtManager().requestPermissionsFromUser,向用户弹出权限请求对话框。若用户拒绝,会通过noticeText提示用户前往设置中开启权限。
// PermissionGuard.etsstaticasyncrequestCamera(context:common.UIAbilityContext):Promise<PermissionResult>{returnPermissionGuard.request(context,['ohos.permission.CAMERA'],'请在设置里打开相机权限后继续');}三、启动系统相机
权限通过后,通过 UIAbilityContext 的startAbilityForResult方法启动系统相机:
context.startAbilityForResult({action:CAMERA_WANT_ACTION}).then((result:common.AbilityResult)=>{consturi=this.getImageUriFromResult(result);this.capturePhoto(uri,'拍照图片');if(uri!==''){this.noticeText='拍照完成,已用新图片覆盖当前预览';}}).catch(()=>{this.noticeText='相机未成功返回,已载入示例画作便于继续流程';this.capturePhoto();});关键点解析
| 要点 | 说明 |
|---|---|
| startAbilityForResult | 启动系统 Ability 并异步等待返回结果,适用于需要获取返回数据的场景 |
| Want对象 | 只需传入action字段,无需指定uri或type,系统相机自动处理 |
| Promise 回调 | then接收拍照成功的结果,catch处理用户取消或相机异常的情况 |
| 兜底策略 | 即使拍照失败,也会调用capturePhoto()(无参数),让流程可以继续 |
这种"兜底策略"设计非常巧妙——即使用户没有成功拍照,仍会使用示例画作填充,保证用户体验不中断。
四、解析相机返回结果
系统相机返回的AbilityResult结构因设备型号和系统版本而异,为了兼容不同机型,getImageUriFromResult方法实现了多层 fallback 解析:
privategetImageUriFromResult(result:common.AbilityResult):string{// 第一层:直接获取 want.uriif(result.want&&result.want.uri){returnresult.want.uri;}// 第二层:当 want.uri 为空时,从 parameters 中查找if(!result.want||!result.want.parameters){return'';}constparameters=result.want.parameters;consturiKeys:string[]=['uri','imageUri','resourceUri','select-item-list'];for(leti=0;i<uriKeys.length;i++){constvalue=parameters[uriKeys[i]];if(typeofvalue==='string'){returnvalue;}if(Array.isArray(value)&&value.length>0&&typeofvalue[0]==='string'){returnvalue[0];}}return'';}解析策略说明
result.want.uri(优先级最高):部分系统版本会将图片 URI 直接放在want对象的顶层uri字段。result.want.parameters遍历(兼容模式):在不同 HarmonyOS 版本上,返回的 key 名可能不同,代码尝试了uri、imageUri、resourceUri、select-item-list四种常见 key 名。- 值类型兼容:同一个 key 在某些版本上是字符串,在某些版本上是数组(单选时数组长度为 1),代码对两种类型都做了处理。
- 兜底返回空字符串:所有查找方式均失败时返回空,上层逻辑会载入示例画作。
五、拍照后的统一处理
无论来自相机还是相册的图片,最终都走capturePhoto方法统一处理:
privatecapturePhoto(uri:string='',sourceLabel:string='拍照图片'){if(uri!==''){this.capturedImageUri=uri;this.imageSourceLabel=sourceLabel;}this.hasPhoto=true;this.activeStep=1;// 进入"准备图片"步骤this.generationProgress=35;// 更新进度条this.noticeText='已采集画作,可以直接生成动画';}该方法做了三件事:
- 存储图片信息:保存 URI 和来源标签(“拍照图片"或"相册图片”)
- 更新状态:标记
hasPhoto = true,推进步骤到第 1 步 - 反馈用户:更新进度值(35%)和提示文字
六、子组件与父组件通信 (@Link)
PhotoRecognitionComponent组件将内部状态通过@Link装饰器暴露给父组件PhotoRecognitionPage,实现双向数据同步:
子组件定义
@Componentexportstruct PhotoRecognitionComponent{@LinkgenerationProgress:number;@LinknoticeText:string;@StateprivatehasPhoto:boolean=false;@StateprivatecapturedImageUri:string='';// ...}@Link:与父组件共享状态,子组件对generationProgress和noticeText的修改会直接反映到父组件 UI 上。@State:组件私有状态,仅在组件内部可见,对外部透明。
父组件调用
PhotoRecognitionComponent({generationProgress:$generationProgress,noticeText:$noticeText})父组件通过$语法传递状态变量的引用,子组件即可通过@Link接收并双向绑定。
七、用户界面触发
在 UI 层,用户通过点击"拍照采集"按钮触发整个流程:
Button('拍照采集').onClick(()=>{if(!this.recognizing){this.openCamera();}})同时检查recognizing状态,避免在生成动画过程中重复触发相机操作。
八、完整流程时序图
用户点击"拍照采集" │ ▼ PermissionGuard.requestCamera() │ ├── 用户拒绝 → noticeText 提示 → 结束 │ └── 用户授权 │ ▼ startAbilityForResult({ action: 'ohos.want.action.imageCapture' }) │ ├── 成功 → getImageUriFromResult() 提取 URI → capturePhoto(uri) │ └── 失败/取消 → capturePhoto()(载入示例画作) │ ▼ hasPhoto = true activeStep = 1 generationProgress = 35总结
本文通过"画伴梦工厂"的相机拍照功能,完整演示了 HarmonyOS 系统相机调用的最佳实践:
| 知识点 | 实现方式 |
|---|---|
| 启动系统相机 | startAbilityForResult+ohos.want.action.imageCapture |
| 权限申请 | PermissionGuard.requestCamera封装 |
| 结果解析 | 多层 fallback 解析AbilityResult.want |
| 状态同步 | @Link双向绑定父子组件数据 |
| 异常兜底 | catch 中自动载入示例画作 |
下一节我们将探讨另一种图片采集方式——通过PhotoViewPicker从相册中选择图片,以及它相对相机模式的不同场景和权限模型差异。
