在安卓中正确设置纵横比

3

我正在开发一个应用程序,它正在在SurfaceView上绘制摄像头。一开始,我遇到了视图显示不正确的问题。因此,我使用了以下方法来纠正纵横比问题:

注意:这种方法被发现在多个地方用于解决Android相机的宽高比问题。

private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio;

    // Check wheter is portrait or landscape
    if (orientation == 1)
        targetRatio = (double) h / w;
    else
        targetRatio = (double) w / h;

    if (sizes == null)
        return null;

    Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    // Try to find an size match aspect ratio and size
    for (Size size : sizes) {
        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
            continue;
        if (Math.abs(size.height - targetHeight) < minDiff) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }

    // Cannot find the one match the aspect ratio, ignore the requirement
    if (optimalSize == null) {
        minDiff = Double.MAX_VALUE;
        for (Size size : sizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }
    return optimalSize;
}

这个方法在我的相机上运行得非常好,而且相机也被正确显示了。但是我在新设备上发现了问题,例如LG G3。它的分辨率被认为是最合适的,但是在纵向模式下,它会显示带有柱形黑边的图像,就像下面展示的图片一样:

enter image description here

为什么会出现这种情况?如何解决纵向模式下的这个柱形黑边问题?

您可以选择裁剪图像而不是显示黑色边距。 - Alex Cohn
如何实现这个?那将是我的解决方案之一。第一种方法,裁剪图像。第二种方法,尝试根据宽度将其适应所拥有的空间。 - Sonhja
可靠的裁剪预览的方法涉及使用SurfaceTexture - Alex Cohn
2个回答

3
除非代码中存在错误,否则您将获得最佳结果。
手机相机的纵横比不能保证与手机屏幕匹配。
只是决定什么是最佳结果的标准问题。该代码是谷歌建议的用于决定在手机屏幕上显示的最佳相机分辨率的方法。
您可能不同意谷歌的观点。在这种情况下,您需要编写自己的算法来确定最佳相机分辨率。您可以优化纵横比,或者优化宽度或高度的分辨率。您可以尝试适应屏幕,也可以裁剪图像的部分以填充整个屏幕。由您决定。
注意:您发布的代码尝试找到与屏幕纵横比相差不超过10%且高度最接近屏幕的相机分辨率。如果没有相机分辨率与屏幕纵横比相差不超过10%,则选择高度最接近屏幕的相机分辨率。在这种情况下,您将在黑色条形区域周围拥有显著的黑色边框。

你的回答帮助我找到了解决方案。我使用宽度和高度进行了调整,现在它按照我的期望工作了! - Sonhja

1
在我的情况下,我在同一个活动中有其他视图,因此我想首先修复相机预览布局的尺寸。在布局中,您需要给出最大的期望尺寸(权重)。
我创建了另一种方法,可以确定任何相机的最佳预览大小,给定目标视图当前的宽度和高度以及活动方向:
public static Size getOptimalPreviewSize(List<Camera.Size> cameraPreviewSizes, int targetWidth, int targetHeight, boolean isActivityPortrait) {
    if (CommonUtils.isEmpty(cameraPreviewSizes)) {
        return null;
    }

    int optimalHeight = Integer.MIN_VALUE;
    int optimalWidth = Integer.MIN_VALUE;

    for (Camera.Size cameraPreviewSize : cameraPreviewSizes) {
        boolean isCameraPreviewHeightBigger = cameraPreviewSize.height > cameraPreviewSize.width;
        int actualCameraWidth = cameraPreviewSize.width;
        int actualCameraHeight = cameraPreviewSize.height;

        if (isActivityPortrait) {
            if (!isCameraPreviewHeightBigger) {
                int temp = cameraPreviewSize.width;
                actualCameraWidth = cameraPreviewSize.height;
                actualCameraHeight = temp;
            }
        } else {
            if (isCameraPreviewHeightBigger) {
                int temp = cameraPreviewSize.width;
                actualCameraWidth = cameraPreviewSize.height;
                actualCameraHeight = temp;
            }
        }

        if (actualCameraWidth > targetWidth || actualCameraHeight > targetHeight) {
            // finds only smaller preview sizes than target size
            continue;
        }

        if (actualCameraWidth > optimalWidth && actualCameraHeight > optimalHeight) {
            // finds only better sizes
            optimalWidth = actualCameraWidth;
            optimalHeight = actualCameraHeight;
        }
    }

    Size optimalSize = null;

    if (optimalHeight != Integer.MIN_VALUE && optimalWidth != Integer.MIN_VALUE) {
        optimalSize = new Size(optimalWidth, optimalHeight);
    }

    return optimalSize;
}

这里使用了自定义的Size对象,因为Android的Size只能在API 21之后使用。

public class Size {

    private int width;
    private int height;

    public Size(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

}

通过监听视图的全局布局更改,可以确定其宽度和高度,然后可以设置新的尺寸。这还展示了如何以编程方式确定活动的方向:

cameraPreviewLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                // gets called after layout has been done but before display.
                cameraPreviewLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);

                boolean isActivityPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
                Size optimalCameraPreviewSize = CustomUtils.getOptimalPreviewSize(cameraPreview.getCameraSizes(), cameraPreviewLayout.getWidth(), cameraPreviewLayout.getHeight(), isActivityPortrait);
                if (optimalCameraPreviewSize != null) {
                    LinearLayout.LayoutParams cameraPreviewLayoutParams = new LinearLayout.LayoutParams(optimalCameraPreviewSize.getWidth(), optimalCameraPreviewSize.getHeight());
                    cameraPreviewLayout.setLayoutParams(cameraPreviewLayoutParams);
                }
            }
        });

你还需要旋转相机方向,这是另一个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接