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