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

    xiaoxiao2021-03-25  71

    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时间的传递其实是根据焦点路径来定的。

    转载请注明原文地址: https://ju.6miu.com/read-19924.html

    最新回复(0)