更多關於Toolbar的使用請移步Toolbar使用詳解系列
從Toolbar的使用一步步解析Toolbar源碼
大體架構
API 0.設置導航圖標
mToolbar.setNavigationIcon(R.drawable.ic_actionbar_flow);
源碼如下
public void setNavigationIcon(int resId) {
this.setNavigationIcon(this.mTintManager.getDrawable(resId));
}
setNavigationIcon
public void setNavigationIcon(@Nullable Drawable icon) {
if(icon != null) {
this.ensureNavButtonView();
if(this.mNavButtonView.getParent() == null) {
this.addSystemView(this.mNavButtonView);
this.updateChildVisibilityForExpandedActionView(this.mNavButtonView);
}
} else if(this.mNavButtonView != null && this.mNavButtonView.getParent() != null) {
this.removeView(this.mNavButtonView);
}
if(this.mNavButtonView != null) {
this.mNavButtonView.setImageDrawable(icon);
}
}
先判斷傳入圖片參數是否爲null
- null,移除導航圖片。
- 不爲null,新建一個ImageView,設置其LayoutParams,最後設置ImageView的圖片爲入參。
ensureNavButtonView
private void ensureNavButtonView() {
if(this.mNavButtonView == null) {
this.mNavButtonView = new ImageButton(this.getContext(), (AttributeSet)null, attr.toolbarNavigationButtonStyle);
Toolbar.LayoutParams lp = this.generateDefaultLayoutParams();
lp.gravity = 8388611 | this.mButtonGravity & 112;
this.mNavButtonView.setLayoutParams(lp);
}
}
設置導航圖標,通過LayoutParams.gravity
lp.gravity=8388611設置gravity=start即左邊開始位置
在Toolbar構造函數內對gravity進行了初始化
this.mButtonGravity = 48;
48:gravity = top
&112 = 得到gravity的縱向位置
綜上,即設置導航座標處於左上位置
關於gravity的詳細說明
繼續往下判斷父窗體是否爲null
addSystemView
private void addSystemView(View v) {
android.view.ViewGroup.LayoutParams vlp = v.getLayoutParams();
Toolbar.LayoutParams lp;
if(vlp == null) {
lp = this.generateDefaultLayoutParams();
} else if(!this.checkLayoutParams(vlp)) {
lp = this.generateLayoutParams(vlp);
} else {
lp = (Toolbar.LayoutParams)vlp;
}
lp.mViewType = 1;
this.addView(v, lp);
}
因爲Toolbar繼承自ViewGroup,當導航圖標的父窗體爲null時,將圖標添加到Toolbar上。updateChildVisbilityForExpandedActionView
API 1.setNavifationOnClickListener
public void setNavigationOnClickListener(OnClickListener listener) {
this.ensureNavButtonView();
this.mNavButtonView.setOnClickListener(listener);
}
ensureNavButtonView上面已作出說明setOnClickListener
API 2.setTitle
public void setTitle(CharSequence title) {
if(!TextUtils.isEmpty(title)) {
<span style="white-space:pre"> </span>//title不爲null
if(this.mTitleTextView == null) {
<span style="white-space:pre"> </span>//如果主標題TextView不存在則新建
Context context = this.getContext();
this.mTitleTextView = new TextView(context);
this.mTitleTextView.setSingleLine();
this.mTitleTextView.setEllipsize(TruncateAt.END);
if(this.mTitleTextAppearance != 0) {
this.mTitleTextView.setTextAppearance(context, this.mTitleTextAppearance);
}
if(this.mTitleTextColor != 0) {
<span style="white-space:pre"> </span>//設置字體顏色
this.mTitleTextView.setTextColor(this.mTitleTextColor);
}
}
if(this.mTitleTextView.getParent() == null) {
<span style="white-space:pre"> </span>//若父窗體爲null,則添加主標題TextView,同導航圖標
this.addSystemView(this.mTitleTextView);
<span style="white-space:pre"> </span>//同樣更新爲可見狀態
this.updateChildVisibilityForExpandedActionView(this.mTitleTextView);
}
} else if(this.mTitleTextView != null && this.mTitleTextView.getParent() != null) {
<span style="white-space:pre"> </span>//title爲null則移除主標題的TextView
this.removeView(this.mTitleTextView);
}
if(this.mTitleTextView != null) {
<span style="white-space:pre"> </span>//存在主標題TextView則設置文字
this.mTitleTextView.setText(title);
}
<span style="white-space:pre"> </span>//設置當前文件
this.mTitleText = title;
API 3.setSubTitle、setTitleTextColor、setSubTitleTextColor
API 4.inflateMenu
public void inflateMenu(int resId) {
this.getMenuInflater().inflate(resId, this.getMenu());
}
最終調用的是SupportMenuInflater.inflate方法 public void inflate(int menuRes, Menu menu) {
if(!(menu instanceof SupportMenu)) {
<span style="white-space:pre"> </span>//getMenu方法下面介紹,先記住getMenu返回的是SupportMenu的實現類
super.inflate(menuRes, menu);
} else {
XmlResourceParser parser = null;
try {
parser = this.mContext.getResources().getLayout(menuRes);
AttributeSet e = Xml.asAttributeSet(parser);
<span style="white-space:pre"> </span>//分析menu.xml文件,往menu裏添加menuItem
this.parseMenu(parser, e, menu);
} catch (XmlPullParserException var9) {
throw new InflateException("Error inflating menu XML", var9);
} catch (IOException var10) {
throw new InflateException("Error inflating menu XML", var10);
} finally {
if(parser != null) {
parser.close();
}
}
}
}
getMenu
public Menu getMenu() {
this.ensureMenu();
return this.mMenuView.getMenu();
}
ensureMenu
private void ensureMenu() {
this.ensureMenuView();
if(this.mMenuView.peekMenu() == null) {
<span style="white-space:pre"> </span>//沉浸式菜單爲空
MenuBuilder menu = (MenuBuilder)this.mMenuView.getMenu();
if(this.mExpandedMenuPresenter == null) {
<span style="white-space:pre"> </span>//創建沉浸式菜單的每一個菜單項
this.mExpandedMenuPresenter = new Toolbar.ExpandedActionViewMenuPresenter(null);
}
<span style="white-space:pre"> </span>//顯示菜單
this.mMenuView.setExpandedActionViewsExclusive(true);
menu.addMenuPresenter(this.mExpandedMenuPresenter, this.mPopupContext);
}
}
ensureMenuView
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);
}
}
這裏很重要,功能是新建這個Toolbar菜單欄。實現在ActionMenuView,ActionMenuView的實現不再本文的討論範圍,只要知道ActionMenuView繼承LinearLayoutCompat。parseMenu
private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu) throws XmlPullParserException, IOException {
SupportMenuInflater.MenuState menuState = new SupportMenuInflater.MenuState(menu);
int eventType = parser.getEventType();
boolean lookingForEndOfUnknownTag = false;
String unknownTagName = null;
String tagName;
<span style="white-space:pre"> </span>//找到<menu>
do {
if(eventType == 2) {
tagName = parser.getName();
if(!tagName.equals("menu")) {
throw new RuntimeException("Expecting menu, got " + tagName);
}
eventType = parser.next();
break;
}
eventType = parser.next();
} while(eventType != 1);
<span style="white-space:pre"> </span>//開始遍歷
for(boolean reachedEndOfMenu = false; !reachedEndOfMenu; eventType = parser.next()) {
switch(eventType) {
case 1:
throw new RuntimeException("Unexpected end of document");
case 2:
if(!lookingForEndOfUnknownTag) {
tagName = parser.getName();
if(tagName.equals("group")) {
menuState.readGroup(attrs);
} else if(tagName.equals("item")) {</span>
<span style="white-space:pre"> </span>//添加菜單項
menuState.readItem(attrs);
} else if(tagName.equals("menu")) {
SubMenu subMenu = menuState.addSubMenuItem();
this.parseMenu(parser, attrs, subMenu);
} else {
lookingForEndOfUnknownTag = true;
unknownTagName = tagName;
}
}
break;
case 3:
tagName = parser.getName();
if(lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
lookingForEndOfUnknownTag = false;
unknownTagName = null;
} else if(tagName.equals("group")) {
menuState.resetGroup();
} else if(tagName.equals("item")) {
if(!menuState.hasAddedItem()) {
if(menuState.itemActionProvider != null && menuState.itemActionProvider.hasSubMenu()) {
menuState.addSubMenuItem();
} else {
menuState.addItem();
}
}
} else if(tagName.equals("menu")) {
reachedEndOfMenu = true;
}
}
}
}
分析完畢後就把MenuItem顯示到Menu上。API 5.setOnMenuItemClickListener
private final ActionMenuView.OnMenuItemClickListener mMenuViewItemClickListener =
new ActionMenuView.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (mOnMenuItemClickListener != null) {
return mOnMenuItemClickListener.onMenuItemClick(item);
}
return false;
}
};
最後實現是在ActionMenuView中。歡迎大家討論,純屬拋磚引玉。