现在的位置: 首页 > 移动开发 > 正文

Android 中keyEvent的消息处理

2019年07月29日 移动开发 ⁄ 共 13273字 ⁄ 字号 评论关闭

1. ViewRootImpl.deliverKeyEvent(QueuedInputEvent q)

      1. 如果mView为空或者mAdded为false,就直接调用finishInputEvent。

      2. mView.dispatchKeyEventPreIme(event), 在传递给IME之前做一些预处理。因为对于View来说,如果有输入窗口存在的话,会先将按键消息派发到输入窗口,只有当输入窗口没有处理这个事件,才会派发到真正的视图。因此如果想要在输入法截取事件前处理该消息,则可以重载这个方法去处理一些特定的按键消息。

      3. 如果有IME窗口存在,就把这个传递给IME进行处理。imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);

      4. deliverKeyEventPostIme(q);  将消息派发给真正的视图。

[java] view
plain
copy

  1. private void deliverKeyEvent(QueuedInputEvent q) {  
  2.     final KeyEvent event = (KeyEvent)q.mEvent;  
  3.     if (mInputEventConsistencyVerifier != null) {  
  4.         mInputEventConsistencyVerifier.onKeyEvent(event, 0);  
  5.     }  
  6.   
  7.     if ((q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {  
  8.         // If there is no view, then the event will not be handled.  
  9.         if (mView == null || !mAdded) {  
  10.             finishInputEvent(q, false);  
  11.             return;  
  12.         }  
  13.   
  14.         if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);  
  15.   
  16.         // Perform predispatching before the IME.  
  17.         if (mView.dispatchKeyEventPreIme(event)) {  
  18.             finishInputEvent(q, true);  
  19.             return;  
  20.         }  
  21.   
  22.         // Dispatch to the IME before propagating down the view hierarchy.  
  23.         // The IME will eventually call back into handleImeFinishedEvent.  
  24.         if (mLastWasImTarget) {  
  25.             InputMethodManager imm = InputMethodManager.peekInstance();  
  26.             if (imm != null) {  
  27.                 final int seq = event.getSequenceNumber();  
  28.                 if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="  
  29.                         + seq + " event=" + event);  
  30.                 imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);  
  31.                 return;  
  32.             }  
  33.         }  
  34.     }  
  35.   
  36.     // Not dispatching to IME, continue with post IME actions.  
  37.     deliverKeyEventPostIme(q);  
  38. }  


1.2  ViewRootImpl.deliverKeyEventPostIme(q);

         1. mView.dispatchKeyEvent(event);  将事件派发到View视图。因为mView就是一个DecorView,所以就是调用DecorView的dispatchKeyEvent的方法。

