相机切换时,相机实时预览会冻结

7
我正在创建一个定制的相机应用程序。我遇到的问题是当我在前置和后置摄像头之间切换时,相机预览会冻结。当通过调用以下代码之一以前置或后置摄像头启动活动时:
boolean opened = safeCameraOpenInView(view, Camera.CameraInfo.CAMERA_FACING_BACK)

在片段的OnCreateView方法中,两个摄像头都会如预期地在启动时显示。但是,一旦我在开关按钮的单击监听器中调用相同的方法,摄像头立即冻结。
这是我尝试的新实现,其中所有代码都在同一个片段中,而不是基于此问题的自定义类:Custom class camera live preview freezes on camera switch,但结果完全相同。我非常确定我需要对SurfaceView进行某些操作,以将其绑定到新摄像头,但我不知道该怎么做。有人有什么指导吗? 我的活动:
public class Camera2ActivityFragment extends Fragment {

    // Native camera.
    private Camera mCamera;


    // View to display the camera output.
    private CameraPreview mPreview;

    // Reference to the containing view.
    private View mCameraView;

    /**
     * Default empty constructor.
     */
    public Camera2ActivityFragment(){
        super();
    }

    /**
     * Static factory method
     * @param sectionNumber
     * @return
     */
    public static Camera2ActivityFragment newInstance(int sectionNumber) {
        Camera2ActivityFragment fragment = new Camera2ActivityFragment();
        //Bundle args = new Bundle();
        //args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        //fragment.setArguments(args);
        return fragment;
    }

