如何使用InputManager模拟缩放(放大/缩小)手势?
Kotlin 用InputManager实现双指缩放手势模拟
要实现双指缩放(捏合/放大)手势,关键在于正确处理多指针触摸事件——使用ACTION_POINTER_DOWN添加第二个手指,通过MotionEvent的多指针API设置每个手指的坐标,最后用ACTION_POINTER_UP和ACTION_UP结束手势。以下是完整实现代码:
import android.hardware.input.InputManager import android.view.InputEvent import android.view.MotionEvent import java.lang.reflect.Method import android.os.SystemClock import kotlin.concurrent.sleep fun simulatePinchZoom( centerX: Int, centerY: Int, // 缩放中心点 startDistance: Float, // 初始双指距离 endDistance: Float, // 结束双指距离(大于startDistance是放大,反之缩小) duration: Long ) { try { // 反射获取InputManager实例 val inputManagerClass = Class.forName("android.hardware.input.InputManager") val getInstanceMethod: Method = inputManagerClass.getMethod("getInstance") val inputManager = getInstanceMethod.invoke(null) val injectInputEventMethod: Method = inputManagerClass.getMethod( "injectInputEvent", InputEvent::class.java, Int::class.javaPrimitiveType ) val downTime = SystemClock.uptimeMillis() var eventTime = downTime // 计算两个手指的初始位置(围绕中心点) val halfStartDist = startDistance / 2 val finger1StartX = centerX - halfStartDist val finger1StartY = centerY.toFloat() val finger2StartX = centerX + halfStartDist val finger2StartY = centerY.toFloat() // 第一步:第一个手指按下(ACTION_DOWN) val downEvent1 = MotionEvent.obtain( downTime, eventTime, MotionEvent.ACTION_DOWN, floatArrayOf(finger1StartX, finger2StartX), floatArrayOf(finger1StartY, finger2StartY), 0 ) injectInputEventMethod.invoke(inputManager, downEvent1, 0) sleep(50) // 短暂间隔模拟真实触摸 // 第二步:第二个手指按下(ACTION_POINTER_DOWN,指针ID为1) eventTime += 50 val downEvent2 = MotionEvent.obtain( downTime, eventTime, MotionEvent.ACTION_POINTER_DOWN or (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT), floatArrayOf(finger1StartX, finger2StartX), floatArrayOf(finger1StartY, finger2StartY), 0 ) injectInputEventMethod.invoke(inputManager, downEvent2, 0) sleep(50) // 第三步:双指移动实现缩放 val steps = 100 val stepDuration = duration / steps val distanceDelta = endDistance - startDistance val stepDelta = distanceDelta / steps var currentFinger1X = finger1StartX var currentFinger2X = finger2StartX for (i in 1..steps) { eventTime += stepDuration val currentHalfDist = halfStartDist + (stepDelta * i) / 2 // 计算当前两个手指的坐标 currentFinger1X = centerX - currentHalfDist currentFinger2X = centerX + currentHalfDist val moveEvent = MotionEvent.obtain( downTime, eventTime, MotionEvent.ACTION_MOVE, floatArrayOf(currentFinger1X, currentFinger2X), floatArrayOf(finger1StartY, finger2StartY), 0 ) injectInputEventMethod.invoke(inputManager, moveEvent, 0) moveEvent.recycle() sleep(stepDuration) } // 第四步:抬起第二个手指(ACTION_POINTER_UP,指针ID为1) eventTime += 50 val upEvent2 = MotionEvent.obtain( downTime, eventTime, MotionEvent.ACTION_POINTER_UP or (1 shl MotionEvent.ACTION_POINTER_INDEX_SHIFT), floatArrayOf(currentFinger1X, currentFinger2X), floatArrayOf(finger1StartY, finger2StartY), 0 ) injectInputEventMethod.invoke(inputManager, upEvent2, 0) sleep(50) // 第五步:抬起第一个手指(ACTION_UP) eventTime += 50 val upEvent1 = MotionEvent.obtain( downTime, eventTime, MotionEvent.ACTION_UP, floatArrayOf(currentFinger1X, currentFinger2X), floatArrayOf(finger1StartY, finger2StartY), 0 ) injectInputEventMethod.invoke(inputManager, upEvent1, 0) // 回收事件资源 downEvent1.recycle() downEvent2.recycle() upEvent2.recycle() upEvent1.recycle() } catch (e: Exception) { e.printStackTrace() } }
关键说明
- 多指针事件处理:第二个手指按下用
ACTION_POINTER_DOWN,抬起用ACTION_POINTER_UP,通过(指针ID shl MotionEvent.ACTION_POINTER_INDEX_SHIFT)指定操作的指针。 - 坐标设置:使用
MotionEvent.obtain的多参数版本,传入两个手指的X/Y坐标数组。 - 缩放逻辑:通过调整两个手指相对于中心点的距离,模拟放大(距离增大)或缩小(距离减小)。
- 时间同步:所有事件共用同一个
downTime,eventTime逐步递增,模拟真实触摸的时间间隔。
你可以通过调用simulatePinchZoom(500, 500, 100f, 300f, 1000)来测试——以屏幕中心(500,500)为原点,初始双指距离100,放大到300,耗时1秒。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

