且构网

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

相机实时预览在相机切换时冻结

更新时间:2023-11-30 16:04:52


好吧,在这里,我将为您编写一个教程,内容涉及使用图像.html rel = noreferrer>相机通过启用相机的一些常见功能。

Well, Here I'm going to write a tutorial for you about capturing an image using Camera by enabling some common features of camera.

第1步:创建一个预览类

Step 1 : Create a preview class

/**
 * A basic Camera preview class
 */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private static final String TAG = "CameraPreview";
    private SurfaceHolder mHolder;
    private Camera mCamera;

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

        // 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);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

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

        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
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here

        // start preview with new settings
        startPreview();
    }

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

    public void startPreview() {
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (Exception e) {
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}

步骤2:使用 FrameLayout 来保存预览。

Step 2 : Use FrameLayout to hold the preview.

 <FrameLayout
        android:id="@+id/cameraPreview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

第3步:获取相机并发送到预览类。您可能需要在通过相机之前设置所需的参数。

Step 3 : Get the camera and send to the preview class. you may need to set the parameters that you need prior passing the camera.

   /**
     * Create our Preview view and set it as the content of UI.
     */
    private void initCameraPreview(final int cameraId, final boolean createPreview) {
        mCamera = getCameraInstance(cameraId);
        setupCameraParameters(cameraId);

        if (createPreview) {
            mPreview = new CameraPreview(this, mCamera);
            mPreviewHolder.addView(mPreview);
        }
        mReadyToCapture = true;
    }



   /**
     * A safe way to get an instance of the Camera object.
     */
    private Camera getCameraInstance(int cameraId) {
        Camera c = null;
        try {
            c = Camera.open(cameraId); // attempt to get a Camera instance
        } catch (Exception e) {
            e.printStackTrace();
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }


   /**
     * Measure and Setup the camera parameters.
     */
    private void setupCameraParameters(int cameraId) {
        boolean hasFlash;

        Camera.Parameters parameters = mCamera.getParameters();

        mPreviewSize = determineBestPreviewSize(parameters);
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);

        Camera.Size bestPictureSize = determineBestPictureSize(parameters);
        parameters.setPictureSize(bestPictureSize.width, bestPictureSize.height);

        hasFlash = Util.hasSystemFeature(this, PackageManager.FEATURE_CAMERA_FLASH);
        if (mCurrentCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            hasFlash = Util.hasFrontCameraFlash(parameters);
        } else {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }
        if (hasFlash)
            parameters.setFlashMode(mFlashMode);

        int[] orientations = Util.getCameraDisplayOrientation(this, cameraId);

        mDisplayOrientation = orientations[0];
        mLayoutOrientation = orientations[1];

        mCamera.setDisplayOrientation(mDisplayOrientation);

        mCamera.setParameters(parameters);
    }

    private Camera.Size determineBestPreviewSize(Camera.Parameters parameters) {
        List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
        return determineBestSize(sizes);
    }

    private Camera.Size determineBestPictureSize(Camera.Parameters parameters) {
        List<Camera.Size> sizes = parameters.getSupportedPictureSizes();
        return determineBestSize(sizes);
    }

    private Camera.Size determineBestSize(List<Camera.Size> sizes) {
        Camera.Size bestSize = null;

        for (Camera.Size currentSize : sizes) {
            boolean isDesiredRatio = (currentSize.width / 4) == (currentSize.height / 3);
            boolean isBetterSize = (bestSize == null || currentSize.width > bestSize.width);
            boolean isInBounds = currentSize.width <= PICTURE_SIZE_MAX_WIDTH;
            if (isDesiredRatio && isInBounds && isBetterSize) {
                bestSize = currentSize;
            }
        }

        if (bestSize == null) {
            return sizes.get(0);
        }

        return bestSize;
    }

第4步:交换相机的写方法

Step 4 : Writing method for swapping camera

   /**
     * Swapping between system cameras
     */
    private void swapCamera() {

        if (!(Camera.getNumberOfCameras() > 1)) {
         /* No front facing camera to switch.*/
            return;
        }

        mReadyToCapture = false;

        mCamera.stopPreview();
        releaseCamera(false);

        if (mCurrentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK)
            mCurrentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
        else
            mCurrentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;

        initCameraPreview(mCurrentCameraId, false);
        mPreview.resetCamera(mCamera);
        mPreview.startPreview();
    }

第5步:切换闪光灯的方法

Step 5 : Method for toggling flash

   /**
     * Toggling camera flash to ON/OFF
     */
    private void toggleFlash() {

        if (Util.hasSystemFeature(this, PackageManager.FEATURE_CAMERA_FLASH)) {

            Camera.Parameters parameters = mCamera.getParameters();


            if (mCurrentCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                if (!Util.hasFrontCameraFlash(parameters)) {

                 /* Front facing camera doesn\'t supported flash. */

                    return;
                }
            }

            mReadyToCapture = false;

            if (Camera.Parameters.FLASH_MODE_ON.equals(parameters.getFlashMode())) {
                mFlashMode = Camera.Parameters.FLASH_MODE_OFF;

            } else {
                mFlashMode = Camera.Parameters.FLASH_MODE_ON;

            }
            mCameraHandler.post(new Runnable() {
                @Override
                public void run() {

                    mCamera.stopPreview();
                    releaseCamera(false);

                    initCameraPreview(mCurrentCameraId, false);

                    mPreview.resetCamera(mCamera);
                    mPreview.startPreview();
                }
            });

        } else {
            /* warning_no_flash */
        }

    }

第6步:在屏幕状态变化期间处理相机的方法

Step 6: Methods for handling camera during the states changes of a screen

   /**
     * Release the camera for other applications
     */
    private void releaseCamera(boolean remove) {
        if (mCamera != null) {
            if (remove)
                mPreview.getHolder().removeCallback(mPreview);
            mCamera.release();
            mCamera = null;
        }
    }

步骤7:实用程序类。

   /**
     * Check whether the given feature available in s/m
     *
     * @return Returns true if the devices supports the feature, else
     * false.
     */
    public static boolean hasSystemFeature(Context context, String feature) {
        return context.getPackageManager().hasSystemFeature(feature);
    }

   /**
     * Check whether front camera flash feature available in s/m
     */
    public static boolean hasFrontCameraFlash(Camera.Parameters cameraParameters) {
        boolean result = true;
        if (cameraParameters.getFlashMode() == null) {
            result = false;
        }
        List<String> supportedFlashModes = cameraParameters.getSupportedFlashModes();
        if (supportedFlashModes == null || supportedFlashModes.isEmpty()
                || supportedFlashModes.size() == 1 &&
                supportedFlashModes.get(0).equals(Camera.Parameters.FLASH_MODE_OFF)) {
            result = false;
        }
        return result;
    }

   /**
     * Showing camera in the same orientation as the display
     */
    public static int[] getCameraDisplayOrientation(Activity activity,
                                                    int cameraId) {
        Camera.CameraInfo info =
                new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay()
                .getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
        } else {  // back-facing
            result = (info.orientation - degrees + 360) % 360;
        }
        return new int[]{result, degrees};
    }

