Android——使用InputManager实现模拟滚动

文章目录

  • 模拟滚动的实现方式
  • 具体实现
  • 如何使用

模拟滚动的实现方式

Android 提供了集中实现模拟滚动的方式:

  • InstrumentationsendPointerSync()
  • InputManagerinjectInputEvent()
  • AccessibilityServicedispatchGesture()方法

这篇文章主要是介绍如何利用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,在InputManagerinjectInputEvent中代表的是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();

你可能感兴趣的:(Android)