setSupportActionBar添加Toolbar後系統之後如何創建Menu

在正常的開發中我們會使用 setSupportActionBar(Toolbar toolbar)方法設置Toolbar,通過Toolbar我們可以添加Menu等菜單操作。那麼setSupportActionBar方法到底是如何設置了ToolBar並且關聯到Menu的?

首先從setSupportActionBar(mToolbar)看起。首先這個方法調用到了this.getDelegate().setSupportActionBar(toolbar);這裏通過代理的方式調用了的AppCompatDelegateImpl的setSupportActionBar(Toolbar toolbar)。

public void setSupportActionBar(Toolbar toolbar) {
        if (this.mOriginalWindowCallback instanceof Activity) {
            ActionBar ab = this.getSupportActionBar();
            if (ab instanceof WindowDecorActionBar) {
                throw new IllegalStateException("This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.");
            } else {
                this.mMenuInflater = null;
                if (ab != null) {
                    ab.onDestroy();
                }

                if (toolbar != null) {
                    ToolbarActionBar tbab = new ToolbarActionBar(toolbar, ((Activity)this.mOriginalWindowCallback).getTitle(), this.mAppCompatWindowCallback);
                    this.mActionBar = tbab;
                    this.mWindow.setCallback(tbab.getWrappedWindowCallback());
                } else {
                    this.mActionBar = null;
                    this.mWindow.setCallback(this.mAppCompatWindowCallback);
                }

                this.invalidateOptionsMenu();
            }
        }
    }

這裏對Toolbar進行了一系列處理。

主要是創建了ToolbarActionBar 並且賦值給mActionBar。

最後調用了invalidateOptionsMenu()

public void invalidateOptionsMenu() {
        ActionBar ab = this.getSupportActionBar();
        if (ab == null || !ab.invalidateOptionsMenu()) {
            this.invalidatePanelMenu(0);
        }
    }

查看getSupportActionBar();最後返回了我們前面創建的ToolBarActionBar

public ActionBar getSupportActionBar() {
        this.initWindowDecorActionBar();
        return this.mActionBar;
    }

所以又執行到了ToolbarActionBar的invalidateOptionsMenu()

 public boolean invalidateOptionsMenu() {
        this.mDecorToolbar.getViewGroup().removeCallbacks(this.mMenuInvalidator);
        ViewCompat.postOnAnimation(this.mDecorToolbar.getViewGroup(), this.mMenuInvalidator);
        return true;
    }
this.mDecorToolbar.getViewGroup().removeCallbacks(this.mMenuInvalidator);執行了一個removeCallbacks()方法。這裏傳入了一個mMenuInvalidator一個Runable對象。
private final Runnable mMenuInvalidator = new Runnable() {
        public void run() {
            ToolbarActionBar.this.populateOptionsMenu();
        }
    };
void populateOptionsMenu() {
        Menu menu = this.getMenu();//創建獲取了一個Menu
        MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder)menu : null;
        if (mb != null) {
            mb.stopDispatchingItemsChanged();
        }

        try {
            menu.clear();
            if (!this.mWindowCallback.onCreatePanelMenu(0, menu) || !this.mWindowCallback.onPreparePanel(0, (View)null, menu)) {//執行這裏
                menu.clear();
            }
        } finally {
            if (mb != null) {
                mb.startDispatchingItemsChanged();
            }

        }

    }

我們大致能看出,最後執行了

this.mWindowCallback.onCreatePanelMenu(0, menu) 

this.mWindowCallback.onPreparePanel(0, (View)null, menu))

我們仔細追蹤一下就會找到這個mWindowCallback在AppCompatDelegateImpl的構造方法中

this.mOriginalWindowCallback = this.mWindow.getCallback();

被賦值。然後再創建ToolbarActionBar的時候賦值給ToolbarActionBar的mWindowCallback。

在ToolbarActionBar的populateOptionsMenu()中執行的也就是對應的mWindow.getCallback的onCreatePanelMenu()方法和onPreparePanel()方法,這裏瞭解源碼也就知道了,這個mWindow就對應了我們操作的一個Activity的Window,所以就執行到了Activity中。(Activity實現了Window.Callback接口,這個接口包含了我們看到的如下兩個方法

 @Override
    public boolean onCreatePanelMenu(int featureId, Menu menu) {
        return super.onCreatePanelMenu(featureId, menu);
    }
    

    @Override
    public boolean onPreparePanel(int featureId, View view, Menu menu) {
        return super.onPreparePanel(featureId, view, menu);
    }

查看Activity的源碼找到這個方法,我們找到我們經常重寫的方法onCreateOptionsMenu(menu);//重寫的方法。

 public boolean onCreatePanelMenu(int featureId, Menu menu) {
        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
            boolean show = onCreateOptionsMenu(menu);//重寫的方法
            show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater());
            return show;
        }
        return false;
    }

