Android按键事件处理流程 KeyEvent

刚接触Android开发的时候,对touch、key事件的处理总是一知半解,一会是Activity里的方法,一会是各种View

中的,自己始终不清楚到底哪个在先哪个在后,总之对整个处理流程没能很好的把握。每次写这部分代码的时候都有些心虚,

因为我不是很清楚什么时候、以什么样的顺序被调用,大都是打下log看看,没问题就算ok了。但随着时间流逝,这种感觉一直

折磨着我。期间也在网上搜索了相关资料,但总感觉不是那么令人满意。自打开始研究Android源码起,这部分内容的分析早就

被列在我的TODO list上了。因为弄懂这部分处理逻辑对明明白白地写android程序实在是太重要了,所以今天我就带领大家看看

这部分的处理逻辑。touch事件的处理我将放在另一篇博客中介绍(相比KeyEvent,大体都一样,只是稍微复杂些)。

  为了突出本文的重点,我们直接从事件被派发到View层次结构的根节点DecorView开始分析,这里我们先来看看DecorView#

dispatchKeyEvent方法,代码如下:

01. @Override
02. public boolean dispatchKeyEvent(KeyEvent event) {
03. final int keyCode = event.getKeyCode();
04. final int action = event.getAction();
05. final boolean isDown = action == KeyEvent.ACTION_DOWN;
06.  
07. /// 1. 第一次down事件的时候,处理panel的快捷键
08. if (isDown && (event.getRepeatCount() == 0)) {
09. // First handle chording of panel key: if a panel key is held
10. // but not released, try to execute a shortcut in it.
11. if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
12. boolean handled = dispatchKeyShortcutEvent(event);
13. if (handled) {
14. return true;
15. }
16. }
17.  
18. // If a panel is open, perform a shortcut on it without the
19. // chorded panel key
20. if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
21. if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
22. return true;
23. }
24. }
25. }
26.  
27. /// 2. 这里是我们本文的重点,当window没destroy且其Callback非空的话,交给其Callback处理
28. if (!isDestroyed()) { // Activity、Dialog都是Callback接口的实现
29. final Callback cb = getCallback(); // mFeatureId < 0 表示是application的DecorView,比如Activity、Dialog
30. final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event) // 派发给callback的方法
31. super.dispatchKeyEvent(event); // 否则直接派发到ViewGroup#dispatchKeyEvent(View层次结构)
32. if (handled) {
33. return true// 如果被上面的步骤处理了则直接返回true,不再往下传递
34. }
35. }
36.  
37. /// 3. 这是key事件的最后一步,如果到这一步还没处理掉,则派发到PhoneWindow对应的onKeyDown, onKeyUp方法
38. return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
39. : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
40. }

  接下来我们按照这个派发顺序依次来看看相关方法的实现,这里先看看Activity(Callback)的dispatchKeyEvent实现:

01. /**
02. * Called to process key events.  You can override this to intercept all
03. * key events before they are dispatched to the window.  Be sure to call
04. * this implementation for key events that should be handled normally.
05. *
06. * @param event The key event.
07. *
08. * @return boolean Return true if this event was consumed.
09. */
10. @Override
11. public boolean dispatchKeyEvent(KeyEvent event) {
12. /// 2.1. 回调接口,实际开发中用处不大,你感兴趣可以参看其方法doc
13. onUserInteraction();
14. Window win = getWindow();
15. /// 2.2. 从这里事件的处理交给了与之相关的window对象,实质是派发到了view层次结构
16. if (win.superDispatchKeyEvent(event)) {
17. return true;
18. }
19. View decor = mDecor;
20. if (decor == null) decor = win.getDecorView();
21. /// 2.3. 到这里如果view层次结构没处理则交给KeyEvent本身的dispatch方法,Activity的各种回调方法会被触发
22. return event.dispatch(this, decor != null
23. ? decor.getKeyDispatcherState() : nullthis);
24. }

紧接着我们看看,Window#superDispatchKeyEvent方法,相关代码如下:

