Android 提供了集中实现模拟滚动的方式:
Instrumentation
的sendPointerSync()
InputManager
的injectInputEvent()
AccessibilityService
的dispatchGesture()
方法这篇文章主要是介绍如何利用InputManager
来实现模拟滚动,先看一下效果图:
由于injectInputEvent
是@hide
的,所以需要使用反射调用此方法。
public class SwipeEvent {
/**
* @param fromX 起始x坐标
* @param fromY 起始y坐标
* @param toX 结束x坐标
* @param toY 结束y坐标
* @param step 单次滑动长度
*/
public void makeSwipeDown(int fromX, int fromY, int toX, int toY, int step) {
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
int y = fromY;
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis();
// 模拟down
MotionEvent motionEvent = null;
motionEvent = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, fromX, fromY, 0);
// 将MotionEvent的输入源设置为InputDevice.SOURCE_TOUCHSCREEN,输入源为触摸屏幕
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
// mode为1,INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT
invokeInjectInputEventMethod(inputManager, motionEvent, 1);
// 模拟move
int stepCount = (fromY - toY) / step;
for (int i = 0; i < stepCount; i++) {
y -= step;
motionEvent = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, fromX, y, 0);
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
// mode为2,INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
invokeInjectInputEventMethod(inputManager, motionEvent, 2);
Log.i("cwx", "y:" + y);
}
// 模拟up
if (y <= toY) {
motionEvent = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, toX, y, 0);
motionEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
invokeInjectInputEventMethod(inputManager, motionEvent, 2);
}
}
private void invokeInjectInputEventMethod(InputManager inputManager, InputEvent event, int mode) {
Class<?> clazz = null;
Method injectInputEventMethod = null;
Method recycleMethod = null;
try {
clazz = Class.forName("android.hardware.input.InputManager");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
injectInputEventMethod = clazz.getMethod("injectInputEvent", InputEvent.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
injectInputEventMethod.invoke(inputManager, event, mode);
// 准备回收event的方法
recycleMethod = event.getClass().getMethod("recycle");
//执行event的recycle方法
recycleMethod.invoke(event);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
上面涉及到参数inputSource
,在InputManager
的injectInputEvent
中代表的是mode
,而InputManager
也提供了三种mode
,下面介绍一下:
/**
* Input Event Injection Synchronization Mode: None.
* Never blocks. Injection is asynchronous and is assumed always to be successful.
* 永远不停止,注入输入时异步的,并假设总是成功的。
* @hide
*/
public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; // see InputDispatcher.h
/**
* Input Event Injection Synchronization Mode: Wait for result.
* Waits for previous events to be dispatched so that the input dispatcher can
* determine whether input event injection will be permitted based on the current
* input focus. Does not wait for the input event to finish being handled
* by the application.
* 等待调度之前的事件,以便调度程序可以根据当前的输入焦点确定是否允许输入事件注入,不必等待程序处理完输入事件。
* @hide
*/
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; // see InputDispatcher.h
/**
* Input Event Injection Synchronization Mode: Wait for finish.
* Waits for the event to be delivered to the application and handled.
* 等待事件传递到应用程序并进行处理
* @hide
*/
public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h
在由于是模拟滚动,当然比较耗时,我将它放进子线程中执行。
final SwipeEvent swipeEvent = new SwipeEvent();
new Thread(new Runnable() {
@Override
public void run() {
swipeEvent.makeSwipeDown(0, 1800, 0, 400, 50);
}
}).start();