[Android]Toolbar使用詳解(三)——源碼解析

更多關於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

保證導航圖片不爲null,爲null則新建並添加。

    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

this.mNavButtonView是ImageView,這裏就是簡單對其設置一個點擊按鈕的監聽事件。

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

原理跟setTitle一樣不再贅述。

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。
其實整個Toolbar就是一個LinearLayout佈局,只是在上面自定義了佈局。

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中。

歡迎大家討論,純屬拋磚引玉。

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