android系统现在被移植到了各式各样的设备上,但它毕竟是为手机设计的操作系统,一些功能不是很完善。比如一些launcher模仿苹果做的dock,在使用鼠标的设备上,当鼠标放到dock里的图标上时,没有任何反应,只能用键盘的方向键激活它的动画。这篇文章就是尝试解决这个问题的笔记。
首先要明确一下android对鼠标是怎样支持的。据说原生的android是不支持鼠标的,后来在hacker们的努力下诞生了x86版本的系统并支持鼠标。
这里只大概描述一下framework层对鼠标的支持。第一次接收到鼠标事件后,WindowManagerService开始绘制鼠标光标,并处理鼠标事件。
RawInputEvent.CLASS_MOUSE为鼠标事件,属于Pointer事件,使用dispatchPointer函数分发事件。左键按下映射为ACTION_DOWN,左键释放映射为ACTION_UP,滑动映射为ACTION_MOVE。
参考:http://hi.baidu.com/haijiaoshu/blog/item/b65591084da31f24e824884e.html
捕获事件并传递的代码:
WindowManagerService.java:
case RawInputEvent.CLASS_MOUSE:
MotionEvent mmev = (MotionEvent)ev.event;
int mcx = (int)mmev.getX();
int mcy = (int)mmev.getY();
if (mMlx != mcx || mMly != mcy) {
mMlx = mcx;
mMly = mcy;
if (!mMouseDisplayed)
mMouseDisplayed = true;
requestAnimationLocked(0);
}
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
对于ACTION_DOWN,WindowManagerService会为它查找target window,同时把mKeyWaiter.mMotionTarget设置为找到的窗口。
WindowManagerService.java:dispatchPointer函数:
Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, ev, true, false, pid, uid);
mKeyWaiter.waitForNextEventTarget函数:
Object target = findTargetWindow(nextKey, qev, nextMotion, isPointerEvent, callingPid, callingUid);
对于ACTION_UP,会直接使用mKeyWaiter.mMotionTarget的值,并把mKeyWaiter.mMotionTarget重新设置为NULL——因为ACTION_UP之前肯定有ACTION_DOWN,并且在UP事件后就应该释放对象处理其他事件了。
WindowManagerService.java:dispatchPointer函数:
if (action == MotionEvent.ACTION_UP) {
// let go of our target
mKeyWaiter.mMotionTarget = null;
mPowerManager.logPointerUpEvent();
} else if (action == MotionEvent.ACTION_DOWN) {
mPowerManager.logPointerDownEvent();
}
对于ACTION_MOVE,如果mKeyWaiter.mMotionTarget不为NULL则传递事件,否则直接丢弃事件。因此平时鼠标的滑动不应该产生任何效果,但按下左键后滑动鼠标意味着要拖动对象。
最后,向窗口传递事件。
WindowManagerService.java:dispatchPointer函数:
target.mClient.dispatchPointer(ev, eventTime, true);
事件由窗口里的ViewRoot承接,并传递给窗口中的View部件。ViewRoot是一个处理消息的handler,具体的请参考其他android资料。
ViewRoot.java:handleMessage函数:
handled = mView.dispatchTouchEvent(event);
第一个接收到消息的是ViewGroup——在android里,各个View部件是附着在ViewGroup里的,ViewGroup互相嵌套成为View层次树。ViewGroup根据事件类型和坐标,传递到子部件上,直到被处理。具体可参看ViewGroup.java的代码。
参考:http://blog.csdn.net/ddna/article/details/5473293
最后是View部件处理按键事件。如果注册了回调函数则回调,否则就用默认的方法处理。
View.java:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
了解以上过程后,就知道怎么做了。首先要修改的是WindowManagerService.java的dispatchPointer方法,为ACTION_MOVE寻找target window——可以把处理ACTION_DOWN的部分代码拷过来,注意不要去设置mKeyWaiter.mMotionTarget的值。为了避免影响其他事件的传递,我把事件码改成了12,并把代码放到return前执行。
然后修改ViewGroup.java,为这个事件寻找View子部件并传递事件。这样事件便可以传递到app上了。
最后修改app,检测到这个事件就requestFocus——重写OnTouch函数,不过为了不影响其他事件的处理,需要先调用父类的OnTouch函数。
首先要明确一下android对鼠标是怎样支持的。据说原生的android是不支持鼠标的,后来在hacker们的努力下诞生了x86版本的系统并支持鼠标。
这里只大概描述一下framework层对鼠标的支持。第一次接收到鼠标事件后,WindowManagerService开始绘制鼠标光标,并处理鼠标事件。
RawInputEvent.CLASS_MOUSE为鼠标事件,属于Pointer事件,使用dispatchPointer函数分发事件。左键按下映射为ACTION_DOWN,左键释放映射为ACTION_UP,滑动映射为ACTION_MOVE。
参考:http://hi.baidu.com/haijiaoshu/blog/item/b65591084da31f24e824884e.html
捕获事件并传递的代码:
WindowManagerService.java:
case RawInputEvent.CLASS_MOUSE:
MotionEvent mmev = (MotionEvent)ev.event;
int mcx = (int)mmev.getX();
int mcy = (int)mmev.getY();
if (mMlx != mcx || mMly != mcy) {
mMlx = mcx;
mMly = mcy;
if (!mMouseDisplayed)
mMouseDisplayed = true;
requestAnimationLocked(0);
}
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;
对于ACTION_DOWN,WindowManagerService会为它查找target window,同时把mKeyWaiter.mMotionTarget设置为找到的窗口。
WindowManagerService.java:dispatchPointer函数:
Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev, ev, true, false, pid, uid);
mKeyWaiter.waitForNextEventTarget函数:
Object target = findTargetWindow(nextKey, qev, nextMotion, isPointerEvent, callingPid, callingUid);
对于ACTION_UP,会直接使用mKeyWaiter.mMotionTarget的值,并把mKeyWaiter.mMotionTarget重新设置为NULL——因为ACTION_UP之前肯定有ACTION_DOWN,并且在UP事件后就应该释放对象处理其他事件了。
WindowManagerService.java:dispatchPointer函数:
if (action == MotionEvent.ACTION_UP) {
// let go of our target
mKeyWaiter.mMotionTarget = null;
mPowerManager.logPointerUpEvent();
} else if (action == MotionEvent.ACTION_DOWN) {
mPowerManager.logPointerDownEvent();
}
对于ACTION_MOVE,如果mKeyWaiter.mMotionTarget不为NULL则传递事件,否则直接丢弃事件。因此平时鼠标的滑动不应该产生任何效果,但按下左键后滑动鼠标意味着要拖动对象。
最后,向窗口传递事件。
WindowManagerService.java:dispatchPointer函数:
target.mClient.dispatchPointer(ev, eventTime, true);
事件由窗口里的ViewRoot承接,并传递给窗口中的View部件。ViewRoot是一个处理消息的handler,具体的请参考其他android资料。
ViewRoot.java:handleMessage函数:
handled = mView.dispatchTouchEvent(event);
第一个接收到消息的是ViewGroup——在android里,各个View部件是附着在ViewGroup里的,ViewGroup互相嵌套成为View层次树。ViewGroup根据事件类型和坐标,传递到子部件上,直到被处理。具体可参看ViewGroup.java的代码。
参考:http://blog.csdn.net/ddna/article/details/5473293
最后是View部件处理按键事件。如果注册了回调函数则回调,否则就用默认的方法处理。
View.java:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
了解以上过程后,就知道怎么做了。首先要修改的是WindowManagerService.java的dispatchPointer方法,为ACTION_MOVE寻找target window——可以把处理ACTION_DOWN的部分代码拷过来,注意不要去设置mKeyWaiter.mMotionTarget的值。为了避免影响其他事件的传递,我把事件码改成了12,并把代码放到return前执行。
然后修改ViewGroup.java,为这个事件寻找View子部件并传递事件。这样事件便可以传递到app上了。
最后修改app,检测到这个事件就requestFocus——重写OnTouch函数,不过为了不影响其他事件的处理,需要先调用父类的OnTouch函数。
详情可参看附件的代码。
WindowManagerService.java
private int dispatchPointer(QueuedEvent qev, MotionEvent ev, int pid, int uid) {
if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Slog.v(TAG,
"dispatchPointer " + ev);
if (MEASURE_LATENCY) {
lt.sample("3 Wait for last dispatch ", System.nanoTime() - qev.whenNano);
}
Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
ev, true, false, pid, uid);
if (MEASURE_LATENCY) {
lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano);
}
int action = ev.getAction();
if (action == MotionEvent.ACTION_UP) {
// let go of our target
mKeyWaiter.mMotionTarget = null;
mPowerManager.logPointerUpEvent();
} else if (action == MotionEvent.ACTION_DOWN) {
mPowerManager.logPointerDownEvent();
}
if (targetObj == null) {
// In this case we are either dropping the event, or have received
// a move or up without a down. It is common to receive move
// events in such a way, since this means the user is moving the
// pointer without actually pressing down. All other cases should
// be atypical, so let's log them.
if (action != MotionEvent.ACTION_MOVE) {
Slog.w(TAG, "No window to dispatch pointer action " &#