HarmonyOS 鸿蒙Next中在开发自定义相机时,如何解决相机在全屏预览的时候,画面会有变形和拉伸?
HarmonyOS 鸿蒙Next中在开发自定义相机时,如何解决相机在全屏预览的时候,画面会有变形和拉伸?
问题描述:在开发自定义相机功能过程中,拍照完成后需要全屏预览,发现拍照后图片显示全屏的时候会出现变形和拉伸,这里应该怎么处理,才不会显示变形。
问题分析:
如果你在相机开发的时候,设置的预览画面是全屏的尺寸:meta60 2760/1260=2.19, 预览用的相机尺寸是1920/1080=1.777 那么这个预览画面1.77投在xcomponent2.19比例上,必然会拉伸变形;
所以要全屏预览还要不变形:需要先获取手机的宽高比,用手机的屏幕的height/width去和相机底层支持的预览尺寸的 width/height 去取最贴近的值
也就是cameraOutputCapability.previewProfiles的分辨率列表中选择2336/1080 = 2.16 这套参数,两个比值只相差 0.03 最合适
//查找【相机全屏预览宽高】最接近的手机默认分辨率
findClosestNumber(cameraOutputCapability:camera.CameraOutputCapability,profileType:string):camera.Profile|undefined {
let profileArr=cameraOutputCapability.previewProfiles; //预览的分辨率
if(profileType=='PhotoProfile'){
profileArr = cameraOutputCapability.photoProfiles; //相机支持的分辨率列表
}
let screenWidth=display.getDefaultDisplaySync().width
let screenHeight=display.getDefaultDisplaySync().height
let target:number=0;
if(profileArr.length < 1) {
return undefined;
}
if(screenWidth&&screenHeight){
target=screenHeight/screenWidth //全屏幕宽高比例
}
let closest = profileArr[0]; // 初始化最接近的数为数组的第一个元素
let smallestDifference = Math.abs(profileArr[0].size.width/profileArr[0].size.height - target); // 初始化最小差值为第一个元素与目标值的差值
for (let i = 1; i < profileArr.length; i++) {
if(profileArr[i].size.width>=1080){ //避免小分辨率的比例算出来的数据也相临近,只计算分辨率大于1080的
let currentDifference = Math.abs(profileArr[i].size.width/profileArr[i].size.height - target);
if (currentDifference <= smallestDifference) {
smallestDifference = currentDifference;
closest = profileArr[i];
}
}
}
return closest; //返回全屏时候最佳的分辨率
}