Step 8: Capturing

Step 8: Capturing

  // Get an image from the camera
 if (null != mCamera && mReadyToCapture) {
     mCameraOrientationListener.rememberOrientation();
     mCamera.takePicture(mShutter, null, mPicture)
    }

   /**
     * Camera shutter sound callback,
     * used to enable sound while capture
     */
    private Camera.ShutterCallback mShutter = new Camera.ShutterCallback() {
        @Override
        public void onShutter() {

        }
    };

    /**
     * Camera picture callback
     */
    private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            mReadyToCapture = false;


            Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

            int rotation = ((mCurrentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK ? mDisplayOrientation :
                    ((360 - mDisplayOrientation) % 360)) + mCameraOrientationListener.getRememberedOrientation()
                    + mLayoutOrientation) % 360;

            if (rotation != 0) {
                Matrix matrix = new Matrix();
                matrix.postRotate(rotation);
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
            }


        }
    };

Step 9: Camera orientation listener for handling image rotation

Step 9: Camera orientation listener for handling image rotation

/**
 * Orientation listener to remember the device's orientation when the user presses
 * the shutter button.
 * <p/>
 * The orientation will be normalized to return it in steps of 90 degrees
 * (0, 90, 180, 270).
 */
public class CameraOrientationListener extends OrientationEventListener {
    private int currentNormalizedOrientation;
    private int rememberedNormalizedOrientation;

    public CameraOrientationListener(Context context) {
        super(context, SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    public void onOrientationChanged(int orientation) {
        if (orientation != ORIENTATION_UNKNOWN) {
            currentNormalizedOrientation = normalize(orientation);
        }
    }

    private int normalize(int degrees) {
        if (degrees > 315 || degrees <= 45) {
            return 0;
        }

        if (degrees > 45 && degrees <= 135) {
            return 90;
        }

        if (degrees > 135 && degrees <= 225) {
            return 180;
        }

        if (degrees > 225 && degrees <= 315) {
            return 270;
        }

        throw new RuntimeException("The physics as we know them are no more. Watch out for anomalies.");
    }

    public void rememberOrientation() {
        rememberedNormalizedOrientation = currentNormalizedOrientation;
    }

    public int getRememberedOrientation() {
        return rememberedNormalizedOrientation;
    }
}

Step 10: States handling

Step 10: States handling

   @Override
    public void onPause() {
        super.onPause();
        mReadyToCapture = false;
        releaseCamera(true);
    }


    @Override
    public void onResume() {
        super.onResume();
        removePreview();
        mReadyToCapture = false;
        smoothCameraLoading();
    }

   private void removePreview() {
        mPreviewHolder.removeAllViews();
    }

   private void smoothCameraLoading() {
        mCameraHandler.post(new Runnable() {
            @Override
            public void run() {
                initCameraPreview(mCurrentCameraId, true);
            }
        });
    }

Step 11: Instance variable used

Step 11: Instance variable used

    private String mFlashMode = Camera.Parameters.FLASH_MODE_OFF;
    private int mCurrentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
    private int mDisplayOrientation;
    private int mLayoutOrientation;
    private boolean mReadyToCapture = false;
    private Camera.Size mPreviewSize;
    private FrameLayout mPreviewHolder;
    private Camera mCamera;
    private CameraPreview mPreview;
    private Handler mCameraHandler;
    private CameraOrientationListener mCameraOrientationListener;
    private FrameLayout mRootView;