    /**
     * OnCreateView fragment override
     * @param inflater
     * @param container
     * @param savedInstanceState
     * @return
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_camera2, container, false);
        boolean opened = safeCameraOpenInView(view, Camera.CameraInfo.CAMERA_FACING_BACK);

        if(opened == false){
            Log.d("CameraGuide","Error, Camera failed to open");
            return view;
        }

        // Trap the capture button.
        Button captureButton = (Button) view.findViewById(R.id.btnCameraStart);
        captureButton.setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        // get an image from the camera
                        mCamera.takePicture(null, null, mPicture);
                    }
                }
        );

        Button switchCameraButton = (Button) view.findViewById(R.id.btnSwitchCamera);
        switchCameraButton.setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        safeCameraOpenInView(getView(), Camera.CameraInfo.CAMERA_FACING_FRONT); //ISSUE OCCURS HERE!
                    }
                }
        );

        return view;
    }

    /**
     * Recommended "safe" way to open the camera.
     * @param view
     * @return
     */
    private boolean safeCameraOpenInView(View view, int camID) {
        boolean qOpened = false;
        releaseCameraAndPreview();
        //mCamera = getCameraInstance(Camera.CameraInfo.CAMERA_FACING_BACK);
        mCamera = getCameraInstance(camID);
        mCameraView = view;
        qOpened = (mCamera != null);

        if(qOpened == true){
            mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera,view);
            FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_view);
            preview.addView(mPreview);
            mPreview.startCameraPreview();
        }
        return qOpened;
    }

    /**
     * Safe method for getting a camera instance.
     * @return
     */
    public static Camera getCameraInstance(int camID){
        Camera c = null;
        try {
            c = Camera.open(camID); // attempt to get a Camera instance
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return c; // returns null if camera is unavailable
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        releaseCameraAndPreview();
    }

    /**
     * Clear any existing preview / camera.
     */
    private void releaseCameraAndPreview() {

        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        if(mPreview != null){
            mPreview.destroyDrawingCache();
            mPreview.mCamera = null;
        }
    }

    /**
     * Surface on which the camera projects it's capture results. This is derived both from Google's docs and the
     * excellent StackOverflow answer provided below.
     *
     * Reference / Credit: https://dev59.com/m-o6XIcBkEYKwwoYSCc1
     */
    class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

        // SurfaceHolder
        private SurfaceHolder mHolder;

        // Our Camera.
        private Camera mCamera;

        // Parent Context.
        private Context mContext;

        // Camera Sizing (For rotation, orientation changes)
        private Camera.Size mPreviewSize;

        // List of supported preview sizes
        private List<Camera.Size> mSupportedPreviewSizes;

        // Flash modes supported by this camera
        private List<String> mSupportedFlashModes;

        // View holding this camera.
        private View mCameraView;

        public CameraPreview(Context context, Camera camera, View cameraView) {
            super(context);

            // Capture the context
            mCameraView = cameraView;
            mContext = context;
            setCamera(camera);

            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed.
            mHolder = getHolder();
            mHolder.addCallback(this);
            mHolder.setKeepScreenOn(true);
            // deprecated setting, but required on Android versions prior to 3.0
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }

        /**
         * Begin the preview of the camera input.
         */
        public void startCameraPreview()
        {
            try{
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }

        /**
         * Extract supported preview and flash modes from the camera.
         * @param camera
         */
        private void setCamera(Camera camera)
        {
            // Source: https://dev59.com/m-o6XIcBkEYKwwoYSCc1
            mCamera = camera;
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            mSupportedFlashModes = mCamera.getParameters().getSupportedFlashModes();

            // Set the camera to Auto Flash mode.
            if (mSupportedFlashModes != null && mSupportedFlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO)){
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
                parameters.setRotation(90);
                //parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
                mCamera.setParameters(parameters);
                mCamera.setDisplayOrientation(90);
            }

            requestLayout();
        }

        /**
         * The Surface has been created, now tell the camera where to draw the preview.
         * @param holder
         */
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                mCamera.setPreviewDisplay(holder);

                //mCam = Camera.open();
                //mCam.setPreviewDisplay(holder);

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * Dispose of the camera preview.
         * @param holder
         */
        public void surfaceDestroyed(SurfaceHolder holder) {
            if (mCamera != null){
                mCamera.stopPreview();
            }
        }

        /**
         * React to surface changed events
         * @param holder
         * @param format
         * @param w
         * @param h
         */
        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

            //Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + 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();
                //mCamera.release();
            } 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
            try {
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
                parameters.setRotation(90);
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                mCamera.setParameters(parameters);
                mCamera.setDisplayOrientation(90);
                mCamera.setPreviewDisplay(mHolder);
                mCamera.enableShutterSound(true);
                mCamera.startPreview();

            } catch (Exception e){
                //Log.d(TAG, "Error starting camera preview: " + e.getMessage());
            }
        }

        /**
         * Calculate the measurements of the layout
         * @param widthMeasureSpec
         * @param heightMeasureSpec
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            /*
            // Source: https://dev59.com/m-o6XIcBkEYKwwoYSCc1
            final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
            final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
            setMeasuredDimension(width, height);

            if (mSupportedPreviewSizes != null){
                mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
            }
            */
            final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
            final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

            if (mSupportedPreviewSizes != null) {
                mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
            }

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

        /**
         * Update the layout based on rotation and orientation changes.
         * @param changed
         * @param left
         * @param top
         * @param right
         * @param bottom
         */
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom)
        {
            // Source: https://dev59.com/m-o6XIcBkEYKwwoYSCc1
            if (changed) {
                final int width = right - left;
                final int height = bottom - top;

                int previewWidth = width;
                int previewHeight = height;

                if (mPreviewSize != null){
                    Display display = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

                    switch (display.getRotation())
                    {
                        case Surface.ROTATION_0:
                            previewWidth = mPreviewSize.height;
                            previewHeight = mPreviewSize.width;
                            mCamera.setDisplayOrientation(90);
                            break;
                        case Surface.ROTATION_90:
                            previewWidth = mPreviewSize.width;
                            previewHeight = mPreviewSize.height;
                            break;
                        case Surface.ROTATION_180:
                            previewWidth = mPreviewSize.height;
                            previewHeight = mPreviewSize.width;
                            break;
                        case Surface.ROTATION_270:
                            previewWidth = mPreviewSize.width;
                            previewHeight = mPreviewSize.height;
                            mCamera.setDisplayOrientation(180);
                            break;
                    }
                }

                final int scaledChildHeight = previewHeight * width / previewWidth;
                mCameraView.layout(0, height - scaledChildHeight, width, height);
            }
        }

        /**
         *
         * @param sizes
         * @param width
         * @param height
         * @return
         */
        private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height)
        {
            final double ASPECT_TOLERANCE = 0.1;
            double targetRatio = (double) height / width;

            if (sizes == null)
                return null;

            Camera.Size optimalSize = null;
            double minDiff = Double.MAX_VALUE;

            int targetHeight = height;

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

    /**
     * Picture Callback for handling a picture capture and saving it out to a file.
     */
    private Camera.PictureCallback mPicture = new Camera.PictureCallback() {

        @Override
        public void onPictureTaken(byte[] data, Camera camera) {

            File pictureFile = getOutputMediaFile();
            if (pictureFile == null){
                Toast.makeText(getActivity(), "Image retrieval failed.", Toast.LENGTH_SHORT)
                        .show();
                return;
            }

            try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.close();

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

    /**
     * Used to return the camera File output.
     * @return
     */
    private File getOutputMediaFile(){

        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "Pocket Booth");

        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                Log.d("Camera Guide", "Required media storage does not exist");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        File mediaFile;
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                "IMG_"+ timeStamp + ".jpg");

        //DialogHelper.showDialog( "Success!","Your picture has been saved!",getActivity());

        return mediaFile;
    }
}

1
切换的方法是在线程(即可运行)中执行...您看到的延迟是因为您正在切换相机。我会看看能否找到一些代码。 - yUdoDis
太好了。我会试着将其添加到异步任务中。我假设这应该可以工作(一个正常的异步任务实现和调用该方法?)。如果有示例代码将不胜感激。 - Joachim Prinsloo
当在AsyncTask中运行safeCameraOpenInView(view, Camera.CameraInfo.CAMERA_FACING_BACK)时,会出现相同的问题。 - Joachim Prinsloo
我添加了一个新的答案,请阅读并告诉我它是否正确。谢谢。 - Hossein Seifi
3个回答

5

好的,我将为您提供一份关于启用相机的常见功能并使用相机捕获图像的教程。

步骤1:创建一个预览类

/**
 * 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来容纳预览。
 <FrameLayout
        android:id="@+id/cameraPreview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

步骤三:获取相机并发送给预览类。在传递相机之前,您可能需要设置所需的参数。
   /**
     * 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:编写交换相机的方法

   /**
     * 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:切换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 */
        }

    }

第六步:在屏幕状态更改期间处理相机的方法
   /**
     * Release the camera for other applications
     */
    private void releaseCamera(boolean remove) {
        if (mCamera != null) {
            if (remove)
                mPreview.getHolder().removeCallback(mPreview);
            mCamera.release();
            mCamera = null;
        }
    }

第七步:实用类。
   /**
     * 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};
    }

第八步:捕获
  // 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);
            }


        }
    };

第九步:相机方向监听器,用于处理图像旋转。
/**
 * 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;
    }
}

步骤10:状态处理

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

步骤11:使用实例变量
    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;

你真是个救命恩人!只有一个疑问:第三步中的PICTURE_SIZE_MAX_WIDTH是什么? - Ahsan Hayat
正如变量名所示,它是最大图片尺寸,使用高清分辨率:private static final int PICTURE_SIZE_MAX_WIDTH = 1280; - Anoop M Maddasseri
这对于后置摄像头完美地工作,但是对于前置摄像头预览显示倾斜。你能帮忙吗?@Anoop - Ahsan Hayat

2
在我的相机应用中,它与你们的应用有一些不同: 1. 在surfaceCreated时打开相机。 2. 在surfaceChanged中开始预览,没有停止预览。 3. 在surfaceDestroyed中释放相机。
要更换相机,只需将surfaceView设置为INVISIBLE,然后设置您的相机参数,再将surfaceView设置为VISIBLE即可。在我的应用程序中,我只是这样做:
        mbtCamera.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            if(cameraMax > 1){
                mSurfaceView.setVisibility(View.INVISIBLE);
                if(mCameraParam.isZoomSupported()) cameraZoomRatios.clear();
                if(cameraUsing == 0) cameraUsing = 1; else cameraUsing = 0;
                mCameraOverlayView.setScaleFactor(1.0f);
                mSurfaceView.setVisibility(View.VISIBLE);
            }
        }
    });

希望这能帮到你! 完整的回调函数:
    class CameraSurfaceHolderCallback implements SurfaceHolder.Callback {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mCamera = Camera.open(cameraUsing);
        mCameraParam = mCamera.getParameters();
        supportFlashMode = mCameraParam.getSupportedFlashModes();
        if(supportFlashMode == null) mbtFlash.setVisibility(View.INVISIBLE);
        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraUsing, info);
        int rotation = 0;
        if (info.facing == CameraInfo.CAMERA_FACING_FRONT){
            rotation = (info.orientation - rt + 360) % 360;
            if(mCameraParam.getSupportedFlashModes() == null)   mbtFlash.setVisibility(View.INVISIBLE); 
        }
        else{ // back-facing camera
            rotation = (info.orientation + rt) % 360;
            mbtFlash.setVisibility(View.VISIBLE);
            if(flashMode.equals(Camera.Parameters.FLASH_MODE_AUTO)) icons[0] = BitmapFactory.decodeResource(getResources(), R.drawable.ic_action_flash_automatic);
            else if(flashMode.equals(Camera.Parameters.FLASH_MODE_OFF)) icons[0] = BitmapFactory.decodeResource(getResources(), R.drawable.ic_action_flash_off);
            else if(flashMode.equals(Camera.Parameters.FLASH_MODE_ON)) icons[0] = BitmapFactory.decodeResource(getResources(), R.drawable.ic_action_flash_on);
            Matrix rot = new Matrix();
            rot.setRotate(360-rt, icons[0].getWidth()/2, icons[0].getHeight()/2);
            icons[0] = Bitmap.createBitmap(icons[0], 0, 0, icons[0].getWidth(), icons[0].getHeight(), rot, true);
            mbtFlash.setImageBitmap(icons[0]);
            mCameraParam.setFlashMode(flashMode);
        }
        if(mCameraParam.isZoomSupported()){
            cameraZoomRatios = mCameraParam.getZoomRatios();
            mCameraOverlayView.setMaxScaleFactor(cameraZoomRatios.get(mCameraParam.getMaxZoom())/100f);
        }else mCameraOverlayView.setMaxScaleFactor(1.0f);
        List<Size> ss = mCameraParam.getSupportedPictureSizes();
        Size maxResolution = ss.get(0);
        long pixel1, pixel2;
        pixel1 = maxResolution.width * maxResolution.height;
        for(int i=0; i<ss.size(); i++){
            pixel2 = ss.get(i).width * ss.get(i).height;
            if(pixel2 > pixel1){
                maxResolution = ss.get(i);
                pixel1 = pixel2;
            }
        }
        mCameraParam.setPictureSize(maxResolution.width, maxResolution.height);
        mCameraParam.setJpegQuality(100);

        LayoutParams rlParams = (LayoutParams) mSurfaceView.getLayoutParams();
        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        Display dp = wm.getDefaultDisplay();
        DisplayMetrics dm = new DisplayMetrics();
        dp.getMetrics(dm);
        float dpiScale = dm.density;
        float wScaleFactor = (dm.widthPixels-10*dpiScale)/maxResolution.height;
        float hScaleFactor = (dm.heightPixels-mllTopButtons.getHeight()-mllBotButtons.getHeight()-10*dpiScale)/maxResolution.width;
        if(wScaleFactor < hScaleFactor){
            rlParams.width = (int) (dm.widthPixels - 10*dpiScale);
            rlParams.height = (int) (maxResolution.width * wScaleFactor);
        }else{
            rlParams.width = (int) (maxResolution.height * hScaleFactor);
            rlParams.height = (int) (dm.heightPixels-mllTopButtons.getHeight()-mllBotButtons.getHeight()-10*dpiScale);
        }
        mSurfaceView.setLayoutParams(rlParams);
        mCameraOverlayView.setLayoutParams(rlParams);

        ss = mCameraParam.getSupportedJpegThumbnailSizes();
        float photoAspectRatio, thumbAspectRatio;
        photoAspectRatio = (float)maxResolution.width / maxResolution.height;
        thumbAspectRatio = 0;
        pixel1 = 0;
        for(int i=0; i<ss.size(); i++){
            if(ss.get(i).height != 0) thumbAspectRatio = (float)ss.get(i).width / ss.get(i).height;
            if(thumbAspectRatio == photoAspectRatio){
                if(pixel1 == 0)
                {
                    maxResolution = ss.get(i);
                    pixel1 = ss.get(i).width * ss.get(i).height;
                }else{
                    pixel2 = ss.get(i).width * ss.get(i).height;
                    if((pixel2 < pixel1)&&(pixel2 != 0)){
                        maxResolution = ss.get(i);
                        pixel1 = pixel2;
                    }
                }
            }
        }
        if(pixel1 != 0){
            mCameraParam.setJpegThumbnailSize(maxResolution.width, maxResolution.height);
            mCameraParam.setJpegThumbnailQuality(100);
        }

        List<String> focusModes = mCameraParam.getSupportedFocusModes();
        if(focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)){
            mCameraParam.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            focusingColor = Color.YELLOW;
        }

        int minEv = mCameraParam.getMinExposureCompensation();
        int maxEv = mCameraParam.getMaxExposureCompensation();
        if((minEv == 0)&&(maxEv == 0)) mCameraOverlayView.setEVCompensate(false);
        else mCameraOverlayView.setEVCompensate(minEv, maxEv);
        mCameraParam.setRotation(rotation);
        mCamera.setParameters(mCameraParam);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        try {
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(mSurfaceView.getHolder());

            List<Size> ss = mCameraParam.getSupportedPreviewSizes();
            Size maxResolution = ss.get(0);
            long pixel1, pixel2;
            float photoAspectRatio, previewAspectRatio;
            photoAspectRatio = (float)mCameraParam.getPictureSize().width / mCameraParam.getPictureSize().height;
            previewAspectRatio = 0;
            pixel1 = 0;
            for(int i=0; i<ss.size(); i++){
                if(ss.get(i).height != 0) previewAspectRatio = (float)ss.get(i).width / ss.get(i).height;
                if(previewAspectRatio == photoAspectRatio){
                    if(pixel1 == 0)
                    {
                        maxResolution = ss.get(i);
                        pixel1 = ss.get(i).width * ss.get(i).height;
                    }else{
                        pixel2 = ss.get(i).width * ss.get(i).height;
                        if(pixel2 > pixel1){
                            maxResolution = ss.get(i);
                            pixel1 = pixel2;
                        }
                    }
                }
            }
            if(pixel1 != 0) mCameraParam.setPreviewSize(maxResolution.width, maxResolution.height);
            mCamera.setParameters(mCameraParam);
            mCamera.startPreview();
        }
        catch(Exception e){}
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mCamera.stopPreview();
        mCamera.release();
    }
}

我在活动中使用它,而不是片段。我不确定这是否会有所不同。

尝试设置SurfaceView的可见性,但无济于事。您介意分享完整的相机类/活动,以便我可以比较发生事情的顺序吗? - Joachim Prinsloo

2

我认为在safeCameraOpenInView方法中存在一个小问题,即:

您的新相机视图是在冻结的图片下创建的。

因此,您应该编辑这一行:preview.addView(mPreview);

如下编辑:preview.addView(mPreview, preview.getChildCount());

或者您可以先删除以前的相机视图,然后再添加新的相机视图:

preview.removeAllViews();
preview.addView(mPreview);

希望这个能够运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接