<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] 本篇文章主要分析應用層在接收到按鍵事件后的分發流程,對于 Framework 層獲得按鍵事件、分發傳遞給應用層等相關知識,在后期 Framework 層源碼學習時再做分析。 項目中只要在 Activity 中重寫 dispatchKeyEvent()方法,就可以進行事件處理和攔截了,那我們從 Activity 類的 dispatchKeyEvent()方法看起: # 分發順序 在分發的任何一步有分發者進行消費,即返回 true 時,事件停止傳遞。 Activity 的分發 * Activity 接收到事件,為 Menu 鍵事件就直接消費掉,否則分發給 PhoneWindow,PhoneWindow 直接把事件傳遞給其 DecorView * DecorView 對 Back 鍵判斷決定是否消費,不消費則分發給 ViewGroup * ViewGroup 不消費時,事件傳遞給 KeyEvent ViewGroup 的分發 * ViewGroup 有焦點且大小確定時,首先自己處理(調用其父類 View 的 dispatchKeyEvent 方法) * Viewgroup 的子 View 有焦點且大小確定時,會向下分發給子 View * 當子 View 也有子 View 時,會層層向下分發,直到 View View 的分發 * View 首先把事件分發給 OnKeyListener 監聽器,監聽器不消費時,事件傳給 KeyEvent KeyEvent 的分發 * KeyEvent 回調 Activity 的 onKeyDown、onKeyUp、onKeyLongPress 方法 * KeyEvent 回調 View 的 onKeyDown、onKeyUp、onKeyLongPress 方法 事件的回傳 * 當 Activity 或 View 的 onKeyDown()、onKeyUp()方法也沒有消費事件時,事件開始回傳,首先給 KeyEvent 的 dispatch()方法 * 依次按 KeyEvent -> Activity -> DecorView -> PhoneWindow 或 KeyEvent -> View -> ViewGroup -> Activity -> DecorView -> PhoneWindow 的方向進行回傳,此處與觸摸事件的回傳方式相似 # 源碼分析 ## Activity 分發 ```java public boolean dispatchKeyEvent(KeyEvent event) { // 用戶交互時會回調 onUserInteraction(); // 當為菜單鍵時,ActionBar 打開菜單并消費事件 final int keyCode = event.getKeyCode(); if (keyCode == KeyEvent.KEYCODE_MENU && mActionBar != null && mActionBar.onMenuKeyEvent(event)) { return true; } else if (event.isCtrlPressed() && event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == '<') { // Capture the Control-< and send focus to the ActionBar final int action = event.getAction(); if (action == KeyEvent.ACTION_DOWN) { final ActionBar actionBar = getActionBar(); if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) { mEatKeyUpEvent = true; return true; } } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) { mEatKeyUpEvent = false; return true; } } // 獲得當前 Activity 對應的 Window 對象 Window win = getWindow(); // 事件分發給 PhoneWindow if (win.superDispatchKeyEvent(event)) { return true; } View decor = mDecor; if (decor == null) decor = win.getDecorView(); return event.dispatch(this, decor != null ? decor.getKeyDispatcherState() : null, this); } ``` 可以看到,在具體事件處理前,首先會回調 onUserInteraction() 方法。注意,在按鍵按下、抬起時都會回調改方法,但觸摸時,只有觸摸按下時會回調,移動、抬起時不會回調。 接下來判斷是不是 Menu 按鍵,是則進行事件處理。不是則把事件傳遞給 Activity 對應的 Window 對象,即 PhoneWindow,調用其 superDispatchKeyEvent(event) 方法,來看下這個方法: ## PhoneWindow 的分發 ```java public boolean superDispatchKeyEvent(KeyEvent event) { return mDecor.superDispatchKeyEvent(event); } ``` 可以看到,事件傳遞給了 PhoneWindow 的 DecorView 對象。來看看 DecorView 對象的處理: ```java public boolean superDispatchKeyEvent(KeyEvent event) { // 先判斷是不是 Back 按鍵事件 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { final int action = event.getAction(); // Back cancels action modes first. // 消費事件 if (mActionMode != null) { if (action == KeyEvent.ACTION_UP) { mActionMode.finish(); } return true; } } // 傳遞事件給 ViewGroup return super.dispatchKeyEvent(event); } ``` 接下來看看 ViewGroup 的分發: ## ViewGroup 的分發 ```java public boolean dispatchKeyEvent(KeyEvent event) { // 進行一致性檢查 if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onKeyEvent(event, 1); } // 當 ViewGroup 已獲取焦點并且大小已確定時 if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) { // 調用其父類即 View 的 dispatchKeyEvent()方法 if (super.dispatchKeyEvent(event)) { return true; } } // 當 ViewGroup 擁有子 View,并且子 View 已獲取到焦點并且大小已確定,事件分發給該子 View else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS) == PFLAG_HAS_BOUNDS) { // 當 mFocused 為 ViewGroup 時,事件會進行遞歸傳遞 if (mFocused.dispatchKeyEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 1); } return false; } ``` 當上面的 mFocused 為 View 時,事件會分發給 View,來看下代碼: ## View 的分發 ```java public boolean dispatchKeyEvent(KeyEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onKeyEvent(event, 0); } // Give any attached key listener a first crack at the event. //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; // 事件首先分發給 View 的 mOnKeyListener 監聽器 if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) { return true; } // 分發給 KeyEvent if (event.dispatch(this, mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null, this)) { return true; } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; } ``` 事件首先分發給 mOnKeyListener 監聽器,再分發給 KeyEvent,接下來看看 KeyEvent 的事件分發: ## KeyEvent 的分發 ```java 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); // 調用 View 的 onKeyDown()方法 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); } // 回調 View 的 onKeyUp 方法 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; } ``` KeyEvent 的 dispatch 方法的第一個參數為 reveiver,可能為 Activity 對象,也可能為 View 對象,具體要看是在哪里傳遞過來的。 KeyEvent 的事件分發主要是根據事件類型來回調 View 的 onKeyDown(),onKeyUp(),onKeyLongPress()方法。 Activity 和 View 的 onKeyLongPress()方法都是默認返回 false: ```java public boolean onKeyLongPress(int keyCode, KeyEvent event) { return false; } ``` 可以重寫改方法進行一些長按操作。 接下來看看 View 的 onKeyDown(),onKeyUp(),onKeyLongPress()方法。 ## View 的 onKeyDown(),onKeyUp()方法 onKeyDown()方法 ```java public boolean onKeyDown(int keyCode, KeyEvent event) { // 按下按鍵,相當于點擊擁有焦點的 View ,包括(KEYCODE_DPAD_CENTER,KEYCODE_ENTER, KEYCODE_SPACE,KEYCODE_NUMPAD_ENTER) if (KeyEvent.isConfirmKey(keyCode)) { // 如果 View 設置不可按狀態,直接消費,相當于已經按下 if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } // View 是可點擊的或者是可長按的時 if (((mViewFlags & CLICKABLE) == CLICKABLE || (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) && (event.getRepeatCount() == 0)) { // For the purposes of menu anchoring and drawable hotspots, // key events are considered to be at the center of the view. final float x = getWidth() / 2f; final float y = getHeight() / 2f; // 設置按下的狀態 setPressed(true, x, y); // 檢查長按,這里和觸摸事件分發的長按事件判斷方式相同,可參考 checkForLongClick(0, x, y); return true; } } return false; } ``` onKeyUp()方法 ```java public boolean onKeyUp(int keyCode, KeyEvent event) { if (KeyEvent.isConfirmKey(keyCode)) { // 消費事件 if ((mViewFlags & ENABLED_MASK) == DISABLED) { return true; } if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) { // 去除按下狀態 setPressed(false); if (!mHasPerformedLongPress) { // 移除長按事件,進行單擊事件 removeLongPressCallback(); return performClick(); } } } return false; } ``` ## Activity 的 onKeyDown(),onKeyUp()方法 當 Activity 內部的 ViewGroup 不消費事件時,事件由 Activity 傳遞給 KeyEvent。KeyEvent 的 dispatch()方法調用的就是 Activity 的 onKeyDown() 和 onKeyUp()方法了,來看看: onKeyDown()方法 ```java public boolean onKeyDown(int keyCode, KeyEvent event) { // 事件為返回鍵時 if (keyCode == KeyEvent.KEYCODE_BACK) { if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.ECLAIR) { // Android 2.1 以上版本開始跟蹤按鍵事件 event.startTracking(); } else { // 2.1 以下版本直接返回 onBackPressed(); } return true; } ... } ``` onKeyUp()方法 ```java public boolean onKeyUp(int keyCode, KeyEvent event) { // 大于 Android 2.1 版本時 if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.ECLAIR) { if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() && !event.isCanceled()) { onBackPressed(); return true; } } return false; } ``` 可以看到,在大于 2.1 版本時,按下返回鍵到 onKeyUp()這里才被處理。我們也可以重寫 onBackPressed()方法來實現自己想要的需求。 至此,應用層的按鍵事件分發流程分析也就完成了,Framework 層的分析將在后期進行。 參考文檔: [Android 源碼](http://androidxref.com/7.1.1_r6/) [按鍵事件傳遞流程(二)](http://andevele.com/2016/07/08/keypad_second/)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看