01. <!-- Window.java -->
02. /**
03. * Used by custom windows, such as Dialog, to pass the key press event
04. * further down the view hierarchy. Application developers should
05. * not need to implement or call this.
06. *
07. */
08. public abstract boolean superDispatchKeyEvent(KeyEvent event);
09.  
10. <!-- PhoneWindow.java -->
11.  
12.  
13. @Override
14. public boolean superDispatchKeyEvent(KeyEvent event) {
15. return mDecor.superDispatchKeyEvent(event);
16. }
17.  
18. <!-- DecorView.superDispatchKeyEvent -->
19.  
20. public boolean superDispatchKeyEvent(KeyEvent event) {
21. /// 2.2.1. 进入view层次结构了,即调用ViewGroup的对应实现了。。。
22. if (super.dispatchKeyEvent(event)) {
23. return true// 如果被view层次结构处理了则直接返回true。
24. }
25.  
26. // Not handled by the view hierarchy, does the action bar want it
27. // to cancel out of something special?
28. /// 2.2.2. ActionBar对BACK key的特殊处理
29. if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
30. final int action = event.getAction();
31. // Back cancels action modes first.
32. if (mActionMode != null) {
33. if (action == KeyEvent.ACTION_UP) {
34. mActionMode.finish();
35. }
36. return true;
37. }
38.  
39. // Next collapse any expanded action views.
40. if (mActionBar != null && mActionBar.hasExpandedActionView()) {
41. if (action == KeyEvent.ACTION_UP) {
42. mActionBar.collapseActionView();
43. }
44. return true;
45. }
46. }
47. /// 2.2.3. 最后返回false表示没处理掉,会接着2.3.步骤处理
48. return false;
49. }

然后我们接着看看2.2.1.包括的小步骤,即ViewGroup#dispatchKeyEvent的实现,代码如下:

01. @Override
02. public boolean dispatchKeyEvent(KeyEvent event) {
03. /// 2.2.1.1. keyevent一致性检测用的,可忽略。。。
04. if (mInputEventConsistencyVerifier != null) {
05. mInputEventConsistencyVerifier.onKeyEvent(event, 1);
06. }
07.  
08. if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
09. == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
10. /// 2.2.1.2. 如果此ViewGroup是focused或者具体的大小被设置了,则交给他处理,即调用View的实现
11. if (super.dispatchKeyEvent(event)) {
12. return true;
13. }
14. else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
15. == PFLAG_HAS_BOUNDS) {
16. /// 2.2.1.3. 否则,如果此ViewGroup中有focused的child,且child有具体的大小,则交给mFocused处理
17. if (mFocused.dispatchKeyEvent(event)) { // 注意这里可能是个递归调用
18. return true// 我们可以看到并不是每个child都能响应key事件,前提必须是focused child才有机会响应
19. }
20. }
21.  
22. if (mInputEventConsistencyVerifier != null) {
23. mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
24. }
25. /// 2.2.1.4. 最后都没被处理返回false,2.2.2.步骤会接着执行。。。
26. return false;
27. }

这里我们可以看出对KeyEvent来说在View层次结构中,如果ViewGroup条件满足则会优先处理事件而不是先派发给其孩子view,

这一点和touch事件有所不同。这里我们看看View的dispatchKeyEvent实现:

