Camera2 API横屏拍照画面出现多余区域的解决求助
解决横屏拍照后照片出现多余侧边的问题
你的问题核心在于:TextureView的预览矩阵变换只是改变了画面的显示方式,但拍照时相机仍然捕获的是传感器的完整画面,没有和预览显示的区域做对应适配,所以最终照片会出现多余的侧边区域。下面是具体的解决步骤和代码修改方案:
1. 匹配预览与拍照的尺寸比例
首先要确保拍照选择的JPEG输出尺寸和预览尺寸的比例一致,这样裁剪后不会出现变形或黑边。修改getPicture()里的尺寸选择逻辑:
// 替换原来的width/height选择代码 if (jpegSizes != null && jpegSizes.length > 0) { // 横屏预览时,我们把previewSize的height作为显示宽度,width作为显示高度,所以预览比例是 height/width float previewRatio = (float) previewSize.getHeight() / previewSize.getWidth(); Size bestMatchSize = jpegSizes[0]; float minRatioDifference = Float.MAX_VALUE; // 遍历所有支持的JPEG尺寸,找到和预览比例最接近的 for (Size size : jpegSizes) { // 考虑照片旋转后的比例(因为横屏拍照后照片会旋转90/270度) float currentRatio = (float) size.getWidth() / size.getHeight(); float rotatedRatio = (float) size.getHeight() / size.getWidth(); float diff = Math.abs(currentRatio - previewRatio); float rotatedDiff = Math.abs(rotatedRatio - previewRatio); if (Math.min(diff, rotatedDiff) < minRatioDifference) { minRatioDifference = Math.min(diff, rotatedDiff); bestMatchSize = size; } } width = bestMatchSize.getWidth(); height = bestMatchSize.getHeight(); }
2. 设置相机裁剪区域,捕获预览显示的部分
通过SCALER_CROP_REGION参数让相机只捕获和预览画面一致的区域,消除多余侧边。在getPicture()里创建capturebuilder后添加以下代码:
// 先获取相机传感器的有效区域 CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId()); Rect sensorActiveRect = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); int screenRotation = getWindowManager().getDefaultDisplay().getRotation(); Rect cropRegion = null; if (screenRotation == Surface.ROTATION_90 || screenRotation == Surface.ROTATION_270) { // 计算预览的宽高比(横屏时viewHeight是显示宽度,viewWidth是显示高度) float previewAspectRatio = (float) viewHeight / viewWidth; float sensorAspectRatio = (float) sensorActiveRect.width() / sensorActiveRect.height(); int cropWidth, cropHeight; if (previewAspectRatio > sensorAspectRatio) { // 预览更宽,需要裁剪传感器的高度,保持宽度不变 cropHeight = (int) (sensorActiveRect.width() / previewAspectRatio); cropWidth = sensorActiveRect.width(); } else { // 预览更高,需要裁剪传感器的宽度,保持高度不变 cropWidth = (int) (sensorActiveRect.height() * previewAspectRatio); cropHeight = sensorActiveRect.height(); } // 计算裁剪区域的偏移,让裁剪区域居中 int offsetX = (sensorActiveRect.width() - cropWidth) / 2; int offsetY = (sensorActiveRect.height() - cropHeight) / 2; cropRegion = new Rect(offsetX, offsetY, offsetX + cropWidth, offsetY + cropHeight); // 根据屏幕旋转方向,转换裁剪区域的坐标(适配传感器方向与预览的对应关系) if (screenRotation == Surface.ROTATION_90) { cropRegion = new Rect( sensorActiveRect.height() - cropRegion.bottom, cropRegion.left, sensorActiveRect.height() - cropRegion.top, cropRegion.right ); } else { // ROTATION_270 cropRegion = new Rect( cropRegion.top, sensorActiveRect.width() - cropRegion.right, cropRegion.bottom, sensorActiveRect.width() - cropRegion.left ); } } else { // 非横屏模式直接用传感器完整区域 cropRegion = sensorActiveRect; } // 将裁剪区域设置到捕获请求 capturebuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRegion);
注意:这里的viewWidth和viewHeight需要作为类的成员变量保存下来,在transformImage()方法调用时赋值(比如在onMeasure或transformImage里把这两个值存到成员变量中)。
3. 确认照片方向设置正确
检查你的ORIENTATION映射表是否正确,确保照片旋转方向和预览一致,示例正确的映射表如下:
private static final SparseIntArray ORIENTATION = new SparseIntArray(); static { ORIENTATION.append(Surface.ROTATION_0, 90); ORIENTATION.append(Surface.ROTATION_90, 0); ORIENTATION.append(Surface.ROTATION_180, 270); ORIENTATION.append(Surface.ROTATION_270, 180); }
原理说明
通过以上修改,我们做到了两点:
- 让拍照的输出尺寸比例和预览一致,避免拉伸
- 告诉相机只捕获预览画面显示的那部分区域,这样最终的照片就不会有多余的侧边了
内容的提问来源于stack exchange,提问作者user3810679
相关产品推荐
相关产品推荐

