1. 程式人生 > >Android6.0原始碼分析之menu鍵彈出popupwindow選單流程分析

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的彈起就分析完了