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

时间:2016-12-05 12:45:30

标签: android camera orientation surfaceview

我已经开始了一个项目,其中包含一个摄影机surfaceView(用于在屏幕上进行实时图像预览),从一开始我就认为在实现这一点时不应该有很多问题,但是现在我和#39;因为预览方向和它的比例而陷入困境,我一直在搜索Stack关于表面视图方向和拉伸问题的主题,但我无法获得好处根本就是他们的结果。

我想做的是以相同的角度获取预览,无论手机是否旋转,所以用户可以一直看到他的脸。

Example picture

如果你能告诉我如何改变我的代码以避免在portret和风景视图中预览拉伸,我会很高兴。

我正在上面修改我的代码

public class CameraView extends SurfaceView implements SurfaceHolder.Callback {


private SurfaceHolder mHolder;
private Camera mCamera;


public CameraView (Context context, Camera camera){
    super(context);
    mCamera = camera;
    mCamera.setDisplayOrientation(90);


    //get the holder and set this class as the callback, so we can get camera data here
    mHolder = getHolder();
    mHolder.addCallback(this);
    //mHolder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL); 
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
    try {
        Camera.Parameters parameters = mCamera.getParameters();
        if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
            parameters.set("orientation", "portrait");
            mCamera.setDisplayOrientation(90);
            parameters.setRotation(90);
        }
        else {
            // This is an undocumented although widely known feature
            parameters.set("orientation", "landscape");
            // For Android 2.2 and above
            mCamera.setDisplayOrientation(0);
            // Uncomment for Android 2.0 and above
            parameters.setRotation(0);
        }
        mCamera.setPreviewDisplay(surfaceHolder);
        mCamera.startPreview();

    } catch (IOException e) {
        // left blank for now
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    this.getHolder().removeCallback(this); 
    mCamera.stopPreview();
    mCamera.release();
}

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

    try {
        Camera.Parameters parameters = mCamera.getParameters();
        if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
            parameters.set("orientation", "portrait");
            mCamera.setDisplayOrientation(90);
            parameters.setRotation(90);
            setCamera(mCamera);
            mCamera.setPreviewDisplay(surfaceHolder);
            mCamera.startPreview();
        }
        else {

            parameters.set("orientation", "landscape");
            mCamera.setDisplayOrientation(0);
            parameters.setRotation(0);
        }
        setCamera(mCamera);
        mCamera.setPreviewDisplay(surfaceHolder);
        mCamera.startPreview();

    } catch (IOException e) {

    }
}

public void setCamera(Camera camera){ 
    mCamera = camera;  }               

1 个答案:

答案 0 :(得分:0)

在您的方法中,您必须选择要在预览中显示的特定比率,这将产生根据屏幕尺寸而变化的结果。

您可能需要考虑不同的方法,您可以按如下方式定义自己的相机预览:

  • 创建相机预览类:

       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中添加自定义预览:

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

  • 在您的活动类中,定义相机源方法,然后可以在onCreate()中调用该方法

/ **  *启动或重新启动相机源(如果存在)。如果相机源尚未存在  *(例如,因为在创建相机源之前调用了onResume),这将被调用  *再次创建相机源时。  * /

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;
        }
    }
}
  • 别忘了添加以下内容:

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

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

    startCameraSource();
}

/ **  *停止相机。  * /

@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();
        }
    }