01. /**
02. * Dispatch a key event to the next view on the focus path. This path runs
03. * from the top of the view tree down to the currently focused view. If this
04. * view has focus, it will dispatch to itself. Otherwise it will dispatch
05. * the next node down the focus path. This method also fires any key
06. * listeners.
07. *
08. * @param event The key event to be dispatched.
09. * @return True if the event was handled, false otherwise.
10. */
11. public boolean dispatchKeyEvent(KeyEvent event) {
12. if (mInputEventConsistencyVerifier != null) {
13. mInputEventConsistencyVerifier.onKeyEvent(event, 0);
14. }
15.  
16. // Give any attached key listener a first crack at the event.
17. //noinspection SimplifiableIfStatement
18. ListenerInfo li = mListenerInfo;
19. /// 2.2.1.2(3).1. 调用onKeyListener,如果它非空且view是ENABLED状态,监听器优先触发
20. if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
21. && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
22. return true;
23. }
24.  
25. /// 2.2.1.2(3).2. 调用KeyEvent.dispatch方法,并将view对象本身作为参数传递进去,view的各种callback方法在这里被触发
26. if (event.dispatch(this, mAttachInfo != null
27. ? mAttachInfo.mKeyDispatchState : nullthis)) {
28. return true;
29. }
30.  
31. if (mInputEventConsistencyVerifier != null) {
32. mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
33. }
34. /// 2.2.1.2(3).3. 还没处理掉返回false,接着2.2.1.4.执行
35. return false;
36. }

不管是这里的2.2.1.2(3).2.步骤还是前面Activity里的2.3.步骤,都调到了KeyEvent.dispatch方法,不过在看其代码之前我们

先来看看这里用到的mAttachInfo.mKeyDispatchState对象是咋来的,代码如下:

001. // 这句代码位于View.AttachInfo类里
002. final KeyEvent.DispatcherState mKeyDispatchState
003. new KeyEvent.DispatcherState();
004.  
005. /**
006. * Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState}
007. * for this view's window.  Returns null if the view is not currently attached
008. * to the window.  Normally you will not need to use this directly, but
009. * just use the standard high-level event callbacks like
010. * {@link #onKeyDown(int, KeyEvent)}.
011. */
012. public KeyEvent.DispatcherState getKeyDispatcherState() {
013. return mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null;
014. }
015.  
016. // KeyEvent.DispatcherState类
017. /**
018. * Use with {@link KeyEvent#dispatch(Callback, DispatcherState, Object)}
019. * for more advanced key dispatching, such as long presses.
020. */
021. public static class DispatcherState {
022. int mDownKeyCode;
023. Object mDownTarget;
024. SparseIntArray mActiveLongPresses = new SparseIntArray();
025.  
026. /**
027. * Reset back to initial state.
028. */
029. public void reset() { // 清空内部状态
030. if (DEBUG) Log.v(TAG, "Reset: " this);
031. mDownKeyCode = 0;
032. mDownTarget = null;
033. mActiveLongPresses.clear();
034. }
035.  
036. /**
037. * Stop any tracking associated with this target.
038. */
039. public void reset(Object target) { // 清空target对应的内部状态
040. if (mDownTarget == target) { // 只有相同时才清空,否则啥也不做
041. if (DEBUG) Log.v(TAG, "Reset in " + target + ": " this);
042. mDownKeyCode = 0;
043. mDownTarget = null;
044. }
045. }
046.  
047. /**
048. * Start tracking the key code associated with the given event.  This
049. * can only be called on a key down.  It will allow you to see any
050. * long press associated with the key, and will result in
051. * {@link KeyEvent#isTracking} return true on the long press and up
052. * events.
053. *
054. * <p>This is only needed if you are directly dispatching events, rather
055. * than handling them in {@link Callback#onKeyDown}.
056. */
057. public void startTracking(KeyEvent event, Object target) {
058. if (event.getAction() != ACTION_DOWN) { // 状态检测
059. throw new IllegalArgumentException(
060. "Can only start tracking on a down event");
061. }
062. if (DEBUG) Log.v(TAG, "Start trackingt in " + target + ": " this);
063. mDownKeyCode = event.getKeyCode(); // 赋值,表示正在track某个keycode
064. mDownTarget = target;
065. }
066.  
067. /**
068. * Return true if the key event is for a key code that is currently
069. * being tracked by the dispatcher.
070. */
071. public boolean isTracking(KeyEvent event) {
072. return mDownKeyCode == event.getKeyCode();
073. }
074.  
075. /**
076. * Keep track of the given event's key code as having performed an
077. * action with a long press, so no action should occur on the up.
078. * <p>This is only needed if you are directly dispatching events, rather
079. * than handling them in {@link Callback#onKeyLongPress}.
080. */
081. public void performedLongPress(KeyEvent event) {// 用来记录发生了生理长按事件
082. mActiveLongPresses.put(event.getKeyCode(), 1);
083. }
084.  
085. /**
086. * Handle key up event to stop tracking.  This resets the dispatcher state,
087. * and updates the key event state based on it.
088. * <p>This is only needed if you are directly dispatching events, rather
089. * than handling them in {@link Callback#onKeyUp}.
090. */
091. public void handleUpEvent(KeyEvent event) {
092. final int keyCode = event.getKeyCode();
093. if (DEBUG) Log.v(TAG, "Handle key up " + event + ": " this);
094. int index = mActiveLongPresses.indexOfKey(keyCode);
095. if (index >= 0) { // 如果发生过生理长按则设置event.mFlags为CACELED,这样在接下来的receiver.onKeyUp中有些处理就不会发生了
096. if (DEBUG) Log.v(TAG, "  Index: " + index); // 因为事件被标记为CANCELED了
097. event.mFlags |= FLAG_CANCELED | FLAG_CANCELED_LONG_PRESS;
098. mActiveLongPresses.removeAt(index);
099. }
100. if (mDownKeyCode == keyCode) {
101. if (DEBUG) Log.v(TAG, "  Tracking!");
102. event.mFlags |= FLAG_TRACKING; // 设置event正确的mFlags,接下来的receiver.onKeyUp可能会检测此状态
103. mDownKeyCode = 0// reset,表示此keycode的tracking到此结束了
104. mDownTarget = null;
105. }
106. }
107. }