[java] view
plain
copy

  1. private void deliverKeyEventPostIme(QueuedInputEvent q) {  
  2.     final KeyEvent event = (KeyEvent)q.mEvent;  
  3.   
  4.     ... ...  
  5.     // If the key's purpose is to exit touch mode then we consume it and consider it handled.  
  6.     if (checkForLeavingTouchModeAndConsume(event)) {  
  7.         finishInputEvent(q, true);  
  8.         return;  
  9.     }  
  10.   
  11.     // Make sure the fallback event policy sees all keys that will be delivered to the  
  12.     // view hierarchy.  
  13.     mFallbackEventHandler.preDispatchKeyEvent(event);  
  14.   
  15.     // Deliver the key to the view hierarchy.  
  16.     if (mView.dispatchKeyEvent(event)) {  
  17.         finishInputEvent(q, true);  
  18.         return;  
  19.     }  
  20.   
  21.     // If the Control modifier is held, try to interpret the key as a shortcut.  
  22.     if (event.getAction() == KeyEvent.ACTION_DOWN  
  23.             && event.isCtrlPressed()  
  24.             && event.getRepeatCount() == 0  
  25.             && !KeyEvent.isModifierKey(event.getKeyCode())) {  
  26.         if (mView.dispatchKeyShortcutEvent(event)) {  
  27.             finishInputEvent(q, true);  
  28.             return;  
  29.         }  
  30.     }  
  31.   
  32.     // Apply the fallback event policy.  
  33.     if (mFallbackEventHandler.dispatchKeyEvent(event)) {  
  34.         finishInputEvent(q, true);  
  35.         return;  
  36.     }  
  37.   
  38.     // Handle automatic focus changes.  
  39.     if (event.getAction() == KeyEvent.ACTION_DOWN) {  
  40.         int direction = 0;  
  41.         switch (event.getKeyCode()) {  
  42.             case KeyEvent.KEYCODE_DPAD_LEFT:  
  43.                 if (event.hasNoModifiers()) {  
  44.                     direction = View.FOCUS_LEFT;  
  45.                 }  
  46.                 break;  
  47.             case KeyEvent.KEYCODE_DPAD_RIGHT:  
  48.                 if (event.hasNoModifiers()) {  
  49.                     direction = View.FOCUS_RIGHT;  
  50.                 }  
  51.                 break;  
  52.             case KeyEvent.KEYCODE_DPAD_UP:  
  53.                 if (event.hasNoModifiers()) {  
  54.                     direction = View.FOCUS_UP;  
  55.                 }  
  56.                 break;  
  57.             case KeyEvent.KEYCODE_DPAD_DOWN:  
  58.                 if (event.hasNoModifiers()) {  
  59.                     direction = View.FOCUS_DOWN;  
  60.                 }  
  61.                 break;  
  62.             case KeyEvent.KEYCODE_TAB:  
  63.                 if (event.hasNoModifiers()) {  
  64.                     direction = View.FOCUS_FORWARD;  
  65.                 } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {  
  66.                     direction = View.FOCUS_BACKWARD;  
  67.                 }  
  68.                 break;  
  69.         }  
  70.         if (direction != 0) {  
  71.             View focused = mView.findFocus();  
  72.             if (focused != null) {  
  73.                 View v = focused.focusSearch(direction);  
  74.                 if (v != null && v != focused) {  
  75.                     // do the math the get the interesting rect  
  76.                     // of previous focused into the coord system of  
  77.                     // newly focused view  
  78.                     focused.getFocusedRect(mTempRect);  
  79.                     if (mView instanceof ViewGroup) {  
  80.                         ((ViewGroup) mView).offsetDescendantRectToMyCoords(  
  81.                                 focused, mTempRect);  
  82.                         ((ViewGroup) mView).offsetRectIntoDescendantCoords(  
  83.                                 v, mTempRect);  
  84.                     }  
  85.                     if (v.requestFocus(direction, mTempRect)) {  
  86.                         playSoundEffect(SoundEffectConstants  
  87.                                 .getContantForFocusDirection(direction));  
  88.                         finishInputEvent(q, true);  
  89.                         return;  
  90.                     }  
  91.                 }  
  92.   
  93.                 // Give the focused view a last chance to handle the dpad key.  
  94.                 if (mView.dispatchUnhandledMove(focused, direction)) {  
  95.                     finishInputEvent(q, true);  
  96.                     return;  
  97.                 }  
  98.             }  
  99.         }  
  100.     }  
  101.   
  102.     // Key was unhandled.  
  103.     finishInputEvent(q, false);  
  104. }  


1.2.1 DecorView.dispatchKeyEvent(KeyEvent event)

     1. 如果是Down事件, 首先会判断是不是Shortcut事件也就是快捷键按钮或者特殊事件,如果是就会调用dispatchKeyShortcutEvent和performPanelShortcut进行一些处理然后返回。

     2. 如果不是快捷键按钮,就调用Callback Activity的cb.dispatchKeyEvent(event)去处理。Activity的dispatchKeyEvent中其实也还是去调用PhoneWindow中的superDispatchKeyEvent, PhoneWindow最终还是调用mDecor.superDispatchKeyEvent(event);

