更多关于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)) {
//title不为null            if(this.mTitleTextView == null) {
//如果主标题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) {
//设置字体颜色                    this.mTitleTextView.setTextColor(this.mTitleTextColor);                }            }            if(this.mTitleTextView.getParent() == null) {
//若父窗体为null,则添加主标题TextView,同导航图标                this.addSystemView(this.mTitleTextView);
//同样更新为可见状态                this.updateChildVisibilityForExpandedActionView(this.mTitleTextView);            }        } else if(this.mTitleTextView != null && this.mTitleTextView.getParent() != null) {
//title为null则移除主标题的TextView            this.removeView(this.mTitleTextView);        }        if(this.mTitleTextView != null) {
//存在主标题TextView则设置文字            this.mTitleTextView.setText(title);        }
//设置当前文件        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)) {
//getMenu方法下面介绍,先记住getMenu返回的是SupportMenu的实现类            super.inflate(menuRes, menu);        } else {            XmlResourceParser parser = null;            try {                parser = this.mContext.getResources().getLayout(menuRes);                AttributeSet e = Xml.asAttributeSet(parser);
//分析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) {
//沉浸式菜单为空            MenuBuilder menu = (MenuBuilder)this.mMenuView.getMenu();            if(this.mExpandedMenuPresenter == null) {
//创建沉浸式菜单的每一个菜单项                this.mExpandedMenuPresenter = new Toolbar.ExpandedActionViewMenuPresenter(null);            }//显示菜单            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;
//找到        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);//开始遍历        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")) {
//添加菜单项                        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中。

欢迎大家讨论,纯属抛砖引玉。

更多相关文章

  1. Android沉浸式标题栏状态栏字体颜色修改(小米和魅族以及Android6.
  2. android p状态栏,插入sim卡,关闭数据流量,状态栏卡图标右下角有X图标
  3. android 全屏 无标题
  4. Activity去掉标题栏失败(使用AppCompat)
  5. android应用中去掉标题栏的方法
  6. 通过设置android:imeOptions来改变软键盘Enter键图标
  7. Android(安卓)标题栏上加一个返回按钮 超简单的返回
  8. Android中隐藏标题栏和状态栏(电源,信号,时间等信息)
  9. Android(安卓)Toast带图标水平布局的简单实现

随机推荐

  1. 从孩子那里访问父类的财产
  2. [Php+APACHE+MySQL][或在windows已装IIS
  3. 有一种防弹的方法可以检测php字符串中的b
  4. 第一次开发PHP网页Hello PHP
  5. 如何将结果输入变量MYSQL
  6. 随手小记:PHP-FPM模式下PHP最大执行时间、
  7. Roundcube-SQL-Global-Address-Books自定
  8. 如何在不更改其键值的情况下删除数组的第
  9. 基于PHP的UUID/GUID/uniqid替换方法及其
  10. 来自加载页面的Javascript响应未显示在中