【问题标题】:Android: Switch camera when button clickedAndroid:单击按钮时切换相机
【发布时间】:2013-05-21 20:36:36
【问题描述】:

我有一个名为 switchCamera 的方法,我试图通过单击一个按钮将摄像头从正面切换到背面,实现平滑过渡。当我调用这个方法时,我的应用程序冻结了——我知道我做的不对。有谁能帮帮我吗?

非常感谢任何帮助。

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

【问题讨论】:

标签: java android button camera


【解决方案1】:
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();
}

如果您想让相机图像以相同的方向显示 显示,您可以使用以下代码。

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

【讨论】:

  • 有帮助的答案,但有一个问题...Camera.CameraInfo.CAMERA_FACING_BACK/FRONT 需要 API 级别 9。我们在旧版本中做了什么?
  • 我的相机照片在纵向模式下仍然没有正确旋转。在横向模式下它工作正常。
  • 太棒了——你用那个漂亮的 setCameraDisplayOrientation 方法为我节省了很多时间!
  • 你在哪里设置currentCameraId
  • @ryderd currentCameraId 是在活动中设置的任意变量,用于跟踪当前设置。如果在其他地方未使用,它可以设置为分配给整个活动的私有变量或同一方法内的变量。
【解决方案2】:

首先你需要销毁前一个相机的SurfacePreview,然后需要创建一个新的相机对象(Back/Front)

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

【讨论】:

    【解决方案3】:

    经过长时间的搜索,我终于可以成功切换相机了。 mjosh 的答案是一个有用的答案,但对我没有用。我最终发现的技巧是创建新的CameraPreview 类并再次添加它。

    这是我的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()
            }
        }
    }
    

    我用它打开相机的openCamera方法:

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

    在创建CameraPreview之前,您必须计算相机的旋转并将其设置为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
        }
    

    我得到cameraId,如下所示:

    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
        }
    

    最后我的SwtichCamera 按钮是这样工作的:

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

    这对我来说是一个可行的解决方案。我希望这对其他人也有帮助。

    编辑:相机预览对于三星设备来说可能是个问题。这是获得最佳预览大小的另一种方法。

    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
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-21
      • 2011-08-12
      • 1970-01-01
      • 2019-12-23
      • 1970-01-01
      相关资源
      最近更新 更多