[java] view
plain
copy

  1. @Override  
  2. public boolean dispatchKeyEvent(KeyEvent event) {  
  3.     final int keyCode = event.getKeyCode();  
  4.     final int action = event.getAction();  
  5.     final boolean isDown = action == KeyEvent.ACTION_DOWN;  
  6.   
  7.     if (isDown && (event.getRepeatCount() == 0)) {  
  8.         // First handle chording of panel key: if a panel key is held  
  9.         // but not released, try to execute a shortcut in it.  
  10.         if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {  
  11.             boolean handled = dispatchKeyShortcutEvent(event);  
  12.             if (handled) {  
  13.                 return true;  
  14.             }  
  15.         }  
  16.   
  17.         // If a panel is open, perform a shortcut on it without the  
  18.         // chorded panel key  
  19.         if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {  
  20.             if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {  
  21.                 return true;  
  22.             }  
  23.         }  
  24.     }  
  25.   
  26.     if (!isDestroyed()) {  
  27.         final Callback cb = getCallback();  
  28.         final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)  
  29.                 : super.dispatchKeyEvent(event);  
  30.         if (handled) {  
  31.             return true;  
  32.         }  
  33.     }  
  34.   
  35.     return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)  
  36.             : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);  
  37. }  


1.2.1.2 DecorView.mDecor.superDispatchKeyEvent(event);

           1. 调用父类的dispatchKeyEvent。

[java] view
plain
copy

  1. public boolean superDispatchKeyEvent(KeyEvent event) {  
  2.      if (super.dispatchKeyEvent(event)) {  
  3.          return true;  
  4.      }  
  5.   
  6.      // Not handled by the view hierarchy, does the action bar want it  
  7.      // to cancel out of something special?  
  8.      if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {  
  9.          final int action = event.getAction();  
  10.          // Back cancels action modes first.  
  11.          if (mActionMode != null) {  
  12.              if (action == KeyEvent.ACTION_UP) {  
  13.                  mActionMode.finish();  
  14.              }  
  15.              return true;  
  16.          }  
  17.   
  18.          // Next collapse any expanded action views.  
  19.          if (mActionBar != null && mActionBar.hasExpandedActionView()) {  
  20.              if (action == KeyEvent.ACTION_UP) {  
  21.                  mActionBar.collapseActionView();  
  22.              }  
  23.              return true;  
  24.          }  
  25.      }  
  26.   
  27.      return false;  
  28.  }  



1.2.1.2.1 ViewGroup.dispatchKeyEvent(KeyEvent event)

        1. mPrivateFlags 是在View中定义的,标志当前View的属性。 这里吧的FOCUSED 和HAS_BOUNDS表示当前的View是否是focused和有bounds的。一般的View和ViewGroup都会有HAS_BOUNDS, 而mFocused记录这当前ViewGroup中处于focus的View。

             例如:如果A包含B,B又包含C,如果C是一个View,C的mPrivateFlags就是 FOCUSED | HAS_BOUNDS那么B的mPrivateFlags就是FOCUSED,mFocused就是C,同理A的mPrivateFlags也是FOCUSED,mFocused就是B。

        2. 所以一般情况下,ViewGroup通过mFocused.dispatchKeyEvent()递归把Event传到View。

[java] view
plain
copy

  1. public boolean dispatchKeyEvent(KeyEvent event) {  
  2.     if (mInputEventConsistencyVerifier != null) {  
  3.         mInputEventConsistencyVerifier.onKeyEvent(event, 1);  
  4.     }  
  5.   
  6.     if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {  
  7.         if (super.dispatchKeyEvent(event)) {  
  8.             return true;  
  9.         }  
  10.     } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {  
  11.         if (mFocused.dispatchKeyEvent(event)) {  
  12.             return true;  
  13.         }  
  14.     }  
  15.   
  16.     if (mInputEventConsistencyVerifier != null) {  
  17.         mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);  
  18.     }  
  19.     return false;  
  20. }  