大概了解了KeyEvent.DispatcherState类,我们就可以来看看KeyEvent.dispatch方法了,代码如下:

01. /**
02. * Deliver this key event to a {@link Callback} interface.  If this is
03. * an ACTION_MULTIPLE event and it is not handled, then an attempt will
04. * be made to deliver a single normal event.
05. *
06. * @param receiver The Callback that will be given the event.
07. * @param state State information retained across events.
08. * @param target The target of the dispatch, for use in tracking.
09. *
10. * @return The return value from the Callback method that was called.
11. */
12. public final boolean dispatch(Callback receiver, DispatcherState state,
13. Object target) {
14. switch (mAction) {
15. case ACTION_DOWN: { // DOWN事件
16. mFlags &= ~FLAG_START_TRACKING; //先清掉START_TRACKING标记
17. if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
18. ": " this);
19. boolean res = receiver.onKeyDown(mKeyCode, this); // 回调Callback接口的onKeyDown方法,View和Activity都是此接口的实现者
20. if (state != null) { // 一般都成立
21. if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
22. if (DEBUG) Log.v(TAG, "  Start tracking!"); // receiver.onKeyDown返回true了且不是repeated
23. state.startTracking(this, target); // 并且也没有开始tracking,则开始tracking当前的KeyEvent和target
24. else if (isLongPress() && state.isTracking(this)) { // 处理生理长按
25. try // 检测到生理长按则调用receiver.onKeyLongPress方法
26. if (receiver.onKeyLongPress(mKeyCode, this)) {
27. if (DEBUG) Log.v(TAG, "  Clear from long press!");
28. state.performedLongPress(this); // 记录此event已经有生理long press发生了。。。
29. res = true// 设置为处理了
30. }
31. catch (AbstractMethodError e) {
32. }
33. }
34. }
35. return res; // 返回down事件处理的结果
36. }
37. case ACTION_UP: // UP事件
38. if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
39. ": " this);
40. if (state != null) {
41. state.handleUpEvent(this); // reset state的内部状态,也改变了KeyEvent的某些状态
42. }
43. return receiver.onKeyUp(mKeyCode, this); // 最后调用receiver.onKeyUp方法
44. case ACTION_MULTIPLE: // 这里可以忽略掉
45. final int count = mRepeatCount;
46. final int code = mKeyCode;
47. if (receiver.onKeyMultiple(code, count, this)) {
48. return true;
49. }
50. if (code != KeyEvent.KEYCODE_UNKNOWN) {
51. mAction = ACTION_DOWN;
52. mRepeatCount = 0;
53. boolean handled = receiver.onKeyDown(code, this);
54. if (handled) {
55. mAction = ACTION_UP;
56. receiver.onKeyUp(code, this);
57. }
58. mAction = ACTION_MULTIPLE;
59. mRepeatCount = count;
60. return handled;
61. }
62. return false;
63. }
64. return false;
65. }

  看完了KeyEvent的具体实现,我们接着看看receiver(Callback接口)的onKeyDown、onKeyUp实现,先来看View相关的,代码如下:

