且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

理想的surfaceView相机方向和比例(避免预览拉伸)-Android

更新时间:2021-11-22 23:36:03

在您的方法中,您必须选择特定的您要在预览中显示的比率,这会产生不同的结果,具体取决于屏幕尺寸。

In your approach you would have to choose the specific ratio that you want to display in your preview, this will produce results which vary based on the screen size.

您可能需要考虑其他方法,可以定义自己的相机预览如下:

You may want to consider a different approach, you can define your own camera preview as follows:


  • 创建相机预览类:

  • Create a camera preview class:

   public class CameraSourcePreview extends ViewGroup {



        private static final String TAG = "CameraSourcePreview";
        private Context mContext;
        private SurfaceView mSurfaceView;
        private boolean mStartRequested;
        private boolean mSurfaceAvailable;
        private CameraSource mCameraSource;

        private GraphicOverlay mOverlay;

        public CameraSourcePreview(Context context, AttributeSet attrs) {
            super(context, attrs);
            mContext = context;
            mStartRequested = false;
            mSurfaceAvailable = false;

            mSurfaceView = new SurfaceView(context);
            mSurfaceView.getHolder().addCallback(new SurfaceCallback());
            addView(mSurfaceView);
        }

        public void start(CameraSource cameraSource) throws IOException {
            if (cameraSource == null) {
                stop();
            }

            mCameraSource = cameraSource;

            if (mCameraSource != null) {
                mStartRequested = true;
                startIfReady();
            }
        }

        public void start(CameraSource cameraSource, GraphicOverlay overlay) throws IOException {
            mOverlay = overlay;
            start(cameraSource);
        }

        public void stop() {
            if (mCameraSource != null) {
                mCameraSource.stop();
            }
        }

        public void release() {
            if (mCameraSource != null) {
                mCameraSource.release();
                mCameraSource = null;
            }
        }

        private void startIfReady() throws IOException {
            if (mStartRequested && mSurfaceAvailable) {
                mCameraSource.start(mSurfaceView.getHolder());
                if (mOverlay != null) {
                    Size size = mCameraSource.getPreviewSize();
                    int min = Math.min(size.getWidth(), size.getHeight());
                    int max = Math.max(size.getWidth(), size.getHeight());
                    if (isPortraitMode()) {
                        // Swap width and height sizes when in portrait, since it will be rotated by
                        // 90 degrees
                        mOverlay.setCameraInfo(min, max, mCameraSource.getCameraFacing());
                    } else {
                        mOverlay.setCameraInfo(max, min, mCameraSource.getCameraFacing());
                    }
                    mOverlay.clear();
                }
                mStartRequested = false;
            }
        }

        private class SurfaceCallback implements SurfaceHolder.Callback {
            @Override
            public void surfaceCreated(SurfaceHolder surface) {
                mSurfaceAvailable = true;
                try {
                    startIfReady();
                } catch (IOException e) {
                    Log.e(TAG, "Could not start camera source.", e);
                }
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder surface) {
                mSurfaceAvailable = false;
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }
        }

        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            int previewWidth = 320;
            int previewHeight = 240;
            if (mCameraSource != null) {
                Size size = mCameraSource.getPreviewSize();
                if (size != null) {
                    previewWidth = size.getWidth();
                    previewHeight = size.getHeight();
                }
            }

            // Swap width and height sizes when in portrait, since it will be rotated 90 degrees
            if (isPortraitMode()) {
                int tmp = previewWidth;
                previewWidth = previewHeight;
                previewHeight = tmp;
            }

            final int viewWidth = right - left;
            final int viewHeight = bottom - top;

            int childWidth;
            int childHeight;
            int childXOffset = 0;
            int childYOffset = 0;
            float widthRatio = (float) viewWidth / (float) previewWidth;
            float heightRatio = (float) viewHeight / (float) previewHeight;

            // To fill the view with the camera preview, while also preserving the correct aspect ratio,
            // it is usually necessary to slightly oversize the child and to crop off portions along one
            // of the dimensions.  We scale up based on the dimension requiring the most correction, and
            // compute a crop offset for the other dimension.
            if (widthRatio > heightRatio) {
                childWidth = viewWidth;
                childHeight = (int) ((float) previewHeight * widthRatio);
                childYOffset = (childHeight - viewHeight) / 2;
            } else {
                childWidth = (int) ((float) previewWidth * heightRatio);
                childHeight = viewHeight;
                childXOffset = (childWidth - viewWidth) / 2;
            }

            for (int i = 0; i < getChildCount(); ++i) {
                // One dimension will be cropped.  We shift child over or up by this offset and adjust
                // the size to maintain the proper aspect ratio.
                getChildAt(i).layout(
                        -1 * childXOffset, -1 * childYOffset,
                        childWidth - childXOffset, childHeight - childYOffset);
            }

            try {
                startIfReady();
            } catch (IOException e) {
                Log.e(TAG, "Could not start camera source.", e);
            }
        }

        private boolean isPortraitMode() {
            int orientation = mContext.getResources().getConfiguration().orientation;
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                return false;
            }
            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
                return true;
            }

            Log.d(TAG, "isPortraitMode returning false by default");
            return false;
        }

}

在您的layout.xml中添加自定义预览:

In your layout.xml add the custom preview:

<yourpackagename.CameraSourcePreview
    android:id="@+id/preview"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

在活动范围内类,定义相机源方法,然后可以在onCreate()中调用该方法

Inside your activity class, define the camera source method, you can then call the method in onCreate()

/ **
*启动或重新启动摄像机源(如果存在)。如果相机源还不存在
*(例如,因为在创建相机源之前调用了onResume),则在创建相机源时,该名称将再次称为
*。
* /

/** * Starts or restarts the camera source, if it exists. If the camera source doesn't exist yet * (e.g., because onResume was called before the camera source was created), this will be called * again when the camera source is created. */

private void startCameraSource() {
    // check that the device has play services available.
    int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(
            getApplicationContext());
    if (code != ConnectionResult.SUCCESS) {
        Dialog dlg =
                GoogleApiAvailability.getInstance().getErrorDialog(this, code, RC_HANDLE_GMS);
        dlg.show();
    }

    if (mCameraSource != null) {
        try {
            mPreview.start(mCameraSource, mGraphicOverlay);
        } catch (IOException e) {
            Log.e(TAG, "Unable to start camera source.", e);
            mCameraSource.release();
            mCameraSource = null;
        }
    }
}




  • 不要忘记添加以下内容:

  • / **
    *重新启动相机。
    * /

    /** * Restarts the camera. */

@Override
protected void onResume() {
    super.onResume();

    startCameraSource();
}

/ **
*停止照相机。
* /

/** * Stops the camera. */

@Override
protected void onPause() {
    super.onPause();
    mPreview.stop();
}

/**
 * Releases the resources associated with the camera source, the associated detector, and the
 * rest of the processing pipeline.
 */


 @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mCameraSource != null) {
            mCameraSource.release();
        }
    }