1.2.1.2.1.2 View.dispatchKeyEvent()

            如果mFocused是ViewGroup那么还是调用1.2.1.2.1;如果是View,那么就调用View的dispatchKeyEvent。

            1. 如果有注册Listener,就直接调用Listener的onKey去相应,然后返回。

            2. 如果没有注册Listener,就调用KeyEvent.dispatch去做最后的处理

[java] view
plain
copy

  1. public boolean dispatchKeyEvent(KeyEvent event) {  
  2.     if (mInputEventConsistencyVerifier != null) {  
  3.         mInputEventConsistencyVerifier.onKeyEvent(event, 0);  
  4.     }  
  5.   
  6.     // Give any attached key listener a first crack at the event.  
  7.     //noinspection SimplifiableIfStatement  
  8.     ListenerInfo li = mListenerInfo;  
  9.     if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED  
  10.             && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {  
  11.         return true;  
  12.     }  
  13.   
  14.     if (event.dispatch(this, mAttachInfo != null  
  15.             ? mAttachInfo.mKeyDispatchState : nullthis)) {  
  16.         return true;  
  17.     }  
  18.   
  19.     if (mInputEventConsistencyVerifier != null) {  
  20.         mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);  
  21.     }  
  22.     return false;  
  23. }  


1.2.1.2.1.2.2 KeyEvent.dispatch

        1. KeyEvent.dispatch会根据Action的类型调用receiver的对应的onXXX函数去处理。(receiver通常都是View)

        2. 如果所有的View都没有处理这个KeyEvent,那么会最终通过KeyEvent调用Activity的onXXX函数。

[java] view
plain
copy

  1. public final boolean dispatch(Callback receiver, DispatcherState state,  
  2.         Object target) {  
  3.     switch (mAction) {  
  4.         case ACTION_DOWN: {  
  5.             mFlags &= ~FLAG_START_TRACKING;  
  6.             if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state  
  7.                     + ": " + this);  
  8.             boolean res = receiver.onKeyDown(mKeyCode, this);  
  9.             if (state != null) {  
  10.                 if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {  
  11.                     if (DEBUG) Log.v(TAG, "  Start tracking!");  
  12.                     state.startTracking(this, target);  
  13.                 } else if (isLongPress() && state.isTracking(this)) {  
  14.                     try {  
  15.                         if (receiver.onKeyLongPress(mKeyCode, this)) {  
  16.                             if (DEBUG) Log.v(TAG, "  Clear from long press!");  
  17.                             state.performedLongPress(this);  
  18.                             res = true;  
  19.                         }  
  20.                     } catch (AbstractMethodError e) {  
  21.                     }  
  22.                 }  
  23.             }  
  24.             return res;  
  25.         }  
  26.         case ACTION_UP:  
  27.             if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state  
  28.                     + ": " + this);  
  29.             if (state != null) {  
  30.                 state.handleUpEvent(this);  
  31.             }  
  32.             return receiver.onKeyUp(mKeyCode, this);  
  33.         case ACTION_MULTIPLE:  
  34.             final int count = mRepeatCount;  
  35.             final int code = mKeyCode;  
  36.             if (receiver.onKeyMultiple(code, count, this)) {  
  37.                 return true;  
  38.             }  
  39.             if (code != KeyEvent.KEYCODE_UNKNOWN) {  
  40.                 mAction = ACTION_DOWN;  
  41.                 mRepeatCount = 0;  
  42.                 boolean handled = receiver.onKeyDown(code, this);  
  43.                 if (handled) {  
  44.                     mAction = ACTION_UP;  
  45.                     receiver.onKeyUp(code, this);  
  46.                 }  
  47.                 mAction = ACTION_MULTIPLE;  
  48.                 mRepeatCount = count;  
  49.                 return handled;  
  50.             }  
  51.             return false;  
  52.     }  
  53.     return false;  
  54. }  

抱歉!评论已关闭.