android key事件分发与焦点事件的传递

android keyEvent事件的分发,要从ViewRootImpl说起,ViewRootImpl承载了WMS与View通信的桥梁,ViewRootImpl中有一个ViewRootHandler ,它是一个Handler的子类:

 

1.该类源码如下

 

final class ViewRootHandler extends Handler{

    @Override

    public String getMessageName(Message message) {

        switch (message.what) {

            case MSG_INVALIDATE:

                return "MSG_INVALIDATE";

            case MSG_INVALIDATE_RECT:

                return "MSG_INVALIDATE_RECT";

            case MSG_DIE:

                return "MSG_DIE";

            case MSG_RESIZED:

                return "MSG_RESIZED";

            case MSG_RESIZED_REPORT:

                return "MSG_RESIZED_REPORT";

            case MSG_WINDOW_FOCUS_CHANGED:

                return "MSG_WINDOW_FOCUS_CHANGED";

            case MSG_DISPATCH_INPUT_EVENT:

                return "MSG_DISPATCH_INPUT_EVENT";

            case MSG_DISPATCH_APP_VISIBILITY:

                return "MSG_DISPATCH_APP_VISIBILITY";

            case MSG_DISPATCH_GET_NEW_SURFACE:

                return "MSG_DISPATCH_GET_NEW_SURFACE";

            case MSG_DISPATCH_KEY_FROM_IME:

                return "MSG_DISPATCH_KEY_FROM_IME";

            case MSG_FINISH_INPUT_CONNECTION:

                return "MSG_FINISH_INPUT_CONNECTION";

            case MSG_CHECK_FOCUS:

                return "MSG_CHECK_FOCUS";

            case MSG_CLOSE_SYSTEM_DIALOGS:

                return "MSG_CLOSE_SYSTEM_DIALOGS";

            case MSG_DISPATCH_DRAG_EVENT:

                return "MSG_DISPATCH_DRAG_EVENT";

            case MSG_DISPATCH_DRAG_LOCATION_EVENT:

                return "MSG_DISPATCH_DRAG_LOCATION_EVENT";

            case MSG_DISPATCH_SYSTEM_UI_VISIBILITY:

                return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY";

            case MSG_UPDATE_CONFIGURATION:

                return "MSG_UPDATE_CONFIGURATION";

            case MSG_PROCESS_INPUT_EVENTS:

                return "MSG_PROCESS_INPUT_EVENTS";

            case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST:

                return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST";

            case MSG_DISPATCH_WINDOW_ANIMATION_STARTED:

                return "MSG_DISPATCH_WINDOW_ANIMATION_STARTED";

            case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED:

                return "MSG_DISPATCH_WINDOW_ANIMATION_STOPPED";

            case MSG_WINDOW_MOVED:

                return "MSG_WINDOW_MOVED";

            case MSG_SYNTHESIZE_INPUT_EVENT:

                return "MSG_SYNTHESIZE_INPUT_EVENT";

            case MSG_DISPATCH_WINDOW_SHOWN:

                return "MSG_DISPATCH_WINDOW_SHOWN";

        }

        return super.getMessageName(message);

    }

 

    @Override

    public void handleMessage(Message msg) {

        switch (msg.what) {

        case MSG_INVALIDATE:

            ((View) msg.obj).invalidate();

            break;

        case MSG_INVALIDATE_RECT:

            final View.AttachInfo.InvalidateInfoinfo = (View.AttachInfo.InvalidateInfo) msg.obj;

            info.target.invalidate(info.left, info.top, info.right, info.bottom);

            info.recycle();

            break;

        case MSG_PROCESS_INPUT_EVENTS:

            mProcessInputEventsScheduled = false;

            doProcessInputEvents();

            break;

\\\--------------中间省略无关代码-------------------

}

 

 

 

2.我们从   doProcessInputEvents()跟踪:

 

 

 


void doProcessInputEvents() {
    // Deliver all pending input events in the queue.
    while (mPendingInputEventHead != null) {
        QueuedInputEvent q = mPendingInputEventHead;
        mPendingInputEventHead = q.mNext;
        if (mPendingInputEventHead == null) {
            mPendingInputEventTail = null;
        }
        q.mNext = null;

        mPendingInputEventCount -= 1;
        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                mPendingInputEventCount);

        long eventTime = q.mEvent.getEventTimeNano();
        long oldestEventTime = eventTime;
        if (q.mEvent instanceof MotionEvent) {
            MotionEvent me = (MotionEvent)q.mEvent;
            if (me.getHistorySize() > 0) {
                oldestEventTime = me.getHistoricalEventTimeNano(0);
            }
        }
        mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);

        deliverInputEvent(q);
    }