001. /**
002. * Default implementation of {@link KeyEvent.Callback#onKeyDown(int, KeyEvent)
003. * KeyEvent.Callback.onKeyDown()}: perform press of the view
004. * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link KeyEvent#KEYCODE_ENTER}
005. * is released, if the view is enabled and clickable.
006. *
007. * <p>Key presses in software keyboards will generally NOT trigger this listener,
008. * although some may elect to do so in some situations. Do not rely on this to
009. * catch software key presses.
010. *
011. * @param keyCode A key code that represents the button pressed, from
012. *                {@link android.view.KeyEvent}.
013. * @param event   The KeyEvent object that defines the button action.
014. */
015. public boolean onKeyDown(int keyCode, KeyEvent event) {
016. boolean result = false;
017.  
018. if (KeyEvent.isConfirmKey(keyCode)) { // 只处理KEYCODE_DPAD_CENTER、KEYCODE_ENTER这2个按键
019. if ((mViewFlags & ENABLED_MASK) == DISABLED) {
020. return true// 针对disabled View直接返回true表示处理过了
021. }
022. // Long clickable items don't necessarily have to be clickable
023. if (((mViewFlags & CLICKABLE) == CLICKABLE ||
024. (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
025. (event.getRepeatCount() == 0)) { // clickable或者long_clickable且是第一次down事件
026. setPressed(true); // 标记pressed,你可能设置了View不同的background,这时候就会有所体现(比如高亮效果)
027. checkForLongClick(0); // 启动View的long click检测
028. return true// 到达这一步就表示KeyEvent被处理掉了
029. }
030. }
031. return result;
032. }
033.  
034. /**
035. * Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent)
036. * KeyEvent.Callback.onKeyUp()}: perform clicking of the view
037. * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or
038. * {@link KeyEvent#KEYCODE_ENTER} is released.
039. * <p>Key presses in software keyboards will generally NOT trigger this listener,
040. * although some may elect to do so in some situations. Do not rely on this to
041. * catch software key presses.
042. *
043. * @param keyCode A key code that represents the button pressed, from
044. *                {@link android.view.KeyEvent}.
045. * @param event   The KeyEvent object that defines the button action.
046. */
047. public boolean onKeyUp(int keyCode, KeyEvent event) {
048. if (KeyEvent.isConfirmKey(keyCode)) { // 同onKeyDown,默认也只处理confirm key
049. if ((mViewFlags & ENABLED_MASK) == DISABLED) {
050. return true// 同样的逻辑,如果是DISABLED view,直接返回true表示处理过了
051. }
052. if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {
053. setPressed(false); // 重置pressed状态
054.  
055. if (!mHasPerformedLongPress) { // 长按没发生的话,
056. // This is a tap, so remove the longpress check
057. removeLongPressCallback(); // 当up事件发生的时候,移除这些已经没用的callback
058. return performClick(); // 调用单击onClick监听器
059. }
060. }
061. }
062. return false// 其他所有的Key默认不处理
063. }
064.  
065. /**
066. * Sets the pressed state for this view.
067. *
068. * @see #isClickable()
069. * @see #setClickable(boolean)
070. *
071. * @param pressed Pass true to set the View's internal state to "pressed", or false to reverts
072. *        the View's internal state from a previously set "pressed" state.
073. */
074. public void setPressed(boolean pressed) {
075. final boolean needsRefresh = pressed != ((mPrivateFlags & PFLAG_PRESSED) == PFLAG_PRESSED);
076.  
077. if (pressed) {
078. mPrivateFlags |= PFLAG_PRESSED;
079. else {
080. mPrivateFlags &= ~PFLAG_PRESSED;
081. }
082.  
083. if (needsRefresh) {
084. refreshDrawableState(); // 这行代码会刷新View的显示状态
085. }
086. dispatchSetPressed(pressed);
087. }
088.  
089. private void checkForLongClick(int delayOffset) {
090. if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { // 必须得是LONG_CLICKABLE的View
091. mHasPerformedLongPress = false// 设置初始值
092.  
093. if (mPendingCheckForLongPress == null) { // 只非空的时候才new一个
094. mPendingCheckForLongPress = new CheckForLongPress();
095. }
096. mPendingCheckForLongPress.rememberWindowAttachCount();
097. postDelayed(mPendingCheckForLongPress, // post一个Runnable,注意延迟是个差值,而不是delayOffset
098. ViewConfiguration.getLongPressTimeout() - delayOffset);
099. }
100. }
101.  
102. class CheckForLongPress implements Runnable {
103.  
104. private int mOriginalWindowAttachCount;
105.  
106. public void run() {
107. if (isPressed() && (mParent != null// 当时间到了,此Runnable没被移除掉的话,并且这些条件都满足的时候,
108. && mOriginalWindowAttachCount == mWindowAttachCount) {
109. if (performLongClick()) { // 客户端定义的onLongClickListener监听器被触发
110. mHasPerformedLongPress = true// 只有当被上面的方法处理掉了,才表示LongPress发生过了
111. }
112. }
113. }
114.  
115. public void rememberWindowAttachCount() {
116. mOriginalWindowAttachCount = mWindowAttachCount;
117. }
118. }
119.  
120. /**
121. * Call this view's OnLongClickListener, if it is defined. Invokes the context menu if the
122. * OnLongClickListener did not consume the event.
123. *
124. * @return True if one of the above receivers consumed the event, false otherwise.
125. */
126. public boolean performLongClick() {
127. sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
128.  
129. boolean handled = false;
130. ListenerInfo li = mListenerInfo;
131. if (li != null && li.mOnLongClickListener != null) { // 优先触发监听器
132. handled = li.mOnLongClickListener.onLongClick(View.this);
133. }
134. if (!handled) { // 如果还没处理,显示ContextMenu如果定义了的话
135. handled = showContextMenu();
136. }
137. if (handled) {
138. performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
139. }
140. return handled; // 返回处理结果
141. }

  接下来,看看Activity对应的onKeyDown,onKeyUp方法:

001. /**
002. * Called when a key was pressed down and not handled by any of the views
003. * inside of the activity. So, for example, key presses while the cursor
004. * is inside a TextView will not trigger the event (unless it is a navigation
005. * to another object) because TextView handles its own key presses.
006. *
007. * <p>If the focused view didn't want this event, this method is called.
008. *
009. * <p>The default implementation takes care of {@link KeyEvent#KEYCODE_BACK}
010. * by calling {@link #onBackPressed()}, though the behavior varies based
011. * on the application compatibility mode: for
012. * {@link android.os.Build.VERSION_CODES#ECLAIR} or later applications,
013. * it will set up the dispatch to call {@link #onKeyUp} where the action
014. * will be performed; for earlier applications, it will perform the
015. * action immediately in on-down, as those versions of the platform
016. * behaved.
017. *
018. * <p>Other additional default key handling may be performed
019. * if configured with {@link #setDefaultKeyMode}.
020. *
021. * @return Return <code>true</code> to prevent this event from being propagated
022. * further, or <code>false</code> to indicate that you have not handled
023. * this event and it should continue to be propagated.
024. * @see #onKeyUp
025. * @see android.view.KeyEvent
026. */
027. public boolean onKeyDown(int keyCode, KeyEvent event)  {
028. if (keyCode == KeyEvent.KEYCODE_BACK) {
029. if (getApplicationInfo().targetSdkVersion
030. >= Build.VERSION_CODES.ECLAIR) {
031. event.startTracking();
032. else {
033. onBackPressed();
034. }
035. return true;
036. }
037.  
038. if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
039. return false;
040. else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
041. if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
042. keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
043. return true;
044. }
045. return false;
046. else {
047. // Common code for DEFAULT_KEYS_DIALER & DEFAULT_KEYS_SEARCH_*
048. boolean clearSpannable = false;
049. boolean handled;
050. if ((event.getRepeatCount() != 0) || event.isSystem()) {
051. clearSpannable = true;
052. handled = false;
053. else {
054. handled = TextKeyListener.getInstance().onKeyDown(
055. null, mDefaultKeySsb, keyCode, event);
056. if (handled && mDefaultKeySsb.length() > 0) {
057. // something useable has been typed - dispatch it now.
058.  
059. final String str = mDefaultKeySsb.toString();
060. clearSpannable = true;
061.  
062. switch (mDefaultKeyMode) {
063. case DEFAULT_KEYS_DIALER:
064. Intent intent = new Intent(Intent.ACTION_DIAL,  Uri.parse("tel:" + str));
065. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
066. startActivity(intent);
067. break;
068. case DEFAULT_KEYS_SEARCH_LOCAL:
069. startSearch(str, falsenullfalse);
070. break;
071. case DEFAULT_KEYS_SEARCH_GLOBAL:
072. startSearch(str, falsenulltrue);
073. break;
074. }
075. }
076. }
077. if (clearSpannable) {
078. mDefaultKeySsb.clear();
079. mDefaultKeySsb.clearSpans();
080. Selection.setSelection(mDefaultKeySsb,0);
081. }
082. return handled;
083. }
084. }
085.  
086. /**
087. * Called when a key was released and not handled by any of the views
088. * inside of the activity. So, for example, key presses while the cursor
089. * is inside a TextView will not trigger the event (unless it is a navigation
090. * to another object) because TextView handles its own key presses.
091. *
092. * <p>The default implementation handles KEYCODE_BACK to stop the activity
093. * and go back.
094. *
095. * @return Return <code>true</code> to prevent this event from being propagated
096. * further, or <code>false</code> to indicate that you have not handled
097. * this event and it should continue to be propagated.
098. * @see #onKeyDown
099. * @see KeyEvent
100. */
101. public boolean onKeyUp(int keyCode, KeyEvent event) {
102. if (getApplicationInfo().targetSdkVersion
103. >= Build.VERSION_CODES.ECLAIR) {
104. if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
105. && !event.isCanceled()) {
106. onBackPressed();
107. return true;
108. }
109. }
110. return false;
111. }

  最后是3.步骤,回到一开始DecorView.dispatchKeyEvent的最后几行代码,我们来看看PhoneWindow对应的onKeyDown,onKeyUp方法:

001. /**
002. * A key was pressed down and not handled by anything else in the window.
003. *
004. * @see #onKeyUp
005. * @see android.view.KeyEvent
006. */
007. protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) {
008. /* ****************************************************************************
009. * HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.
010. *
011. * If your key handling must happen before the app gets a crack at the event,
012. * it goes in PhoneWindowManager.
013. *
014. * If your key handling should happen in all windows, and does not depend on
015. * the state of the current application, other than that the current
016. * application can override the behavior by handling the event itself, it
017. * should go in PhoneFallbackEventHandler.
018. *
019. * Only if your handling depends on the window, and the fact that it has
020. * a DecorView, should it go here.
021. * ****************************************************************************/
022.  
023. final KeyEvent.DispatcherState dispatcher =
024. mDecor != null ? mDecor.getKeyDispatcherState() : null;
025. //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount()
026. //        + " flags=0x" + Integer.toHexString(event.getFlags()));
027.  
028. switch (keyCode) {
029. case KeyEvent.KEYCODE_VOLUME_UP: // key event处理中的最后一步,
030. case KeyEvent.KEYCODE_VOLUME_DOWN:
031. case KeyEvent.KEYCODE_VOLUME_MUTE: { // 处理音量调节键
032. // Similar code is in PhoneFallbackEventHandler in case the window
033. // doesn't have one of these.  In this case, we execute it here and
034. // eat the event instead, because we have mVolumeControlStreamType
035. // and they don't.
036. getAudioManager().handleKeyDown(event, mVolumeControlStreamType);
037. return true;
038. }
039.  
040. case KeyEvent.KEYCODE_MENU: {
041. onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event);
042. return true;
043. }
044.  
045. case KeyEvent.KEYCODE_BACK: {
046. if (event.getRepeatCount() > 0break;
047. if (featureId < 0break;
048. // Currently don't do anything with long press.
049. if (dispatcher != null) {
050. dispatcher.startTracking(event, this);
051. }
052. return true;
053. }
054.  
055. }
056.  
057. return false;
058. }
059.  
060. /**
061. * A key was released and not handled by anything else in the window.
062. *
063. * @see #onKeyDown
064. * @see android.view.KeyEvent
065. */
066. protected boolean onKeyUp(int featureId, int keyCode, KeyEvent event) {
067. final KeyEvent.DispatcherState dispatcher =
068. mDecor != null ? mDecor.getKeyDispatcherState() : null;
069. if (dispatcher != null) {
070. dispatcher.handleUpEvent(event);
071. }
072. //Log.i(TAG, "Key up: repeat=" + event.getRepeatCount()
073. //        + " flags=0x" + Integer.toHexString(event.getFlags()));
074.  
075. switch (keyCode) {
076. case KeyEvent.KEYCODE_VOLUME_UP:
077. case KeyEvent.KEYCODE_VOLUME_DOWN:
078. case KeyEvent.KEYCODE_VOLUME_MUTE: {
079. // Similar code is in PhoneFallbackEventHandler in case the window
080. // doesn't have one of these.  In this case, we execute it here and
081. // eat the event instead, because we have mVolumeControlStreamType
082. // and they don't.
083. getAudioManager().handleKeyUp(event, mVolumeControlStreamType);
084. return true;
085. }
086.  
087. case KeyEvent.KEYCODE_MENU: {
088. onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
089. event);
090. return true;
091. }
092.  
093. case KeyEvent.KEYCODE_BACK: {
094. if (featureId < 0break;
095. if (event.isTracking() && !event.isCanceled()) {
096. if (featureId == FEATURE_OPTIONS_PANEL) {
097. PanelFeatureState st = getPanelState(featureId, false);
098. if (st != null && st.isInExpandedMode) {
099. // If the user is in an expanded menu and hits back, it
100. // should go back to the icon menu
101. reopenMenu(true);
102. return true;
103. }
104. }
105. closePanel(featureId);
106. return true;
107. }
108. break;
109. }
110.  
111. case KeyEvent.KEYCODE_SEARCH: {
112. /*
113. * Do this in onKeyUp since the Search key is also used for
114. * chording quick launch shortcuts.
115. */
116. if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
117. break;
118. }
119. if (event.isTracking() && !event.isCanceled()) {
120. launchDefaultSearch();
121. }
122. return true;
123. }
124. }
125.  
126. return false;
127. }

  至此所有按键事件的处理就分析完毕了,鉴于篇幅略长,我们最后稍微总结下。主要有这么几点:

1. View的各种KeyEvent.Callback接口早于Activity的对应接口被调用;

2. 整个处理环节中只要有一处表明处理掉了,则处理结束,不在往下传递;

3. 各种Callback接口的处理优先级低于监听器,也就是说各种onXXXListener的方法优先被调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值