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); 将消息派发给真正的视图。
- private void deliverKeyEvent(QueuedInputEvent q) {
- final KeyEvent event = (KeyEvent)q.mEvent;
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onKeyEvent(event, 0);
- }
-
- if ((q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
-
- if (mView == null || !mAdded) {
- finishInputEvent(q, false);
- return;
- }
-
- if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
-
-
- if (mView.dispatchKeyEventPreIme(event)) {
- finishInputEvent(q, true);
- return;
- }
-
-
-
- if (mLastWasImTarget) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- final int seq = event.getSequenceNumber();
- if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
- + seq + " event=" + event);
- imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
- return;
- }
- }
- }
-
-
- deliverKeyEventPostIme(q);
- }
1.2 ViewRootImpl.deliverKeyEventPostIme(q);
1. mView.dispatchKeyEvent(event); 将事件派发到View视图。因为mView就是一个DecorView,所以就是调用DecorView的dispatchKeyEvent的方法。
- private void deliverKeyEventPostIme(QueuedInputEvent q) {
- final KeyEvent event = (KeyEvent)q.mEvent;
-
- ... ...
-
- if (checkForLeavingTouchModeAndConsume(event)) {
- finishInputEvent(q, true);
- return;
- }
-
-
-
- mFallbackEventHandler.preDispatchKeyEvent(event);
-
-
- if (mView.dispatchKeyEvent(event)) {
- finishInputEvent(q, true);
- return;
- }
-
-
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && event.isCtrlPressed()
- && event.getRepeatCount() == 0
- && !KeyEvent.isModifierKey(event.getKeyCode())) {
- if (mView.dispatchKeyShortcutEvent(event)) {
- finishInputEvent(q, true);
- return;
- }
- }
-
-
- if (mFallbackEventHandler.dispatchKeyEvent(event)) {
- finishInputEvent(q, true);
- return;
- }
-
-
- 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) {
-
-
-
- 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));
- finishInputEvent(q, true);
- return;
- }
- }
-
-
- if (mView.dispatchUnhandledMove(focused, direction)) {
- finishInputEvent(q, true);
- return;
- }
- }
- }
- }
-
-
- finishInputEvent(q, false);
- }
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);
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- final int keyCode = event.getKeyCode();
- final int action = event.getAction();
- final boolean isDown = action == KeyEvent.ACTION_DOWN;
-
- if (isDown && (event.getRepeatCount() == 0)) {
-
-
- if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
- boolean handled = dispatchKeyShortcutEvent(event);
- if (handled) {
- return true;
- }
- }
-
-
-
- if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
- if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
- return true;
- }
- }
- }
-
- if (!isDestroyed()) {
- final Callback cb = getCallback();
- final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
- : super.dispatchKeyEvent(event);
- if (handled) {
- return true;
- }
- }
-
- return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
- : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
- }
1.2.1.2 DecorView.mDecor.superDispatchKeyEvent(event);
1. 调用父类的dispatchKeyEvent。
- public boolean superDispatchKeyEvent(KeyEvent event) {
- if (super.dispatchKeyEvent(event)) {
- return true;
- }
-
-
-
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
- final int action = event.getAction();
-
- if (mActionMode != null) {
- if (action == KeyEvent.ACTION_UP) {
- mActionMode.finish();
- }
- return true;
- }
-
-
- if (mActionBar != null && mActionBar.hasExpandedActionView()) {
- if (action == KeyEvent.ACTION_UP) {
- mActionBar.collapseActionView();
- }
- return true;
- }
- }
-
- return false;
- }
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。
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onKeyEvent(event, 1);
- }
-
- if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
- if (super.dispatchKeyEvent(event)) {
- return true;
- }
- } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
- if (mFocused.dispatchKeyEvent(event)) {
- return true;
- }
- }
-
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
- }
- return false;
- }
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去做最后的处理
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onKeyEvent(event, 0);
- }
-
-
-
- ListenerInfo li = mListenerInfo;
- if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
- && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
- return true;
- }
-
- if (event.dispatch(this, mAttachInfo != null
- ? mAttachInfo.mKeyDispatchState : null, this)) {
- return true;
- }
-
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
- }
- return false;
- }
1.2.1.2.1.2.2 KeyEvent.dispatch
1. KeyEvent.dispatch会根据Action的类型调用receiver的对应的onXXX函数去处理。(receiver通常都是View)
2. 如果所有的View都没有处理这个KeyEvent,那么会最终通过KeyEvent调用Activity的onXXX函数。
- public final boolean dispatch(Callback receiver, DispatcherState state,
- Object target) {
- switch (mAction) {
- case ACTION_DOWN: {
- mFlags &= ~FLAG_START_TRACKING;
- if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
- + ": " + this);
- boolean res = receiver.onKeyDown(mKeyCode, this);
- if (state != null) {
- if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
- if (DEBUG) Log.v(TAG, " Start tracking!");
- state.startTracking(this, target);
- } else if (isLongPress() && state.isTracking(this)) {
- try {
- if (receiver.onKeyLongPress(mKeyCode, this)) {
- if (DEBUG) Log.v(TAG, " Clear from long press!");
- state.performedLongPress(this);
- res = true;
- }
- } catch (AbstractMethodError e) {
- }
- }
- }
- return res;
- }
- case ACTION_UP:
- if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
- + ": " + this);
- if (state != null) {
- state.handleUpEvent(this);
- }
- return receiver.onKeyUp(mKeyCode, this);
- case ACTION_MULTIPLE:
- final int count = mRepeatCount;
- final int code = mKeyCode;
- if (receiver.onKeyMultiple(code, count, this)) {
- return true;
- }
- if (code != KeyEvent.KEYCODE_UNKNOWN) {
- mAction = ACTION_DOWN;
- mRepeatCount = 0;
- boolean handled = receiver.onKeyDown(code, this);
- if (handled) {
- mAction = ACTION_UP;
- receiver.onKeyUp(code, this);
- }
- mAction = ACTION_MULTIPLE;
- mRepeatCount = count;
- return handled;
- }
- return false;
- }
- return false;
- }