【问题标题】:How to set camera preview to a layout of desired dimensions如何将相机预览设置为所需尺寸的布局
【发布时间】:2017-09-08 14:11:56
【问题描述】:

我在屏幕顶部有一个具有特定高度和宽度的相机预览。我正在使用前置摄像头生成预览。相机正在工作,但问题是预览显示了相机看到的底部。

我将相机正对着脸部,它应该可以显示整个脸部。

这是我的表面视图类:

 public class MyCameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder mHolder;
    private Camera mCamera;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Camera.Size mPreviewSize;

    public MyCameraSurfaceView(Context context, Camera camera) {
        super(context);
        mCamera = camera;


        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
          for(Camera.Size str: mSupportedPreviewSizes)
            Log.e("Sizes", str.width + "/" + str.height);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int weight,
                               int height) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.

        Log.e("Surface changed", "surfaceChanged => w=" + weight + ", h=" + height);

        if (mHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // make any resize, rotate or reformatting changes here

        // start preview with new settings
        try {
            Camera.Parameters parameters = mCamera.getParameters();
          //  parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);

            mCamera.setParameters(parameters);
            mCamera.setPreviewDisplay(mHolder);
            //mediaRecorder.setOrientationHint(90);
            mCamera.setDisplayOrientation(90);
            mCamera.startPreview();

        } catch (Exception e) {
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.setDisplayOrientation(90);
            mCamera.startPreview();
        } catch (IOException e) {
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }

        if (mPreviewSize != null) {
            float ratio;
            if (mPreviewSize.height >= mPreviewSize.width)
                ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
            else
                ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;

            // One of these methods should be used, second method squishes preview slightly
            setMeasuredDimension(width, (int) (width * ratio));
            //        setMeasuredDimension((int) (width * ratio), height);
        }
    }

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

        if (sizes == null)
            return null;

        Camera.Size optimalSize = null;

        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.height / size.width;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;

            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }

        return optimalSize;
    }

}

还有xml:

 <FrameLayout
    android:id="@+id/videoview"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:layout_marginTop="10dp"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"

    >
</FrameLayout>

我尝试手动将值设置为 parameters.setPreviewSize(300,200) 但它只是在预览中显示黑屏。我是 android 中的相机 API 的新手,不幸的是,我没有时间阅读谷歌文档以了解相机 API 的原理并相应地操作我的代码。 任何帮助将不胜感激。

【问题讨论】:

    标签: android camera surfaceview


    【解决方案1】:

    您可以尝试将 setMeasuredDimension 参数更改为 onMeasure 方法。

    【讨论】:

    • 它正在做这项工作,但正在压缩预览。就像它将相机看到的整个高度压缩到参数中提到的高度一样。
    【解决方案2】:

    您不能硬编码 300×200,因为您只能选择一种受支持的预览尺寸。但在您的情况下,不同设备的宽度可能不同,纵横比也可能不同。

    实际上,选择 300×200 并不好还有一个原因:即使您的预览高度硬编码为 200,它是 200dp,但相机预览适用于真实像素。当您的预览尺寸不小于屏幕上预览表面的(实际像素)尺寸时,可获得最佳效果(无像素化)。

    MyCameraSurfaceView 类的代码看起来不错。问题是如何将它插入到videoview 框架中。您应该将布局参数设置为Gravity.CENTER。这将使您的预览从顶部和底部均匀裁剪。如果您想发送所有像素进行显示,您可以选择扭曲图像或保留边距(类似于 youtube 显示窄视频的方式)。

    【讨论】:

    • 为 Gravity.center 设置参数没有做任何事情。我只需要相机预览显示的内容比现在显示的要少。这可以通过做其他事情来实现吗?
    • 你可能做错了重力。你能透露你的代码吗?
    • 嗯,我还有另一种解决方法。我增加了预览的高度并显示了良好的预览。我遇到的另一个问题是当我开始记录预览更改(调整了分辨率)并且预览出现拉伸时。你知道为什么会这样吗?表面是否正在重新创建?如果是这样,即使在媒体录制开始后,我如何才能保持相同的预览?
    • 您单独设置 mediarecorder 的大小。确保使用相同的尺寸。
    • 我尝试使用多种组合。录制开始时预览会失真。是否可以在不使用 mediaRecorder.setPreviewDisplay() 的情况下将现有的相机预览保留在表面上并开始媒体录制?因为根据谷歌文档,如果你的表面不为空,你不需要设置预览显示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-07
    相关资源
    最近更新 更多