您的位置:首页 > 移动开发 > Android开发

Android多点触摸的实现(2)

2010-10-27 09:25 197 查看
在Android的KeyInputQueue.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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: