如何将相机预览设置为所需尺寸的布局

时间:2017-09-08 14:11:57

标签: android camera surfaceview

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

Check out the screenshot

我把相机放在脸前,它应该显示整个脸部。

这是我的surfaceview类:

 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的原理并相应地操纵我的代码。 Anny帮助将不胜感激。

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

您无法对300×200进行硬编码,因为您只能选择一种支持的预览尺寸。但在您的情况下,不同设备的宽度可能会有所不同,宽高比也可能不同。

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

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