android的widget在鼠标划过时自动聚焦

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函数。

详情可参看附件的代码。



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 " &#
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值