Android多点触摸的实现(2)

来源:互联网 发布:linux ftp ip限制 编辑:程序博客网 时间:2024/05/24 06:41

AndroidKeyInputQueue.java中,系统创建了一个线程,然后把所有的Input事件放入一个队列:

public abstract class KeyInputQueue {

……………………

Thread mThread = new Thread("InputDeviceReader") {

        public void run() {

            android.os.Process.setThreadPriority(

                    android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);

            

            try {

                RawInputEvent ev = new RawInputEvent();

                while (true) {

                     InputDevice di;

                     // block, doesn't release the monitor

                     readEvent(ev);

if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {

                        synchronized (mFirst) {

                            di = newInputDevice(ev.deviceId);

                            mDevices.put(ev.deviceId, di);

                            configChanged = true;

                        }

                    } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {

                        synchronized (mFirst) {

                            Log.i(TAG, "Device removed: id=0x"

                                    + Integer.toHexString(ev.deviceId));

                            di = mDevices.get(ev.deviceId);

                            if (di != null) {

                                mDevices.delete(ev.deviceId);

                                configChanged = true;

                            } else {

                                Log.w(TAG, "Bad device id: " + ev.deviceId);

                            }

                        }

                    } else {

                        di = getInputDevice(ev.deviceId);

                        

                        // first crack at it

                        send = preprocessEvent(di, ev);

                        if (ev.type == RawInputEvent.EV_KEY) {

                            di.mMetaKeysState = makeMetaState(ev.keycode,

                                    ev.value != 0, di.mMetaKeysState);

                            mHaveGlobalMetaState = false;

                        }

                    }

                    if (di == null) {

                        continue;

                    }

                    

                    if (configChanged) {

                        synchronized (mFirst) {

                            addLocked(di, SystemClock.uptimeMillis(), 0,

                                    RawInputEvent.CLASS_CONFIGURATION_CHANGED,

                                    null);

                        }

                    }

                    

                    if (!send) {

                        continue;

                    }

                    

                    synchronized (mFirst) {

                      ……………………….

                    if (type == RawInputEvent.EV_KEY &&

                                (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&

                                (scancode < RawInputEvent.BTN_FIRST ||

                                        scancode > RawInputEvent.BTN_LAST)) {

                      /* 键盘按键事件 */

                      …………………….

                     } else if (ev.type == RawInputEvent.EV_KEY) {

                      /* 下面是EV_KEY事件分支,只支持单点的触摸屏有按键事件,

                       * 而支持多点的触摸屏没有按键事件,只有绝对坐标事件

*/

                          if (ev.scancode == RawInputEvent.BTN_TOUCH &&

                                    (classes&(RawInputEvent.CLASS_TOUCHSCREEN

                                            |RawInputEvent.CLASS_TOUCHSCREEN_MT))

                                            == RawInputEvent.CLASS_TOUCHSCREEN) {

                      /* 只支持单点的触摸屏的按键事件 */

                       …………………………………

                            } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&

                                    (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {

                      /* 鼠标和轨迹球 */

                       ……………………….

                      } else if (ev.type == RawInputEvent.EV_ABS &&

                                (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {

                      /* 下面才是多点触摸屏上报的事件 */

                         if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {

                                di.mAbs.changed = true;

                                di.mAbs.mNextData[di.mAbs.mAddingPointerOffset

                                        + MotionEvent.SAMPLE_PRESSURE] = ev.value;

                            } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {

                                di.mAbs.changed = true;

                                di.mAbs.mNextData[di.mAbs.mAddingPointerOffset

                                    + MotionEvent.SAMPLE_X] = ev.value;                          

                            } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {

                                di.mAbs.changed = true;

                                di.mAbs.mNextData[di.mAbs.mAddingPointerOffset

                                    + MotionEvent.SAMPLE_Y] = ev.value;                            

                            } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {

                                di.mAbs.changed = true;

                                di.mAbs.mNextData[di.mAbs.mAddingPointerOffset

                                    + MotionEvent.SAMPLE_SIZE] = ev.value;

                            }

            /* 上面这段就是多点触摸屏要用到的事件上报部分; 

             * 使用一个数组mNextData来保存,其中di.mAbs.mAddingPointerOffset 

             * 是当前点的偏移量,在每个点中还在MotionEvent中定义了X,Y,PRESSURE

             *  SIZE等偏移量,多点触摸屏的压力值由绝对坐标事件ABS_MT_TOUCH_MAJOR确定。

             */

                     } else if (ev.type == RawInputEvent.EV_ABS &&

                                (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {

            /* 这里是对单点触摸屏上报坐标事件的新的处理方法,同样使用了数组来保存 */

                           if (ev.scancode == RawInputEvent.ABS_X) {

                                di.mAbs.changed = true;

                                di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;

                            } else if (ev.scancode == RawInputEvent.ABS_Y) {

                                di.mAbs.changed = true;

                                di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;

                            } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {

                                di.mAbs.changed = true;

                                di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;

                                di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA

                                                 + MotionEvent.SAMPLE_PRESSURE] = ev.value;

                            } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {

                                di.mAbs.changed = true;

                                di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;

                                di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA

                                                 + MotionEvent.SAMPLE_SIZE] = ev.value;

                            }

            …………………………………………….}

            /* 下面是关键的同步处理方法 */

                   if (ev.type == RawInputEvent.EV_SYN

                                && ev.scancode == RawInputEvent.SYN_MT_REPORT

                                && di.mAbs != null) {

                    /* 在这里实现了对SYN_MT_REPORT事件的处理,

                     * 改变了di.mAbs.mAddingPointerOffset的值,从而将

                     * 新增的点的参数保存到下一组偏移量的位置。

                     */

                               …………………….

                             final int newOffset = (num <= InputDevice.MAX_POINTERS)

                                            ? (num * MotionEvent.NUM_SAMPLE_DATA)

                                            : (InputDevice.MAX_POINTERS *

                                                    MotionEvent.NUM_SAMPLE_DATA);

                                    di.mAbs.mAddingPointerOffset = newOffset;

                                    di.mAbs.mNextData[newOffset

                                            + MotionEvent.SAMPLE_PRESSURE] = 0;

                      }

                       ……………….

                   } else if (send || (ev.type == RawInputEvent.EV_SYN

                                && ev.scancode == RawInputEvent.SYN_REPORT)) {

                   /* 这里实现了对SYN_REPORT事件的处理

                    * 如果是单点触摸屏,即使用di.curTouchVals数组保存的点

                    * 转化为多点触摸屏的mNextData数组保存

                    * 最后是调用InputDevice中的generateAbsMotion处理这个数组。这个函数

                    * 的具体实现方法将在后面补充

                    */

                             …………………………..

                         ms.finish();          //重置所有点和偏移量

                           ……………………..

}

由于上层的代码仍然使用ABS_X, ABS_Y这些事件,为了使多点触摸屏代码有良好的兼容性,在KeyInputQueue.java的最后,我们将多点事件类型转化为单点事件类型,返回一个新的InputDevice:

private InputDevice newInputDevice(int deviceId) {

    int classes = getDeviceClasses(deviceId);

String name = getDeviceName(deviceId);

InputDevice.AbsoluteInfo absX;

    InputDevice.AbsoluteInfo absY;

    InputDevice.AbsoluteInfo absPressure;

    InputDevice.AbsoluteInfo absSize;

    if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {

            absX = loadAbsoluteInfo(deviceId,

                     RawInputEvent.ABS_MT_POSITION_X, "X");

            absY = loadAbsoluteInfo(deviceId,

                    RawInputEvent.ABS_MT_POSITION_Y, "Y");

            absPressure = loadAbsoluteInfo(deviceId,

                    RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");

            absSize = loadAbsoluteInfo(deviceId,

                    RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");

     } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {

            absX = loadAbsoluteInfo(deviceId,

                    RawInputEvent.ABS_X, "X");

            absY = loadAbsoluteInfo(deviceId,

                    RawInputEvent.ABS_Y, "Y");

            absPressure = loadAbsoluteInfo(deviceId,

                    RawInputEvent.ABS_PRESSURE, "Pressure");

            absSize = loadAbsoluteInfo(deviceId, 

RawInputEvent.ABS_TOOL_WIDTH, "Size");

} else {

            absX = null;

            absY = null;

            absPressure = null;

            absSize = null;

     }        

        return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);

 }

原创粉丝点击