Android6.0原始碼分析之menu鍵彈出popupwindow選單流程分析
例如上圖,在按下選單鍵後會彈出對應的選單選項,準確來說,是在選單鍵彈起後出現的一個popupwindow,那麼從選單鍵彈起到popupwindow建立所涉及到的歷程是怎樣的呢?
理論上是底層監測menu按鍵鍵值,通知framework層,framework經過一系列的處理後分發給上層,或者攔截掉
現在是假設framework層已經把menu按鍵分發給了使用者,那麼接下里就是activity進行響應,所以從Activity開始看起,整體流程如下:
從按鍵彈起framework層分發到activity開始,到彈出popupwindow結束整個流程圖
程式碼所在目錄如下
流程圖中所涉及到的一些只是單純的作為中介呼叫了一下,按著流程自己也可以看到,現在是挑一些有用的進行分析
1,第一個需要分析的就是Activity.java檔案中的分發menu按鍵的方法
/** * Called to process key events. You can override this to intercept all * key events before they are dispatched to the window. Be sure to call * this implementation for key events that should be handled normally. * * @param event The key event. * * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event) { onUserInteraction(); // Let action bars open menus in response to the menu key prioritized over // the window handling it if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && mActionBar != null && mActionBar.onMenuKeyEvent(event)) { //當menu按鍵到來時,先判斷下actionBar是否為null,如果actionbar不為空再進行處理,即讓actionbar響應按鍵事件 return true; } Window win = getWindow();//獲取到當前activity的視窗例項 if (win.superDispatchKeyEvent(event)) { //如果acitonbar沒有對按鍵進行處理,則傳遞給activity所在視窗進行處理 return true; } View decor = mDecor;//先介紹一下,activity的佈局的根節點為DecorView,在這裡獲取到activity的根節點 if (decor == null) decor = win.getDecorView(); //如果actionbar沒有處理event,window視窗也沒處理或者攔截event,則傳遞給view進行處理 return event.dispatch(this, decor != null ? decor.getKeyDispatcherState() : null, this); }
處理分發按鍵事件時會呼叫,使用者也可以自己覆寫,來達到按自己的意願處理按鍵事件的目的,比如攔截按鍵傳遞給視窗,view等。
通過以上方法的分析可以總結出,一個menu事件在傳遞給activity後,如果不是menu事件就會直接交給window會向下傳遞,有三方可能進行處理
actionbar
window
view
這三個優先順序由高到低,也就是說,menu事件會首先傳遞給actionbar,如果actionbar進行了處理並且將事件攔截下來不派發給視窗,那麼menu事件到actionbar處理之後就結束了。反之則會向window和view進行傳遞。
2,流程中第二個值得分析的就在ActionMenuPresenter.java中
/**
* Display the overflow menu if one is present.
* @return true if the overflow menu was shown, false otherwise.
*/
public boolean showOverflowMenu() {
if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
//if條件進行了以下幾個判斷,mReserveOverflow判斷了是否顯示overflow按鈕,isOverflowMenuShowing表示了選單項的popupwindow是否正在顯示
//,以及要開啟選單項的程序是否已經初始化,選單項是否不是空
//初始化popupwindow物件
OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
開啟執行緒,載入popupwindow
mPostedOpenRunnable = new OpenOverflowRunnable(popup);
// Post this for later; we might still need a layout for the anchor to be right.
((View) mMenuView).post(mPostedOpenRunnable);
// ActionMenuPresenter uses null as a callback argument here
// to indicate overflow is opening.
super.onSubMenuSelected(null);
return true;
}
return false;
}
總結下來就是,該方法先對一些必要的條件做了一些判斷,比如menu是否存在,menu選單選項是否為空,menu的popup是否已經彈出,或者正在彈出,然後在進行popup物件的例項化,並開啟載入popup的執行緒。
private class OpenOverflowRunnable implements Runnable {
private OverflowPopup mPopup;
public OpenOverflowRunnable(OverflowPopup popup) {
mPopup = popup;
}
public void run() {
mMenu.changeMenuMode();
final View menuView = (View) mMenuView;
if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
mOverflowPopup = mPopup;
}
mPostedOpenRunnable = null;
}
}
在載入執行緒中修改menu的狀態模式,並且試著去彈出popup,mPopup.tryShow();
到這裡menu按下從activity分發到popup的彈起就分析完了