更多关于HarmonyOS 鸿蒙Next中在开发自定义相机时,如何解决相机在全屏预览的时候,画面会有变形和拉伸?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,解决相机全屏预览画面变形和拉伸问题,需正确配置预览尺寸与屏幕宽高比。使用CameraKit API,通过getSupportedPreviewSizes()获取支持的预览尺寸列表,选择与屏幕宽高比最匹配的尺寸。然后,在PreviewOutput中设置该尺寸,确保预览视图的布局参数与所选尺寸比例一致。若需适配不同屏幕,可动态计算并应用缩放与裁剪策略。
在HarmonyOS Next开发自定义相机时,解决全屏预览画面变形和拉伸的核心在于正确处理预览流(PreviewOutput)的宽高比与预览显示窗口(XComponent)的宽高比之间的匹配关系。
这是一个典型的宽高比不匹配问题。相机传感器输出的预览图像有固定的宽高比(如4:3、16:9),而你的全屏预览窗口的宽高比通常由设备屏幕决定(如常见的19.5:9)。直接将一种宽高比的图像填充到另一种宽高比的视图中,必然导致拉伸或变形。
解决方案:使用“适配填充”模式
正确的做法不是拉伸图像,而是采用 Image.ClipType 或通过计算进行裁剪适配,确保图像内容区域不变形,同时填充整个屏幕。在HarmonyOS Next中,这通常通过以下步骤实现:
-
获取关键参数:
- 预览流尺寸:从
CameraOutputCapability中获取PreviewOutput支持的预览分辨率(宽高),并选择一个(通常选择与屏幕比例最接近或分辨率最高的)。 - 屏幕/窗口尺寸:获取用于显示预览的
XComponent或Surface的实际宽高。
- 预览流尺寸:从
-
计算适配比例与偏移:
- 比较预览流宽高比与屏幕宽高比。
- 核心逻辑:
- 如果预览流更“胖”(宽高比更大),则预览流的高度需要适配屏幕高度,宽度会超出屏幕。此时需要按高度比例缩放预览流,并计算水平方向的裁剪起始点(
cropStartX)。 - 如果屏幕更“胖”(宽高比更大),则预览流的宽度需要适配屏幕宽度,高度会超出屏幕。此时需要按宽度比例缩放预览流,并计算垂直方向的裁剪起始点(
cropStartY)。
- 如果预览流更“胖”(宽高比更大),则预览流的高度需要适配屏幕高度,宽度会超出屏幕。此时需要按高度比例缩放预览流,并计算水平方向的裁剪起始点(
- 计算公式本质上是求两个比例之间的最大公倍数填充,确保一个方向完全填充,另一个方向居中裁剪。
-
应用至预览输出:
- 在配置
PreviewOutput时,关键一步是设置其Surface(通常来自XComponent)的裁剪区域和变换矩阵。 - 在HarmonyOS Next的相机框架中,你需要通过
PreviewOutput的setSurfaceSize()和setSurfaceOffset()等方法,或通过CameraSession的setDisplayRotation()及setDisplayCrop()等机制(具体API名称可能随版本调整),将计算出的缩放比例、裁剪区域应用到预览画布上。这相当于告诉系统:“请将相机预览流按照这个规则映射到我的显示Surface上”。 - 更常见的做法是,将用于预览的
XComponent的布局和显示模式设置为能够处理这种比例差异。虽然XComponent本身可能不直接提供复杂的缩放模式,但你可以将其嵌入到一个Stack或Flex布局中,并配合Image组件的objectFit属性(如设置为Cover)的类似思想,通过上层容器控制其显示。但更底层的控制仍需通过相机API设置Surface的映射关系。
- 在配置
简要代码思路示意(概念层面):
// 1. 获取预览能力并选择预览分辨率
let previewProfile: camera.PreviewProfile = ... // 从capability中选择一个
let previewRatio = previewProfile.size.width / previewProfile.size.height;
// 2. 获取显示窗口尺寸
let displayWidth = ... // XComponent的宽度
let displayHeight = ... // XComponent的高度
let displayRatio = displayWidth / displayHeight;
// 3. 计算缩放与裁剪
let scale: number;
let cropX: number = 0;
let cropY: number = 0;
if (previewRatio > displayRatio) {
// 预览流更宽,按高度适配
scale = displayHeight / previewProfile.size.height;
let scaledWidth = previewProfile.size.width * scale;
cropX = (scaledWidth - displayWidth) / 2.0 / scale; // 换算回原始预览流坐标
} else {
// 屏幕更宽(或同等),按宽度适配
scale = displayWidth / previewProfile.size.width;
let scaledHeight = previewProfile.size.height * scale;
cropY = (scaledHeight - displayHeight) / 2.0 / scale; // 换算回原始预览流坐标
}
// 4. 将计算出的cropX, cropY, scale等参数应用于PreviewOutput的Surface配置
// 具体API调用需参考最新的HarmonyOS Next Camera Kit文档
previewOutput.setSurfaceCropRegion({ /* 根据计算结果设置矩形区域 */ });
// 或通过设置变换矩阵等方式
总结要点:
- 根源:预览流与显示窗口宽高比不一致。
- 原则:以裁剪代替拉伸,保持图像内容比例正确。
- 关键:计算正确的缩放比例和裁剪起始点,并通过相机API(
PreviewOutput或CameraSession的相关设置)将其应用于预览Surface。 - 后续处理:拍照得到的图片(
PhotoOutput)是完整的传感器输出,其处理方式与预览分离。全屏查看照片时应使用图片查看组件,并同样采用objectFit: Cover或类似模式,避免二次拉伸。
请根据HarmonyOS Next最新的相机API文档,查找与设置预览Surface裁剪、缩放相关的具体方法并实施。

