如何在Android中修复相机预览(surfaceview)的正确宽高比?

时间:2016-12-18 19:44:43

标签: android android-camera surfaceview aspect-ratio

我正在使用相机功能创建Android应用。 摄像机屏幕包含顶部的工具栏,工具栏下方的表面视图(摄像机预览)以及屏幕底部的摄像机控制按钮。屏幕始终处于纵向状态。

[删除与问题无关的某些代码行]

这是我的片段FragmentCamera

import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.dmc.R;
import com.dmc.entities.Preview;

public class CameraFragment implements View.OnClickListener, View.OnTouchListener {

    public static final String ARG_CAMERA_MODE = "camera.mode";
    public static final String TYPE_CAMERA_MODE_IMAGE = "image";
    public static final String TYPE_CAMERA_MODE_VIDEO = "video";
    public MediaRecorder mrec = new MediaRecorder();

    private Camera camera;
    private String mCameraMode = TYPE_CAMERA_MODE_IMAGE; //or video
    private com.dmc.entities.Preview preview;
    private ImageView btnStopRecording;
    private SurfaceView surfaceView;
    private View view;

    public static FrCamera getInstance(String cameraMode) {
        CameraFragment fragment = new CameraFragment();
        Bundle bundle = new Bundle(1);
        bundle.putString(ARG_CAMERA_MODE, cameraMode);
        return fragment.setArguments(bundle);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCameraMode = getArguments().getString(ARG_CAMERA_MODE);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment_camera, container, false);
        view.setOnTouchListener(this);
        btnStopRecording = (ImageView) view.findViewById(R.id.btnStopRecording);
        if (!mCameraMode.equals(TYPE_CAMERA_MODE_IMAGE)) {
            btnStopRecording.setOnClickListener(this);
        }
        surfaceView = (SurfaceView) view.findViewById(R.id.surfaceView);
        view.findViewById(R.id.imgCameraTakePicture).setOnClickListener(this);

        preview = new Preview(getActivity(), (SurfaceView) view.findViewById(R.id.surfaceView));
        preview.setKeepScreenOn(true);
        return view;
    }

    @Override
    public void onStart() {
        super.onStart();

        int numCams = Camera.getNumberOfCameras();
        if (numCams > 0) {
            try {
                camera = Camera.open(0);
                preview.setCamera(camera);
                camera.startPreview();
            } catch (RuntimeException ex) {
            }
        }
    }

    @Override
    public void onPause() {
        if (camera != null) {
            camera.stopPreview();
            preview.setCamera(null);
            camera.release();
            camera = null;
        }
        super.onPause();
    }

    @Override
    public void onResume() {
        super.onResume();
        camera.startPreview();
    }

    private void startVideoRecording() {
        try {
            mrec = new MediaRecorder();
            mrec.setCamera(camera);
            mrec.setVideoSource(MediaRecorder.VideoSource.CAMERA);
            mrec.setAudioSource(MediaRecorder.AudioSource.MIC);
            CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
            mrec.setProfile(profile);
            camera.lock();
            camera.unlock();
            mrec.setPreviewDisplay(preview.mHolder.getSurface());
            mrec.setOutputFile(outVideoFile.getPath());
            mrec.setOrientationHint(Preview.rotate);
            mrec.prepare();
            mrec.start();
        } catch (Exception ex) {
            Log.e(getClass().getName(), ex.getMessage());
        }
    }

    protected void stopRecording() {
        if (mrec != null) {
            mrec.stop();
            mrec.release();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.imgCameraTakenPicture:
                // Save image
                break;
            case R.id.btnStopRecording:
                stopRecording();
                break;
            case R.id.imgCameraTakePicture:
                if (mCameraMode.equals(TYPE_CAMERA_MODE_IMAGE)) {
                    camera.takePicture(shutterCallback, rawCallback, jpegCallback);
                } else
                    startVideoRecording();
                break;
        }
    }
}

这是预览

import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;

import java.io.IOException;
import java.util.List;

public class Preview extends ViewGroup implements SurfaceHolder.Callback {
    public static final float RATIO = 0.75f;
    public static int rotate;

    public SurfaceView mSurfaceView;
    public SurfaceHolder mHolder;
    Size mPreviewSize;
    List<Size> mSupportedPreviewSizes;
    Camera mCamera;

