在正常的開發中我們會使用 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的創建。