17

以下是在 camera2 apis 中使用纹理视图时的屏幕截图。在全屏下,预览会拉伸,但在使用较低分辨率(第二张图像)时可以正常工作。如何在不拉伸的情况下全屏使用此预览。

4

4 回答 4

11

以下答案假设您仅处于纵向模式。

你的问题是

如何在不拉伸的情况下全屏使用预览

让我们把它分解成两件事:

  1. 您希望预览填满屏幕
  2. 预览不能扭曲

首先,您需要知道如果您的设备的视口具有不同的纵横比以及相机提供的任何可用分辨率,那么这在逻辑上是不可能的。

所以我假设你接受裁剪预览。

第 1 步:获取可用分辨率列表

StreamConfigurationMap map = mCameraCharacteristics.get(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
    throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
}
Size[] sizes = map.getOutputSizes(SurfaceTexture.class);

现在,您将获得设备相机的可用分辨率(尺寸)列表。

第 2 步:找到最佳纵横比

这个想法是循环尺寸,看看哪一个最适合。您可能需要编写自己的“最适合”的实现。

我不会在这里提供任何代码,因为我所拥有的与您的用例完全不同。但理想情况下,它应该是这样的:

Size findBestSize (Size[] sizes) {
    //Logic goes here
}

第 3 步:告诉 Camera API 您要使用此尺寸

    //...
    textureView.setBufferSize(bestSize.getWidth(), bestSize.getHeight());
    Surface surface = textureView.getSurface();
    try {
        mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        mPreviewRequestBuilder.addTarget(surface);
        mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                mSessionCallback, null);
    } catch (final Exception e) {
        //...
    }

第 4 步:让您的预览超出您的视口

这与 Camera2 API 无关。SurfaceView我们通过让/TextureView超出设备的视口来“裁剪”预览。

首先将您的SurfaceViewTextureView放在一个RelativeLayout.

在从第 2 步获得纵横比后,使用以下内容将其扩展到屏幕之外。
请注意,在这种情况下,您可能需要在启动相机之前知道此纵横比。

   //Suppose this value is obtained from Step 2.
    //I simply test here by hardcoding a 3:4 aspect ratio, where my phone has a thinner aspect ratio.
    float cameraAspectRatio = (float) 0.75;

    //Preparation
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    int screenWidth = metrics.widthPixels;
    int screenHeight = metrics.heightPixels;
    int finalWidth = screenWidth;
    int finalHeight = screenHeight;
    int widthDifference = 0;
    int heightDifference = 0;
    float screenAspectRatio = (float) screenWidth / screenHeight;

    //Determines whether we crop width or crop height
    if (screenAspectRatio > cameraAspectRatio) { //Keep width crop height
        finalHeight = (int) (screenWidth / cameraAspectRatio);
        heightDifference = finalHeight - screenHeight;
    } else { //Keep height crop width
        finalWidth = (int) (screenHeight * cameraAspectRatio);
        widthDifference = finalWidth - screenWidth;
    }

    //Apply the result to the Preview
    RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) cameraView.getLayoutParams();
    lp.width = finalWidth;
    lp.height = finalHeight;
    //Below 2 lines are to center the preview, since cropping default occurs at the right and bottom
    lp.leftMargin = - (widthDifference / 2);
    lp.topMargin = - (heightDifference / 2);
    cameraView.setLayoutParams(lp);

如果您不关心第 2 步的结果,您实际上可以忽略第 1 步到第 3 步,只需使用那里的库,只要您可以配置其纵横比即可。(看起来这个是最好的,但我还没试过)

我已经使用我的分叉库进行了测试。在不修改我的库的任何代码的情况下,我仅使用第 4 步就设法使预览全屏:

使用第 4 步之前:
在此处输入图像描述

使用第 4 步后:
在此处输入图像描述

拍照后的预览也不会失真,因为预览也超出了您的屏幕范围。
但是输出图像将包含您在预览中看不到的区域,这非常有意义。

Step 1到Step 3的代码一般参考谷歌的CameraView

于 2018-03-23T12:45:11.250 回答
1

这是某些设备上的常见问题。我注意到它主要是在三星上。您可以使用一种技巧来设置您的转换TextureView,使其具有类似于 centerCrop 的ImageView行为

于 2018-03-21T15:06:25.700 回答
0

我也遇到过类似的情况,但是这一行解决了我的问题

view_finder.preferredImplementationMode = PreviewView.ImplementationMode.TEXTURE_VIEW

在你的 xml 中:

<androidx.camera.view.PreviewView
    android:id="@+id/view_finder"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

对于使用 cameraX 的相机实现,您可以参考 https://github.com/android/camera-samples/tree/master/CameraXBasic

于 2020-05-11T19:59:40.093 回答
-1

我知道你的问题是什么。您可能正在尝试这样的事情:

textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int j) {
        cam.startPreview(surfaceTexture, i, j);
        cam.takePicture();
    }

    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { }
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { return false; }
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { }
});
于 2018-03-24T09:41:17.803 回答