    public Preview(Context context, SurfaceView sv) {
        super(context);
        mSurfaceView = sv;
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void setCamera(Camera camera) {
        mCamera = camera;
        if (mCamera != null) {
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            requestLayout();
            // get Camera parameters
            Camera.Parameters params = mCamera.getParameters();

            List<String> focusModes = params.getSupportedFocusModes();
            if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
            } else {
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            }

            params.setJpegThumbnailQuality(100);
            params.setJpegQuality(100);

            // Configure image format. RGB_565 is the most common format.
            List<Integer> formats = params.getSupportedPictureFormats();
            if (formats.contains(PixelFormat.RGB_565))
                params.setPictureFormat(PixelFormat.RGB_565);
            else if (formats.contains(PixelFormat.JPEG))
                params.setPictureFormat(PixelFormat.JPEG);
            else params.setPictureFormat(formats.get(0));

            Camera.CameraInfo camInfo = new Camera.CameraInfo();
            Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, camInfo);
            int cameraRotationOffset = camInfo.orientation;

            Camera.Parameters parameters = mCamera.getParameters();

            int rotation = ((Activity) getContext()).getWindowManager().getDefaultDisplay().getRotation();
            int degrees = 0;
            switch (rotation) {
                case Surface.ROTATION_0:
                    degrees = 0;
                    break; // Natural orientation
                case Surface.ROTATION_90:
                    degrees = 90;
                    break; // Landscape left
                case Surface.ROTATION_180:
                    degrees = 180;
                    break;// Upside down
                case Surface.ROTATION_270:
                    degrees = 270;
                    break;// Landscape right
            }
            int displayRotation;
            if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                displayRotation = (cameraRotationOffset + degrees) % 360;
                //displayRotation = (360 - displayRotation) % 360; // compensate the mirror
            } else { // back-facing
                displayRotation = (cameraRotationOffset - degrees + 360) % 360;
            }
            mCamera.setDisplayOrientation(displayRotation);

            if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                rotate = (360 + cameraRotationOffset + degrees) % 360;
            } else {
                rotate = (360 + cameraRotationOffset - degrees) % 360;
            }

            parameters.set("orientation", "portrait");
            parameters.setRotation(rotate);

            mCamera.setParameters(params);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed && getChildCount() > 0) {
            final View child = getChildAt(0);

            final int width = r - l;
            final int height = b - t;

            int previewWidth = width;
            int previewHeight = height;
            if (mPreviewSize != null) {
                previewWidth = mPreviewSize.width;
                previewHeight = mPreviewSize.height;
            }

            // Center the child SurfaceView within the parent.
            if (width * previewHeight > height * previewWidth) {
                final int scaledChildWidth = previewWidth * height / previewHeight;
                child.layout((width - scaledChildWidth) / 2, 0,
                        (width + scaledChildWidth) / 2, height);
            } else {
                final int scaledChildHeight = previewHeight * width / previewWidth;
                child.layout(0, (height - scaledChildHeight) / 2,
                        width, (height + scaledChildHeight) / 2);
            }
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
            if (mCamera != null) {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            }
        } catch (IOException exception) {}
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (mHolder.getSurface() == null) {
            return;
        }
        stopPreview();
        setCamera(mCamera);
        startPreview();
        mCamera.startPreview();
    }

    public void startPreview() {
        try {
            if (mCamera != null) {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
            } 
        } catch (Exception e) {}
    }

    public void stopPreview() {
        try {
            if (mCamera != null)
                mCamera.stopPreview();
        } catch (Exception e) {}
    }
}

预期结果在左侧的图像上。获得的结果在右侧的图像上。相机预览被拉伸。如何修复相机预览的正确宽高比? enter image description here

1 个答案:

答案 0 :(得分:0)

Camera1和Android 4-5的源代码:

@Override
protected void onResume() {
    super.onResume();
    camera = Camera.open(CAMERA_ID);
    setPreviewSize();
}

@Override
protected void onPause() {
    super.onPause();
    if (camera != null)
        camera.release();
    camera = null;
}
...
void setPreviewSize() {
    // получаем размеры экрана
    Display display = getWindowManager().getDefaultDisplay();
    int w1 = display.getWidth();
    int h1 = display.getHeight();
    boolean widthIsMax = display.getWidth() > display.getHeight();

    // определяем размеры превью камеры
    Camera.Size size = camera.getParameters().getPreviewSize();

    RectF rectDisplay = new RectF();
    RectF rectPreview = new RectF();

    // RectF экрана, соотвествует размерам экрана
    rectDisplay.set(0, 0, w1, h1);

    // подготовка матрицы преобразования
    Matrix matrix = new Matrix();
    // RectF первью
    if (widthIsMax) {
        // превью в горизонтальной ориентации
        rectPreview.set(0, 0, size.width, size.height);

        // если экран будет "втиснут" в превью (третий вариант из урока)
        matrix.setRectToRect(rectPreview, rectDisplay,
                Matrix.ScaleToFit.START);
    } else {
        // превью в вертикальной ориентации
        rectPreview.set(0, 0, size.height, size.width);

        // если превью будет "втиснут" в экран (второй вариант из урока)
        matrix.setRectToRect(rectPreview, rectDisplay,
                Matrix.ScaleToFit.START);
    }

    // преобразование
    matrix.mapRect(rectPreview);

    // установка размеров surface из получившегося преобразования
    h0 = (int) (rectPreview.bottom);
    w0 = (int) (rectPreview.right);

    surfaceView.getLayoutParams().height = h0;
    surfaceView.getLayoutParams().width = w0;
}

请参阅http://startandroid.ru/ru/uroki/vse-uroki-spiskom/264-urok-132-kamera-vyvod-izobrazhenija-na-ekran-obrabotka-povorota.html