您的位置:首页 > 移动开发 > Android开发

[Android]Toolbar使用详解(三)——源码解析

2015-07-28 18:00 981 查看
更多关于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中。

欢迎大家讨论,纯属抛砖引玉。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: