web-dev-qa-db-fra.com

Android: changer d'appareil photo lorsque vous cliquez sur le bouton

J'ai une méthode appelée switchCamera, j'essaie de basculer la caméra d'avant en arrière en cliquant sur un bouton, en une seule transition en douceur. Mon application se bloque lorsque j'appelle cette méthode - je sais que je ne fais pas quelque chose de bien. Est-ce que quelqu'un pourrait m'aider?

Toute aide est très appréciée.

public void switchCamera(){
    int camNum = 0;
    camNum = Camera.getNumberOfCameras();
    int camBackId = Camera.CameraInfo.CAMERA_FACING_BACK;
    int camFrontId = Camera.CameraInfo.CAMERA_FACING_FRONT;

    Camera.CameraInfo currentCamInfo = new Camera.CameraInfo();

    //if camera is running
    if (camera != null){
        //and there is more than one camera
        if (camNum > 1){
            //stop current camera
            camera.stopPreview();
            camera.setPreviewCallback(null);
            //camera.takePicture(null, null, PictureCallback);
            camera.release();
            camera = null;
            //stop surfaceHolder?

            if (currentCamInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
                //switch camera to back camera
                camera=Camera.open(camBackId);
            }
            else{
                //switch camera to front camera
                camera=Camera.open(camFrontId);
            }
            //switch camera back on
            //specify surface?
            try {
                camera.setPreviewDisplay(surfaceHolder);
                camera.setPreviewCallback((PreviewCallback) this);
                camera.startPreview();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
37
LKB
Button otherCamera = (Button) findViewById(R.id.OtherCamera);

otherCamera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (inPreview) {
    camera.stopPreview();
}
//NB: if you don't release the current camera before switching, you app will crash
camera.release();

//swap the id of the camera to be used
if(currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){
    currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
}
else {
    currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
camera = Camera.open(currentCameraId);

setCameraDisplayOrientation(CameraActivity.this, currentCameraId, camera);
try {

    camera.setPreviewDisplay(previewHolder);
} catch (IOException e) {
    e.printStackTrace();
}
camera.startPreview();
}

Si vous souhaitez que l'image de la caméra s'affiche dans la même orientation que l'affichage, vous pouvez utiliser le code suivant.

public static void setCameraDisplayOrientation(Activity activity,
         int cameraId, Android.hardware.Camera camera) {
     Android.hardware.Camera.CameraInfo info =
             new Android.hardware.Camera.CameraInfo();
     Android.hardware.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;
     }
     camera.setDisplayOrientation(result);
 }
72
mjosh

Vous devez d'abord détruire la SurfacePreview de la caméra précédente, puis créer un nouvel objet de caméra (arrière/avant)

`//Code to destroy SurfacePreview
mPreview.surfaceDestroyed(mPreview.getHolder());
mPreview.getHolder().removeCallback(mPreview);
mPreview.destroyDrawingCache();
preview.removeView(mPreview);
mCamera.stopPreview();
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();

//Now create new camera object
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
    mCamera = Camera.open(camIdx);
    mPreview = new CameraPreview(CameraActivity.this, mCamera);
    preview.addView(mPreview);
    mCamera.setPreviewDisplay(mPreview.getHolder());
    mCamera.startPreview();
}`
5
Shailendra Yadav

Après une longue recherche, je peux enfin changer de caméra avec succès. La réponse de mjosh est une réponse utile mais cela n'a pas fonctionné pour moi. L'astuce que j'ai finalement trouvée est de créer une nouvelle classe CameraPreview et de l'ajouter à nouveau.

Voici ma classe CameraPreview.

@SuppressLint("ViewConstructor")
class CameraPreview(context: Context?,
                    private var camera: Camera,
                    private val displayRotation: Int) : SurfaceView(context), SurfaceHolder.Callback {
    companion object {
        private const val TAG = "TAG"
        private const val FOCUS_AREA_SIZE = 300
    }

    val surfaceHolder: SurfaceHolder = holder
    private var previewSize: Camera.Size? = null
    private val supportedPreviewSizes: MutableList<Camera.Size>?

    init {
        surfaceHolder.addCallback(this)
        supportedPreviewSizes = camera.parameters.supportedPreviewSizes
    }

    private val surfaceViewTouchListener: View.OnTouchListener = OnTouchListener { v, event ->
        camera.cancelAutoFocus()
        val focusRect = calculateFocusArea(event.x, event.y)
        val parameters = camera.parameters
        if (parameters.focusMode == Camera.Parameters.FOCUS_MODE_AUTO) {
            parameters.focusMode = Camera.Parameters.FOCUS_MODE_AUTO
        }
        if (parameters.maxNumFocusAreas > 0) {
            val areaList = ArrayList<Camera.Area>()
            areaList.add(Camera.Area(focusRect, 1000))
            parameters.focusAreas = areaList
        }
        try {
            camera.cancelAutoFocus()
            camera.parameters = parameters
            camera.startPreview()
            camera.autoFocus { _, cam ->
                if (cam.parameters.focusMode == Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
                    val parameters = cam.parameters;
                    parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
                    if (parameters.maxNumFocusAreas > 0) {
                        parameters.focusAreas = null
                    }
                    camera.parameters = parameters
                    camera.startPreview()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return@OnTouchListener true
    }

    override fun surfaceCreated(holder: SurfaceHolder?) {
        setOnTouchListener(surfaceViewTouchListener)
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            camera.setPreviewDisplay(holder)
            camera.setDisplayOrientation(displayRotation)
            camera.startPreview()
        } catch (e: IOException) {
            Log.d(TAG, "Error setting camera preview: " + e.message)
        }
    }

    override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
        // 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 (holder?.surface == null) {
            // preview surface does not exist
            return
        }
        // stop preview before making changes
        try {
            camera.stopPreview()
        } catch (e: Exception) {
            // 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 {
            val parameters = camera.parameters
            val bestPictureSize = getBestPictureSize(width, height, parameters)
            bestPictureSize?.let {
                parameters.setPictureSize(it.width, it.height)
            }
            previewSize?.let {
                parameters.setPreviewSize(it.width, it.height)
            }

            camera.parameters = parameters
            camera.setPreviewDisplay(holder)
            camera.startPreview()
        } catch (e: Exception) {
            Log.d(TAG, "Error starting camera preview: " + e.message)
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = View.resolveSize(suggestedMinimumWidth, widthMeasureSpec)
        val height = View.resolveSize(suggestedMinimumHeight, heightMeasureSpec)
        setMeasuredDimension(width, height)
        if (supportedPreviewSizes != null) {
            previewSize = getOptimalPreviewSize(supportedPreviewSizes, width, height)
        }
    }

    private fun getOptimalPreviewSize(sizes: List<Camera.Size>?, w: Int, h: Int): Camera.Size? {
        val ASPECT_TOLERANCE = 0.1
        val targetRatio = h.toDouble() / w
        if (sizes == null) return null
        var optimalSize: Camera.Size? = null
        var minDiff = Java.lang.Double.MAX_VALUE
        for (size in sizes) {
            val ratio = size.width.toDouble() / size.height
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue
            if (Math.abs(size.height - h) < minDiff) {
                optimalSize = size
                minDiff = Math.abs(size.height - h).toDouble()
            }
        }
        if (optimalSize == null) {
            minDiff = Java.lang.Double.MAX_VALUE
            for (size in sizes) {
                if (Math.abs(size.height - h) < minDiff) {
                    optimalSize = size
                    minDiff = Math.abs(size.height - h).toDouble()
                }
            }
        }
        return optimalSize
    }

    override fun surfaceDestroyed(holder: SurfaceHolder?) {
        // no-op
    }

    private fun getBestPictureSize(width: Int, height: Int, parameters: Camera.Parameters): Camera.Size? {
        var bestSize: Camera.Size?
        val sizeList = parameters.supportedPictureSizes
        bestSize = sizeList[0]
        for (i in 1 until sizeList.size) {
            if (sizeList[i].width * sizeList[i].height > bestSize!!.width * bestSize.height) {
                bestSize = sizeList[i]
            }
        }
        return bestSize
    }

    private fun calculateFocusArea(x: Float, y: Float): Rect {
        val left = clamp(Java.lang.Float.valueOf(x / width * 2000 - 1000).toInt(), FOCUS_AREA_SIZE)
        val top = clamp(Java.lang.Float.valueOf(y / height * 2000 - 1000).toInt(), FOCUS_AREA_SIZE)
        return Rect(left, top, left + FOCUS_AREA_SIZE, top + FOCUS_AREA_SIZE)
    }

    private fun clamp(touchCoordinateInCameraReper: Int, focusAreaSize: Int): Int {
        return if (Math.abs(touchCoordinateInCameraReper) + focusAreaSize / 2 > 1000) {
            if (touchCoordinateInCameraReper > 0) {
                1000 - focusAreaSize / 2
            } else {
                -1000 + focusAreaSize / 2
            }
        } else {
            touchCoordinateInCameraReper - focusAreaSize / 2
        }
    }

    fun turnFlashOnOrOff() {
        try {
            camera.stopPreview()
        } catch (e: Exception) {
            // ignore
        }

        val params = camera.parameters
        params?.let {
            if (params.flashMode == Camera.Parameters.FLASH_MODE_TORCH) {
                params.flashMode = Camera.Parameters.FLASH_MODE_OFF
                //flash.setImageResource(R.mipmap.baseline_flash_off_white_24dp)
            } else {
                params.flashMode = Camera.Parameters.FLASH_MODE_TORCH
                //flash.setImageResource(R.mipmap.baseline_flash_on_white_24dp)
            }
            camera.setPreviewDisplay(holder)
            try {
                camera.parameters = params
            } catch (e: Exception) {
                e.printStackTrace()
            }
            camera.startPreview()
        }
    }
}

Ma méthode openCamera avec laquelle j'ouvre la caméra:

private fun openCamera() {
        camera = CameraUtil.getCameraInstance(getCameraId())
        rotation = getDisplayRotation()
        cameraPreview = CameraPreview(activity, camera!!, rotation)
        fl_camera.addView(cameraPreview)
    }

Avant de créer CameraPreview, vous devez calculer la rotation de la caméra et la définir comme displayOrientation

private fun getDisplayRotation(): Int {
        val info = Camera.CameraInfo()
        Camera.getCameraInfo(getCameraId(), info)
        val rotation = activity.windowManager.defaultDisplay.rotation
        var degrees = 0
        when (rotation) {
            Surface.ROTATION_0 -> degrees = 0
            Surface.ROTATION_90 -> degrees = 90
            Surface.ROTATION_180 -> degrees = 180
            Surface.ROTATION_270 -> degrees = 270
        }

        var result: Int
        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 result
    }

Et je reçois cameraId comme ci-dessous:

private fun getCameraId(): Int {
        val numberOfCameras = Camera.getNumberOfCameras()
        var cameraInfo: Camera.CameraInfo
        for (i in 0 until numberOfCameras) {
            cameraInfo = Camera.CameraInfo()
            Camera.getCameraInfo(i, cameraInfo)
            if (cameraInfo.facing == currentCamera) {
                return i
            }
        }
        return 0
    }

Et enfin mon bouton SwtichCamera fonctionne comme ceci:

switch_camera.setOnClickListener {
            try {
                camera?.stopPreview()
            } catch (e: Exception) {
                e.printStackTrace()
            }


            camera?.release()


           currentCamera = if (currentCamera === Android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) {
                Camera.CameraInfo.CAMERA_FACING_FRONT
            } else {
                Camera.CameraInfo.CAMERA_FACING_BACK
            }

            fl_camera.removeView(cameraPreview)
            openCamera()
        }

C'est une solution de travail pour moi. J'espère que cela vous aidera aussi.

Modifier: L'aperçu de l'appareil photo peut être un problème pour les appareils Samsung. Voici une méthode alternative pour obtenir la meilleure taille d'aperçu.

private fun getOptimalPreviewSize(sizes: List<Camera.Size>?, w: Int, h: Int): Camera.Size? {

        if (sizes == null) return null

        var optimalSize: Camera.Size? = null
        val ratio = h.toDouble() / w
        var minDiff = Java.lang.Double.MAX_VALUE
        var newDiff: Double
        for (size in sizes) {
            newDiff = Math.abs(size.width.toDouble() / size.height - ratio)
            if (newDiff < minDiff) {
                optimalSize = size
                minDiff = newDiff
            }
        }
        return optimalSize
    }
1
savepopulation