    // We are done processing all input events that we can process right now
    // so we can clear the pending flag immediately.
    if (mProcessInputEventsScheduled) {
        mProcessInputEventsScheduled = false;
        mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
    }
}

 

 

3.doProcessInputEvents获取了一个QueuedInputEvent,然后传给deliverInputEvent(q)处理:

源码:

 

private void deliverInputEvent(QueuedInputEvent q) {
    Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
            q.mEvent.getSequenceNumber());
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
    }

    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }

    if (stage != null) {
        stage.deliver(q);
    } else {
        finishInputEvent(q);
    }
}

 

 

4.从步骤3中我们看到了一个stage.deliver(q)的方法,stage的deliver方法属于InputStage中的一个final 方法:

/**
 * Delivers an event to be processed.
 */
public final void deliver(QueuedInputEvent q) {
    if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
        forward(q);
    } else if (shouldDropInputEvent(q)) {
        finish(q, false);
    } else {
        apply(q, onProcess(q));
    }
}

 

 

5.在4中我们看到了apply方法里面调用了onProcess()方法,这个onProcess只走的InputStage的子类ViewPostImeInputStage的onProcess,我们来看看这个方法

 

final class ViewPostImeInputStage extends InputStage {
    public ViewPostImeInputStage(InputStage next) {
        super(next);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (q.mEvent instanceof KeyEvent) {
            return processKeyEvent(q);
        } else {
            // If delivering a new non-key event, make sure the window is
            // now allowed to start updating.
            handleDispatchWindowAnimationStopped();
            final int source = q.mEvent.getSource();
            if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                return processPointerEvent(q);
            } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                return processTrackballEvent(q);
            } else {
                return processGenericMotionEvent(q);
            }
        }
    }

6.我们看到作为KeyEvent当然是走了processKeyEvent():

 

 

 

private int processKeyEvent(QueuedInputEvent q) {
    final KeyEvent event = (KeyEvent)q.mEvent;

    if (event.getAction() != KeyEvent.ACTION_UP) {
        // If delivering a new key event, make sure the window is
        // now allowed to start updating.
        handleDispatchWindowAnimationStopped();
    }

    // Deliver the key to the view hierarchy.
 

    if (mView.dispatchKeyEvent(event)) {
        return FINISH_HANDLED;
    }

    if (shouldDropInputEvent(q)) {
        return FINISH_NOT_HANDLED;
    }

    // If the Control modifier is held, try to interpret the key as a shortcut.
    if (event.getAction() == KeyEvent.ACTION_DOWN
            && event.isCtrlPressed()
            && event.getRepeatCount() == 0
            && !KeyEvent.isModifierKey(event.getKeyCode())) {
        if (mView.dispatchKeyShortcutEvent(event)) {
            return FINISH_HANDLED;
        }
        if (shouldDropInputEvent(q)) {
            return FINISH_NOT_HANDLED;
        }
    }

    // Apply the fallback event policy.
    if (mFallbackEventHandler.dispatchKeyEvent(event)) {
        return FINISH_HANDLED;
    }
    if (shouldDropInputEvent(q)) {
        return FINISH_NOT_HANDLED;
    }

    // Handle automatic focus changes.
    if (event.getAction() == KeyEvent.ACTION_DOWN) {
        int direction = 0;
        switch (event.getKeyCode()) {
            case KeyEvent.KEYCODE_DPAD_LEFT:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_LEFT;
                }
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_RIGHT;
                }
                break;
            case KeyEvent.KEYCODE_DPAD_UP:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_UP;
                }
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_DOWN;
                }
                break;
            case KeyEvent.KEYCODE_TAB:
                if (event.hasNoModifiers()) {
                    direction = View.FOCUS_FORWARD;
                } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
                    direction = View.FOCUS_BACKWARD;
                }
                break;
        }
        if (direction != 0) {
            View focused = mView.findFocus();
            if (focused != null) {
                View v = focused.focusSearch(direction);
                if (v != null && v != focused) {
                    // do the math the get the interesting rect
                    // of previous focused into the coord system of
                    // newly focused view
                    focused.getFocusedRect(mTempRect);
                    if (mView instanceof ViewGroup) {
                        ((ViewGroup) mView).offsetDescendantRectToMyCoords(
                                focused, mTempRect);
                        ((ViewGroup) mView).offsetRectIntoDescendantCoords(
                                v, mTempRect);
                    }
                    if (v.requestFocus(direction, mTempRect)) {
                        playSoundEffect(SoundEffectConstants
                                .getContantForFocusDirection(direction));
                        return FINISH_HANDLED;
                    }
                }

                // Give the focused view a last chance to handle the dpad key.
                if (mView.dispatchUnhandledMove(focused, direction)) {
                    return FINISH_HANDLED;
                }
            } else {
                // find the best view to give focus to in this non-touch-mode with no-focus
                View v = focusSearch(null, direction);
                if (v != null && v.requestFocus(direction)) {
                    return FINISH_HANDLED;
                }
            }
        }
    }
    return FORWARD;
}

 

 

