AndroidX Camera:如何实现指定区域的捏合缩放
实现AndroidX Camera指定区域的捏合缩放功能
要解决捏合缩放始终以预览中心为基准的问题,核心是将触摸区域的屏幕坐标转换为相机传感器的归一化坐标,再通过CameraX的CameraControl设置缩放中心,而非依赖默认的中心缩放。以下是具体实现步骤:
1. 监听捏合手势与触摸坐标
使用Android原生的ScaleGestureDetector捕获捏合操作,同时记录捏合手势的中心点屏幕坐标:
private lateinit var scaleGestureDetector: ScaleGestureDetector private var currentZoomRatio = 1f private lateinit var cameraControl: CameraControl private lateinit var cameraInfo: CameraInfo private val scaleListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() { override fun onScaleBegin(detector: ScaleGestureDetector): Boolean { // 记录当前缩放比例,避免初始缩放跳变 currentZoomRatio = cameraInfo.zoomState.value?.zoomRatio ?: 1f return true } override fun onScale(detector: ScaleGestureDetector): Boolean { // 计算新的缩放比例并限制范围 val maxZoom = cameraInfo.zoomState.value?.maxZoomRatio ?: 1f val newZoom = currentZoomRatio * detector.scaleFactor currentZoomRatio = newZoom.coerceIn(1f, maxZoom) // 获取捏合中心点的屏幕坐标 val touchScreenX = detector.focusX val touchScreenY = detector.focusY // 将屏幕坐标转换为相机传感器归一化坐标 val previewView = findViewById<PreviewView>(R.id.preview_view) val previewMatrix = Matrix() previewView.getPreviewMatrix(previewMatrix) // 反转矩阵,将屏幕坐标映射到预览纹理坐标 val invertedMatrix = Matrix() if (previewMatrix.invert(invertedMatrix)) { val touchPoints = floatArrayOf(touchScreenX, touchScreenY) invertedMatrix.mapPoints(touchPoints) // 转换为传感器归一化坐标(0-1范围,(0,0)为传感器左上角) val sensorNormX = touchPoints[0] / previewView.width val sensorNormY = touchPoints[1] / previewView.height // 设置缩放中心与缩放比例 cameraControl.setZoomCenter(PointF(sensorNormX, sensorNormY)) cameraControl.setZoomRatio(currentZoomRatio) } return true } }
2. 绑定触摸监听与Camera实例
在CameraX初始化完成后,绑定预览View的触摸事件,并获取CameraControl和CameraInfo实例:
// 假设已通过CameraProvider绑定相机生命周期 val camera = cameraProvider.bindToLifecycle( this, CameraSelector.DEFAULT_BACK_CAMERA, previewUseCase ) cameraControl = camera.cameraControl cameraInfo = camera.cameraInfo // 初始化手势检测器并绑定触摸监听 scaleGestureDetector = ScaleGestureDetector(this, scaleListener) previewView.setOnTouchListener { _, event -> scaleGestureDetector.onTouchEvent(event) true }
3. 适配传感器方向(可选)
部分设备的相机传感器方向与屏幕方向不一致,可能导致缩放中心偏移。可通过cameraInfo.sensorRotationDegrees调整坐标:
// 根据传感器旋转角度调整归一化坐标 val sensorRotation = cameraInfo.sensorRotationDegrees val adjustedX: Float val adjustedY: Float when (sensorRotation) { 90 -> { adjustedX = 1f - sensorNormY adjustedY = sensorNormX } 180 -> { adjustedX = 1f - sensorNormX adjustedY = 1f - sensorNormY } 270 -> { adjustedX = sensorNormY adjustedY = 1f - sensorNormX } else -> { adjustedX = sensorNormX adjustedY = sensorNormY } } cameraControl.setZoomCenter(PointF(adjustedX, adjustedY))
关键说明
- 必须使用
PreviewView.getPreviewMatrix()获取预览的转换矩阵,该矩阵包含了预览的缩放、旋转、裁剪等变换信息,反转后才能将屏幕坐标正确映射到相机传感器坐标系。 - 缩放比例需限制在
1f到maxZoomRatio之间,避免超出相机硬件支持的范围。 - 可通过
cameraInfo.zoomState的观察者实时更新当前缩放比例,确保手势开始时的初始值准确。
内容的提问来源于stack exchange,提问作者Rahul Mishra
相关产品推荐
相关产品推荐

