文章目录什么是 preconfig支持的档位使用流程完整的 CameraService 实现页面里加个画质选择器录像模式下也可以用preconfig vs 手动配置代码量对比几个注意点写在最后写过相机分辨率适配代码的人都知道这有多烦要遍历设备支持的所有 profile筛宽高比防止拉伸预览/拍照/录像还要分别处理不同手机支持的分辨率也不一样……光分辨率配置就能写几百行。后来发现了 preconfig代码量直接缩到 6 行。什么是 preconfigpreconfig预配置是 HarmonyOS 的一键设定画质档位方案你只需要告诉系统我要 1080P 的画质系统自动帮你选好预览/拍照/录像的最优分辨率组合不同手机会自动适配类比一下就像点外卖时选小份/中份/大份不需要你指定我要 200g 鸡腿、50g 米饭……支持的档位// 画质档位enumPreconfigType{PRECONFIG_720P0,// 720×1280省流量速度快PRECONFIG_1080P1,// 1080×1920日常使用推荐PRECONFIG_4K2,// 2160×3840高清但文件大PRECONFIG_HIGH_QUALITY3// 最高画质支持 HDR色彩最丰富}// 画面比例enumPreconfigRatio{PRECONFIG_RATIO_1_10,// 1:1 方形适合头像、朋友圈PRECONFIG_RATIO_4_31,// 4:3传统照片比例PRECONFIG_RATIO_16_92// 16:9全面屏手机默认比例}组合起来就是你说要1080P 16:9系统帮你配好一切。使用流程关键点preconfig必须在beginConfig之前调用。完整的 CameraService 实现这里为了避免命名冲突 在文件名后面多加一个D 注意区分// model/CameraServiceD.etsimport{camera}fromkit.CameraKit;import{BusinessError}fromkit.BasicServicesKit;import{display}fromkit.ArkUI;constTAGCameraService;classCameraServiceD{privatecameraManager:camera.CameraManager;privatecameraInput:camera.CameraInput|undefinedundefined;privatepreviewOutput:camera.PreviewOutput|undefinedundefined;privatephotoOutput:camera.PhotoOutput|undefinedundefined;privatesession:camera.PhotoSession|undefinedundefined;constructor(context:Context){this.cameraManagercamera.getCameraManager(context);}/** * 用 preconfig 初始化相机指定画质档位和画面比例 */asyncinitCameraWithPreconfig(surfaceId:string,preconfigType:camera.PreconfigType,configRatio:camera.PreconfigRatio):Promisevoid{try{awaitthis.releaseAll();constcamerasthis.cameraManager.getSupportedCameras();constdevicecameras.find(dd.cameraPositioncamera.CameraPosition.CAMERA_POSITION_BACK);if(!device)return;this.cameraInputthis.cameraManager.createCameraInput(device);awaitthis.cameraInput.open();// 不传 profile让 preconfig 决定分辨率this.previewOutputthis.cameraManager.createPreviewOutput(surfaceId);this.photoOutputthis.cameraManager.createPhotoOutput();awaitthis.buildSessionWithPreconfig(this.cameraInput,this.previewOutput,this.photoOutput,preconfigType,configRatio);}catch(e){console.error(TAG,initCameraWithPreconfig failed:${JSON.stringify(e)});}}privateasyncbuildSessionWithPreconfig(cameraInput:camera.CameraInput,previewOutput:camera.PreviewOutput,photoOutput:camera.PhotoOutput,preconfigType:camera.PreconfigType,configRatio:camera.PreconfigRatio):Promisevoid{constsessionthis.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO)ascamera.PhotoSession;// 先检查是否支持不检查直接调 preconfig 会抛异常letisSupportedfalse;try{isSupportedsession.canPreconfig(preconfigType,configRatio);}catch(e){console.error(TAG,canPreconfig error:${(easBusinessError).code});return;}if(!isSupported){console.warn(TAG,preconfig not supported, should fallback to manual config);return;}// 一行搞定分辨率配置session.preconfig(preconfigType,configRatio);console.info(TAG,preconfig applied: type${preconfigType}, ratio${configRatio});// 之后按正常流程走session.beginConfig();session.addInput(cameraInput);session.addOutput(previewOutput);session.addOutput(photoOutput);awaitsession.commitConfig();// 顺手设一下预览旋转角try{constdisplayRotationdisplay.getDefaultDisplaySync().rotation;constimageRotationdisplayRotation*camera.ImageRotation.ROTATION_90;constpreviewRotationpreviewOutput.getPreviewRotation(imageRotation);previewOutput.setPreviewRotation(previewRotation,false);}catch(e){console.error(TAG,setPreviewRotation failed);}awaitsession.start();this.sessionsession;console.info(TAG,preconfig session started);}asyncreleaseAll():Promisevoid{awaitthis.session?.stop();awaitthis.previewOutput?.release();awaitthis.photoOutput?.release();awaitthis.cameraInput?.close();awaitthis.session?.release();this.sessionundefined;this.previewOutputundefined;this.photoOutputundefined;this.cameraInputundefined;}}exportdefaultCameraServiceD;页面里加个画质选择器// pages/CameraServicePage.etsimportCameraServiceDfrom../model/CameraServiceD;import{camera}fromkit.CameraKit;EntryComponentstruct CameraServicePage{privatepreconfigOptions:SelectOption[][{value:720P 省流量},{value:1080P 推荐},{value:4K 高清},{value:最高画质 HDR}];privateratioOptions:SelectOption[][{value:1:1 方形},{value:4:3 传统},{value:16:9 全屏}];StateselectedPreconfig:number1;// 默认 1080PStateselectedRatio:number2;// 默认 16:9privatemXComponentController:XComponentControllernewXComponentController();privatesurfaceId:string;privateCameraServiceD:CameraServiceD|undefinedundefined;privategetPreconfigType():camera.PreconfigType{constmap[camera.PreconfigType.PRECONFIG_720P,camera.PreconfigType.PRECONFIG_1080P,camera.PreconfigType.PRECONFIG_4K,camera.PreconfigType.PRECONFIG_HIGH_QUALITY];returnmap[this.selectedPreconfig]??camera.PreconfigType.PRECONFIG_1080P;}privategetPreconfigRatio():camera.PreconfigRatio{constmap[camera.PreconfigRatio.PRECONFIG_RATIO_1_1,camera.PreconfigRatio.PRECONFIG_RATIO_4_3,camera.PreconfigRatio.PRECONFIG_RATIO_16_9];returnmap[this.selectedRatio]??camera.PreconfigRatio.PRECONFIG_RATIO_16_9;}privateasyncreinitCamera():Promisevoid{constcontextthis.getUIContext().getHostContext()!;this.CameraServiceDnewCameraServiceD(context);awaitthis.CameraServiceD.initCameraWithPreconfig(this.surfaceId,this.getPreconfigType(),this.getPreconfigRatio());}build(){Column(){// 顶部画质和比例选择Row({space:16}){Text(画质:).fontSize(14).fontColor(Color.White)Select(this.preconfigOptions).selected(this.selectedPreconfig).onSelect(async(index){this.selectedPreconfigindex;if(this.surfaceId)awaitthis.reinitCamera();})Text(比例:).fontSize(14).fontColor(Color.White)Select(this.ratioOptions).selected(this.selectedRatio).onSelect(async(index){this.selectedRatioindex;if(this.surfaceId)awaitthis.reinitCamera();})}.width(100%).padding({left:16,top:8,right:16,bottom:8})// 相机预览XComponent({type:XComponentType.SURFACE,controller:this.mXComponentController}).width(100%).layoutWeight(1).onLoad(async(){this.surfaceIdthis.mXComponentController.getXComponentSurfaceId();awaitthis.reinitCamera();})}.width(100%).height(100%).backgroundColor(Color.Black)}}录像模式下也可以用asyncfunctionbuildVideoSessionWithPreconfig(cameraManager:camera.CameraManager,cameraInput:camera.CameraInput,previewOutput:camera.PreviewOutput,videoOutput:camera.VideoOutput,preconfigType:camera.PreconfigType,configRatio:camera.PreconfigRatio):Promisevoid{constsessioncameraManager.createSession(camera.SceneMode.NORMAL_VIDEO)ascamera.VideoSession;if(!session.canPreconfig(preconfigType,configRatio))return;session.preconfig(preconfigType,configRatio);session.beginConfig();session.addInput(cameraInput);session.addOutput(previewOutput);session.addOutput(videoOutput);awaitsession.commitConfig();awaitsession.start();}preconfig vs 手动配置代码量对比手动配置分辨率传统方式大概要写这么多// 手动配置遍历 profiles匹配宽高比选最优分辨率……constcapabilitycameraManager.getSupportedOutputCapability(device,sceneMode);constpreviewProfilescapability.previewProfiles;letoptimalProfile:camera.Profile|undefined;letmaxHeight0;for(constprofileofpreviewProfiles){if(profile.format!1003)continue;constratioprofile.size.width/profile.size.height;if(Math.abs(ratio-targetRatio)0.2)continue;if(profile.size.heightmaxHeight){optimalProfileprofile;maxHeightprofile.size.height;}}// 还需要对 photoProfiles 和 videoProfiles 做同样的事……preconfig 方式// 就这两行其他不管if(session.canPreconfig(camera.PreconfigType.PRECONFIG_1080P,camera.PreconfigRatio.PRECONFIG_RATIO_16_9)){session.preconfig(camera.PreconfigType.PRECONFIG_1080P,camera.PreconfigRatio.PRECONFIG_RATIO_16_9);}几个注意点必须先canPreconfig再preconfig跳过检查直接调会抛异常别省这一行。preconfig要在beginConfig之前调用顺序不能乱。旧设备可能不支持API 20 以下的设备canPreconfig可能返回false要做 fallback 处理。切换画质需要重建 Session调了preconfig后需要重新commitConfigstart不能在 Session 运行中途切换。写在最后preconfig 真的让我省了很多时间。如果不是需要精确控制分辨率比如 AI 摄像头要求特定输入尺寸直接用 preconfig 就好了。嗯这里得说下 哪个尺寸设计有点问题 各位同学在写的时候注意下哈 我是真的不想调试了