经过长时间的搜索,我终于成功地切换了相机。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)
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 (holder?.surface == null) {
return
}
try {
camera.stopPreview()
} catch (e: Exception) {
}
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?) {
}
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) {
}
val params = camera.parameters
params?.let {
if (params.flashMode == Camera.Parameters.FLASH_MODE_TORCH) {
params.flashMode = Camera.Parameters.FLASH_MODE_OFF
} else {
params.flashMode = Camera.Parameters.FLASH_MODE_TORCH
}
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
} else {
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
}
最后,我的
SwitchCamera
按钮的工作方式如下:
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
}