這兩個方法中傳遞了Menu參數,這個參數在前面的代碼中已經寫過了。populateOptionsMenu()方法中有一句

Menu menu = this.getMenu();//創建獲取了一個Menu

我們看下如果得到這個Menu對象。

 private Menu getMenu() {
        if (!this.mMenuCallbackSet) {
            this.mDecorToolbar.setMenuCallbacks(new ToolbarActionBar.ActionMenuPresenterCallback(), new ToolbarActionBar.MenuBuilderCallback());
            this.mMenuCallbackSet = true;
        }

        return this.mDecorToolbar.getMenu();
    }

調用了mDeorToolbar.getMenu()方法。追蹤mDeorToolbar找到ToolbarWidgetWrapper這個類。調用了getMenu().

public Menu getMenu() {
        return this.mToolbar.getMenu();
    }

最後又回到了Toolbar中。

在ToolBar中查看

public Menu getMenu() {
        this.ensureMenu();
        return this.mMenuView.getMenu();
    }

創建了mMenuView。

 private void ensureMenuView() {
        if (this.mMenuView == null) {
            this.mMenuView = new ActionMenuView(this.getContext());
            this.mMenuView.setPopupTheme(this.mPopupTheme);
            this.mMenuView.setOnMenuItemClickListener(this.mMenuViewItemClickListener);
            this.mMenuView.setMenuCallbacks(this.mActionMenuPresenterCallback, this.mMenuBuilderCallback);
            Toolbar.LayoutParams lp = this.generateDefaultLayoutParams();
            lp.gravity = 8388613 | this.mButtonGravity & 112;
            this.mMenuView.setLayoutParams(lp);
            this.addSystemView(this.mMenuView, false);
        }

    }

調用mMenuView.getMeun(),從而創建Menu

  public Menu getMenu() {
        if (this.mMenu == null) {
            Context context = this.getContext();
            this.mMenu = new MenuBuilder(context);
            this.mMenu.setCallback(new ActionMenuView.MenuBuilderCallback());
            this.mPresenter = new ActionMenuPresenter(context);
            this.mPresenter.setReserveOverflow(true);
            this.mPresenter.setCallback((Callback)(this.mActionMenuPresenterCallback != null ? this.mActionMenuPresenterCallback : new ActionMenuView.ActionMenuPresenterCallback()));
            this.mMenu.addMenuPresenter(this.mPresenter, this.mPopupContext);
            this.mPresenter.setMenuView(this);
        }

        return this.mMenu;
    }

我們在Activity中一般會重寫這個方法

 @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (mConversationType != null && mConversationType == Conversation.ConversationType.GROUP) {
            MenuInflater inflater = getMenuInflater();
            //增加統一的Menu
            inflater.inflate(R.menu.menu_im_group, menu);//解析Menu佈局
            return true;
        }
        return super.onCreateOptionsMenu(menu);
    }

解析Menu佈局,獲取佈局的View。

查看inflate()方法可以知道。inflate()中調用了parseMenu(parser, attrs, menu);

繼續跟蹤發現通過Menu對象創建了MenuState menuState = new MenuState(menu);

循環解析xml文件獲取item,然後調用meunStatu.addItem();

menuState.readItem(attrs);
 public MenuItem addItem() {
            itemAdded = true;
            MenuItem item = menu.add(groupId, itemId, itemCategoryOrder, itemTitle);
            setItem(item);
            return item;
        }

最後把解析的item添加到menu中。

回到創建Menu的MenuBuilder中

 public MenuItem add(CharSequence title) {
        return this.addInternal(0, 0, 0, title);
    }
 protected MenuItem addInternal(int group, int id, int categoryOrder, CharSequence title) {
        int ordering = getOrdering(categoryOrder);
        MenuItemImpl item = this.createNewMenuItem(group, id, categoryOrder, ordering, title, this.mDefaultShowAsAction);
        if (this.mCurrentMenuInfo != null) {
            item.setMenuInfo(this.mCurrentMenuInfo);
        }

        this.mItems.add(findInsertIndex(this.mItems, ordering), item);//加入集合
        this.onItemsChanged(true);
        return item;
    }
ArrayList<MenuItemImpl> mItems;
this.mItems.add(findInsertIndex(this.mItems, ordering), item);//加入集合
最後將創建的item添加到ArrayList集合中,完成整個Menu的創建。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章