分析:此处有2处关键逻辑


   
if (mView.dispatchKeyEvent(event)) {
       
return FINISH_HANDLED;
   
}

分析:首先把时间传给ViewHierachy去处理,我们来看源码:

 

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onKeyEvent(event, 1);
    }

    if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
            == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
        if (super.dispatchKeyEvent(event)) {
            return true;
        }
    } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
            == PFLAG_HAS_BOUNDS) {
        if (mFocused.dispatchKeyEvent(event)) {
            return true;
        }
    }

    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
    }
    return false;
}

源码的逻辑大概的意思是如果我本身需要处理那么我就自己处理,也就是调用super.dispatchKeyEvent(),ViewGroup的super不就是View嘛,也就是该ViewGroup自己优先处理,如果自己不需要,那么就往下传递给有焦点的子view处理,如果没有焦点或者都不处理么,那么就往下走到第二处逻辑:

 

  if(direction != 0){
            View focused =
mView.findFocus();
            if
(focused != null) {
                View v =focused.focusSearch(direction)
;

----------------------------------------------此处省略----}else{

 

 

              // find the best view to give focus to in thisnon-touch-mode with no-focus
               
View v = focusSearch(null, direction);
                if
(v != null &&v.requestFocus(direction)) {
                   
return FINISH_HANDLED;
               
}

}

 

 

分析:上一步如果KeyEvent不被ViewHierachy消费,那么就焦点分发逻辑,把这个事件当成一个焦点传递事件,焦点获取成功并刷新界面之后这个事件就被标记为finish_handled

 

If else的区别在于当前mview有没有焦点,根据有无焦点来处理,如果本身含有焦点,就走找FocusFinderfocusSearch方法中FocusView参数不为空的逻辑,如果本身没有焦点也是走ViewRootImplfocusSearch方法,为其分配一个焦点,其实if()中的focusView.focushSearch方法我们从看看源码发现它的逻辑是递归调用super.focusSearch方法,viewviewgroup本身没有提供找焦点的方法实现,最后递归到viewRootImpl中的focusSearch方法,该方法调用FocusFinder工具来找焦点,找焦点的方法就是确定focusView的坐标,然后根据就近原则来找出合适的焦点,具体源码大家自己去看吧。找到焦点view之后调用requestFocus方法来获取焦点并刷新焦点界面的显示,但是要注意的是找到的焦点并不是最终实际获取焦点的view,有可能是它自己也有可能是它的子View,这个是根据requestFocush的源码:

 

@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
    if (DBG) {
        System.out.println(this + " ViewGroup.requestFocus direction="
                + direction);
    }
    int descendantFocusability = getDescendantFocusability();

    switch (descendantFocusability) {
        case FOCUS_BLOCK_DESCENDANTS:
            return super.requestFocus(direction, previouslyFocusedRect);
        case FOCUS_BEFORE_DESCENDANTS: {
            final boolean took = super.requestFocus(direction, previouslyFocusedRect);
            return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
        }
        case FOCUS_AFTER_DESCENDANTS: {
            final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
            return took ? took : super.requestFocus(direction, previouslyFocusedRect);
        }
        default:
            throw new IllegalStateException("descendant focusability must be "
                    + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
                    + "but is " + descendantFocusability);
    }
}

 

 

 

根据焦点传递关系来确定谁来真正获取焦点框。


综上所述,android的KeyEvent时间的传递其实是根据焦点路径来定的。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值