鸿蒙实战:拍照页 CapturePage 完整实现 鸿蒙实战拍照页 CapturePage 完整实现前言必读图鸿蒙实战拍照页 CapturePage 完整实现第 78 篇 运行效果截图HarmonyOS NEXT拍照页是用户采集手写笔迹的主入口。它支持两种输入模式——Camera Kit 相机拍照 和 PhotoViewPicker 相册选取配合呼吸脉冲动画引导用户操作。本文详细拆解拍照页的完整实现。图CapturePage 双模输入架构——Camera Kit 拍照 PhotoViewPicker 相册的数据流CapturePage相机模式相册模式申请相机权限CameraKit 初始化CaptureSession 配置拍照 takePicturePixelMap 获取PhotoViewPicker 选取读取 URIimage.createImageSourcecreatePixelMap 解码→ 图片裁剪处理→ AnalyzingPage一、页面状态机INITIAL → CAMERA_READY → CAPTURING → PREVIEW → CONFIRMED │ │ │ │ │ ├── 取景框 │ ├── 确认使用 │ │ 呼吸脉冲动画 │ ├── 重拍 → CAMERA_READY │ │ │ │ │ └── 相册按钮 │ └── 保存路径 跳转分析 │ │ ├── 相册选取 │ │ PhotoViewPicker │ │ (非拍照模式) │ └── 返回 │二、State 状态定义StateisCapturing:booleanfalse// 是否正在拍照StatehasPreview:booleanfalse// 是否有预览照片StatepreviewPath:string// 预览照片路径Statesource:camera|albumcamera// 图片来源StateretakeScale:number1.0// 重拍按钮缩放StateconfirmScale:number1.0// 确认按钮缩放三、Camera Kit 相机集成3.1 权限声明// module.json5requestPermissions:[{name:ohos.permission.CAMERA,reason:$string:permission_camera_reason,usedScene:{abilities:[EntryAbility],when:inuse}}]3.2 相机初始化蓝图import{camera}fromkit.CameraKitasyncfunctioninitCamera(surfaceId:string):Promisevoid{constcameraManagercamera.getCameraManager(getContext())// 获取后置摄像头constcamerascameraManager.getSupportedCameras()constbackCameracameras.find(cc.cameraPositioncamera.CameraPosition.CAMERA_POSITION_BACK)if(!backCamera)return// 创建相机输入constcameraInputcameraManager.createCameraInput(backCamera)awaitcameraInput.open()// 创建会话constsessioncameraManager.createCaptureSession()session.beginConfig()// 添加输入session.addInput(cameraInput)// 创建预览输出constpreviewOutputsession.createPreviewOutput(surfaceId)session.addOutput(previewOutput)// 配置完成session.commitConfig()awaitsession.start()}3.3 拍照asyncfunctiontakePicture():Promisestring{// 创建拍照输出constphotoOutputsession.createPhotoOutput()session.addOutput(photoOutput)// 执行拍照constphotoawaitphotoOutput.capture()// 获取图片路径returnphoto.path}四、PhotoViewPicker 相册选取作为相机的备选方案用户也可以从相册选取已有照片asyncfunctionpickFromAlbum():Promisestring|null{constphotoPickernewphotoAccessHelper.PhotoViewPicker()constresultawaitphotoPicker.select({MIMEType:photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE,maxSelectNumber:1})if(resultresult.photoUris.length0){returnresult.photoUris[0]}returnnull}五、呼吸脉冲动画拍照按钮使用两个同心圆环的呼吸脉冲动画引导用户点击StatepulseActive:booleantrueprivatestartPulse():void{constanimate(){if(!this.pulseActive)return// 脉冲环放大 淡出animateTo({duration:1500,curve:Curve.EaseInOut},(){this.pulseScale1.3this.pulseOpacity0})// 重置setTimeout((){animateTo({duration:0},(){this.pulseScale1.0this.pulseOpacity0.3})setTimeout(()animate(),200)},1500)}setTimeout(()animate(),500)}四角引导线设计// 四角 L 型引导线Row(){Column(){// 左上角 LRow(){Column().width(24).height(2).backgroundColor(AppColors.WARM).opacity(0.8)Blank()}.width(100%)Row(){Column().width(2).height(24).backgroundColor(AppColors.WARM).opacity(0.8)Blank()}.width(100%)}.width(100%).height(100%)}六、拍照全流程// 点击拍照按钮privateasynccapture():Promisevoid{this.isCapturingtruetry{constimagePathawaittakePicture()this.previewPathimagePaththis.hasPreviewtrue}catch(e){hilog.error(0x0000,TAG,拍照失败: %{public}s,JSON.stringify(e))this.getUIContext().getPromptAction().showToast({message:拍照失败请重试,duration:1500})}this.isCapturingfalse}// 确认照片privateconfirmPhoto():void{AppStorage.setOrCreatestring(capture_image_path,this.previewPath)this.getUIContext().getRouter().pushUrl({url:pages/AnalyzingPage})}// 重拍privateretakePhoto():void{this.hasPreviewfalsethis.previewPath}七、UI 布局build(){Column(){// 顶部栏返回 标题 相册按钮Row(){Text(✕).onClick(()router.back())Text(拍摄笔迹)Blank()Text(相册).onClick(()this.pickFromAlbum())}if(!this.hasPreview){// 取景模式Stack(){// 相机预览XComponentXComponent({type:surface,controller:this.surfaceController})// 四角引导线CornerGuide()// 拍照按钮含呼吸脉冲环CaptureButton().onClick(()this.capture())}}else{// 预览模式Stack(){Image(this.previewPath).objectFit(ImageFit.Contain)// 底部操作Row(){Button(重拍).onClick(()this.retakePhoto())Button(确认).onClick(()this.confirmPhoto())}}}}}八、完整业务流程用户启动拍照页 ↓ 相机初始化 → 取景框显示 周围呼吸脉冲动画 四角引导线 ↓ 用户点击拍照按钮 / 选择相册 ↓ 照片预览显示 ↓ ┌──────┐ ┌──────┐ │ 重拍 │ │ 确认 │ └──────┘ └──────┘ ↓ ↓ 回到取景 capture_image_path → AppStorage ↓ pushUrl(AnalyzingPage)九、注意事项与常见问题9.1 开发注意事项在正式开发前建议按以下步骤完成环境准备与前置检查版本确认检查 DevEco Studio 与 SDK 版本确保满足目标 API Level 要求权限声明在module.json5的requestPermissions字段中提前声明所有需要的系统权限设备能力检查调用前验证设备是否支持目标能力相机、NFC、传感器等异步封装所有耗时操作数据库、文件 I/O、网络请求统一使用async/await处理资源释放在组件aboutToDisappear()生命周期钩子中及时释放系统资源防止内存泄漏9.2 常见错误与解决方案开发过程中的高频问题汇总错误现象可能原因解决方案权限被系统拒绝未在module.json5中静态声明添加requestPermissions权限配置项页面跳转后白屏路由路径未在main_pages.json注册检查并补充路由配置文件UI 状态不刷新状态变量未添加响应式装饰器为变量加State/Observed装饰器数据库查询返回空查询条件与存储数据格式不匹配使用hilog.debug打印 SQL 条件排查相机预览黑屏运行时相机权限未获取先调用requestPermissionsFromUser申请9.3 调试技巧掌握以下调试方法可大幅提升排错效率使用hilog.info / debug / error分级日志替代console.log方便在 DevEco 日志面板过滤在关键流程入口打印参数快照定位数据异常传递位置数据库操作前后打印 SQL 语句和行数验证增删改查是否符合预期使用 Profiler 工具定位 CPU / 内存热点优化性能瓶颈总结✅双输入模式Camera Kit 拍照 PhotoViewPicker 相册选取✅状态机INITIAL → PREVIEW → CONFIRMED 三态✅呼吸脉冲同心圆环循环脉冲引导操作✅四角引导线WARM 色 L 型框架辅助构图✅流程数据传递capture_image_path 通过 AppStorage 传给 AnalyzingPage下一篇将讲解手写板 WritePage 触摸绘图实现。 收藏提示拍照页是用户手写输入的主要入口Camera Kit PhotoViewPicker 双通道保证最低有一个可用输入方式。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源ArkUI 组件参考animateTo 显式动画AppStorage 全局状态管理relationalStore RDBCamera Kit 相机开发fileIo 文件操作HarmonyOS NEXT 开发指南第34篇 Camera Kit 相机接入七、CapturePage 进阶实现7.1 相机与相册的统一处理流两种输入路径需要统一收口到同一个处理流// 统一图片处理入口privateasyncprocessImage(pixelMap:image.PixelMap){// 1. 裁剪为正方形constcroppedawaitImageCropUtil.cropSquare(pixelMap);// 2. 压缩控制文件大小constcompressedawaitImageCompressUtil.compress(cropped,0.8);// 3. 保存到沙箱constsavedPathawaitFileUtil.saveToSandbox(compressed,capture);// 4. 存入 AppStorage 传递给分析页AppStorage.set(pendingImagePath,savedPath);// 5. 跳转分析页router.pushUrl({url:pages/AnalyzingPage,params:{imagePath:savedPath}});}7.2 取景框的 UI 实现取景框使用叠加层Stack实现Stack(){// 相机预览画面XComponent({id:camera_preview,type:surface}).width(100%).height(100%)// 取景框遮罩四角边框 中央透明Column(){ViewfinderOverlay({size:280})// 自定义组件}.width(100%).height(100%).justifyContent(FlexAlign.Center)// 引导文字Text(将手写内容对准取景框).fontColor(Color.White).position({x:50%,y:85%}).translate({x:-50%})}7.3 错误场景的优雅降级拍照页需要处理多种异常异常类型处理方式相机权限拒绝显示「请前往设置开启相机权限」并引导跳转相机初始化失败隐藏相机区域只显示相册入口选图后读取失败Toast 提示「图片读取失败请重试」沙箱存储不足提示清理空间核心设计原则拍照页是「输入门户」用户体验的核心是快速、准确、容错。优先保证相机预览流畅次之是错误处理的友好提示最后才是视觉装饰效果。7.4 CapturePage 与 WritePage 的对比拍照页CapturePage和手写板WritePage是鹿鹿 App 的两种采集路径对比维度CapturePageWritePage输入方式相机拍照/相册选取手指触摸绘图适合场景已有纸质手写内容直接在设备上书写分辨率质量高受相机约束中受 Canvas 约束操作步骤少一次拍照多多段书写如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力