欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

setSupportActionBar添加Toolbar后系统之后如何创建Menu

程序员文章站 2022-05-24 16:09:50
...

在正常的开发中我们会使用 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的创建。