Android:单击按钮时切换相机
Posted
技术标签:
【中文标题】Android:单击按钮时切换相机【英文标题】:Android: Switch camera when button clicked 【发布时间】: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();
【问题讨论】:
这可能会有所帮助:***.com/questions/6599454/… 你能在logcat冻结之前粘贴一些日志吗? 【参考方案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
【讨论】:
以上是关于Android:单击按钮时切换相机的主要内容,如果未能解决你的问题,请参考以下文章
在android中单击相机后,无需按确定按钮即可从相机获取图像