ToolBar的gravity属性设置为bottom时的源码分析

什么叫"专业性",如果以我现在的经验来解释是:一个人在处理某个问题时,不依靠他人,自己独自解决问题的能力。在当前这个社会中,专业性是社会进步的基石,看看身边的事物,都是由专业的人做的,各行各业各司其职,推动着人类文明的进步;对于个人来说,专业性是指个人在某个行业中的精湛程度,精湛程度越高,拥有的社会地位越高。

前几天在公司做项目时,遇到一个问题:

当ToolBar的gravity设置为bottom时,子类的下面显示不全,如图,子类是一个蓝色背景的TextView

显示不全.jpg

1. 问题分析

1.1 ToolBar是V7包里面的,路径为android.support.v7.widget.Toolbar

1.2 ToolBar继承的是ViewGroup

1.3 gravity是ToolBar的一个自定义的属性

1.4 ToolBar子类的布局摆放是在ToolBar.onLayout中操作子类的layout方法来确定位置的

1.5 当子类设置layout_marginBottom属性时,可完整显示该子类

2. ToolBar源码分析

当设置gravity为bottom时,在ToolBar的构造函数中获得该属性

android:gravity="bottom"

this.mGravity = a.getInteger(styleable.Toolbar_android_gravity, this.mGravity);//mGravity = 80

直接看onLayout方法:

    boolean isRtl = ViewCompat.getLayoutDirection(this) == 1;
    int width = this.getWidth();
    int height = this.getHeight();
    int paddingLeft = this.getPaddingLeft();
    int paddingRight = this.getPaddingRight();
    int paddingTop = this.getPaddingTop();
    int paddingBottom = this.getPaddingBottom();
    int left = paddingLeft;
    int right = width - paddingRight;
    int[] collapsingMargins = this.mTempMargins;
    collapsingMargins[0] = collapsingMargins[1] = 0;
    int minHeight = ViewCompat.getMinimumHeight(this);
    int alignmentHeight = minHeight >= 0 ? Math.min(minHeight, b - t) : 0;
    if (this.shouldLayout(this.mNavButtonView)) {
        if (isRtl) {
            right = this.layoutChildRight(this.mNavButtonView, right, collapsingMargins, alignmentHeight);
        } else {
            left = this.layoutChildLeft(this.mNavButtonView, paddingLeft, collapsingMargins, alignmentHeight);
        }
    }

    if (this.shouldLayout(this.mCollapseButtonView)) {
        if (isRtl) {
            right = this.layoutChildRight(this.mCollapseButtonView, right, collapsingMargins, alignmentHeight);
        } else {
            left = this.layoutChildLeft(this.mCollapseButtonView, left, collapsingMargins, alignmentHeight);
        }
    }

    if (this.shouldLayout(this.mMenuView)) {
        if (isRtl) {
            left = this.layoutChildLeft(this.mMenuView, left, collapsingMargins, alignmentHeight);
        } else {
            right = this.layoutChildRight(this.mMenuView, right, collapsingMargins, alignmentHeight);
        }
    }

    int contentInsetLeft = this.getCurrentContentInsetLeft();
    int contentInsetRight = this.getCurrentContentInsetRight();
    collapsingMargins[0] = Math.max(0, contentInsetLeft - left);
    collapsingMargins[1] = Math.max(0, contentInsetRight - (width - paddingRight - right));
    left = Math.max(left, contentInsetLeft);
    right = Math.min(right, width - paddingRight - contentInsetRight);
    if (this.shouldLayout(this.mExpandedActionView)) {
        if (isRtl) {
            right = this.layoutChildRight(this.mExpandedActionView, right, collapsingMargins, alignmentHeight);
        } else {
            left = this.layoutChildLeft(this.mExpandedActionView, left, collapsingMargins, alignmentHeight);
        }
    }

    if (this.shouldLayout(this.mLogoView)) {
        if (isRtl) {
            right = this.layoutChildRight(this.mLogoView, right, collapsingMargins, alignmentHeight);
        } else {
            left = this.layoutChildLeft(this.mLogoView, left, collapsingMargins, alignmentHeight);
        }
    }

    boolean layoutTitle = this.shouldLayout(this.mTitleTextView);
    boolean layoutSubtitle = this.shouldLayout(this.mSubtitleTextView);
    int titleHeight = 0;
    Toolbar.LayoutParams lp;
    if (layoutTitle) {
        lp = (Toolbar.LayoutParams)this.mTitleTextView.getLayoutParams();
        titleHeight += lp.topMargin + this.mTitleTextView.getMeasuredHeight() + lp.bottomMargin;
    }

    if (layoutSubtitle) {
        lp = (Toolbar.LayoutParams)this.mSubtitleTextView.getLayoutParams();
        titleHeight += lp.topMargin + this.mSubtitleTextView.getMeasuredHeight() + lp.bottomMargin;
    }

    int centerRight;
    int centerViewsCount;
    int i;
    int titleTop;
    if (layoutTitle || layoutSubtitle) {
        View topChild = layoutTitle ? this.mTitleTextView : this.mSubtitleTextView;
        View bottomChild = layoutSubtitle ? this.mSubtitleTextView : this.mTitleTextView;
        Toolbar.LayoutParams toplp = (Toolbar.LayoutParams)topChild.getLayoutParams();
        Toolbar.LayoutParams bottomlp = (Toolbar.LayoutParams)bottomChild.getLayoutParams();
        boolean titleHasWidth = layoutTitle && this.mTitleTextView.getMeasuredWidth() > 0 || layoutSubtitle && this.mSubtitleTextView.getMeasuredWidth() > 0;
        switch(this.mGravity & 112) {
        case 16:
        default:
            centerRight = height - paddingTop - paddingBottom;
            centerViewsCount = (centerRight - titleHeight) / 2;
            if (centerViewsCount < toplp.topMargin + this.mTitleMarginTop) {
                centerViewsCount = toplp.topMargin + this.mTitleMarginTop;
            } else {
                i = height - paddingBottom - titleHeight - centerViewsCount - paddingTop;
                if (i < toplp.bottomMargin + this.mTitleMarginBottom) {
                    centerViewsCount = Math.max(0, centerViewsCount - (bottomlp.bottomMargin + this.mTitleMarginBottom - i));
                }
            }

            titleTop = paddingTop + centerViewsCount;
            break;
        case 48:
            titleTop = this.getPaddingTop() + toplp.topMargin + this.mTitleMarginTop;
            break;
        case 80:
            titleTop = height - paddingBottom - bottomlp.bottomMargin - this.mTitleMarginBottom - titleHeight;
        }

        int var10000;
        Toolbar.LayoutParams lp;
        int subtitleLeft;
        int subtitleBottom;
        if (isRtl) {
            centerRight = (titleHasWidth ? this.mTitleMarginStart : 0) - collapsingMargins[1];
            right -= Math.max(0, centerRight);
            collapsingMargins[1] = Math.max(0, -centerRight);
            centerViewsCount = right;
            i = right;
            if (layoutTitle) {
                lp = (Toolbar.LayoutParams)this.mTitleTextView.getLayoutParams();
                subtitleLeft = right - this.mTitleTextView.getMeasuredWidth();
                subtitleBottom = titleTop + this.mTitleTextView.getMeasuredHeight();
                this.mTitleTextView.layout(subtitleLeft, titleTop, right, subtitleBottom);
                centerViewsCount = subtitleLeft - this.mTitleMarginEnd;
                titleTop = subtitleBottom + lp.bottomMargin;
            }

            if (layoutSubtitle) {
                lp = (Toolbar.LayoutParams)this.mSubtitleTextView.getLayoutParams();
                titleTop += lp.topMargin;
                subtitleLeft = right - this.mSubtitleTextView.getMeasuredWidth();
                subtitleBottom = titleTop + this.mSubtitleTextView.getMeasuredHeight();
                this.mSubtitleTextView.layout(subtitleLeft, titleTop, right, subtitleBottom);
                i = right - this.mTitleMarginEnd;
                var10000 = subtitleBottom + lp.bottomMargin;
            }

            if (titleHasWidth) {
                right = Math.min(centerViewsCount, i);
            }
        } else {
            centerRight = (titleHasWidth ? this.mTitleMarginStart : 0) - collapsingMargins[0];
            left += Math.max(0, centerRight);
            collapsingMargins[0] = Math.max(0, -centerRight);
            centerViewsCount = left;
            i = left;
            if (layoutTitle) {
                lp = (Toolbar.LayoutParams)this.mTitleTextView.getLayoutParams();
                subtitleLeft = left + this.mTitleTextView.getMeasuredWidth();
                subtitleBottom = titleTop + this.mTitleTextView.getMeasuredHeight();
                this.mTitleTextView.layout(left, titleTop, subtitleLeft, subtitleBottom);
                centerViewsCount = subtitleLeft + this.mTitleMarginEnd;
                titleTop = subtitleBottom + lp.bottomMargin;
            }

            if (layoutSubtitle) {
                lp = (Toolbar.LayoutParams)this.mSubtitleTextView.getLayoutParams();
                titleTop += lp.topMargin;
                subtitleLeft = left + this.mSubtitleTextView.getMeasuredWidth();
                subtitleBottom = titleTop + this.mSubtitleTextView.getMeasuredHeight();
                this.mSubtitleTextView.layout(left, titleTop, subtitleLeft, subtitleBottom);
                i = subtitleLeft + this.mTitleMarginEnd;
                var10000 = subtitleBottom + lp.bottomMargin;
            }

            if (titleHasWidth) {
                left = Math.max(centerViewsCount, i);
            }
        }
    }

    this.addCustomViewsWithGravity(this.mTempViews, 3);
    titleTop = this.mTempViews.size();

    int rightViewsCount;
    for(rightViewsCount = 0; rightViewsCount < titleTop; ++rightViewsCount) {
        left = this.layoutChildLeft((View)this.mTempViews.get(rightViewsCount), left, collapsingMargins, alignmentHeight);
    }

    this.addCustomViewsWithGravity(this.mTempViews, 5);
    rightViewsCount = this.mTempViews.size();

    int centerViewsWidth;
    for(centerViewsWidth = 0; centerViewsWidth < rightViewsCount; ++centerViewsWidth) {
        right = this.layoutChildRight((View)this.mTempViews.get(centerViewsWidth), right, collapsingMargins, alignmentHeight);
    }

    this.addCustomViewsWithGravity(this.mTempViews, 1);
    centerViewsWidth = this.getViewListMeasuredWidth(this.mTempViews, collapsingMargins);
    int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2;
    int halfCenterViewsWidth = centerViewsWidth / 2;
    int centerLeft = parentCenter - halfCenterViewsWidth;
    centerRight = centerLeft + centerViewsWidth;
    if (centerLeft < left) {
        centerLeft = left;
    } else if (centerRight > right) {
        centerLeft -= centerRight - right;
    }

    centerViewsCount = this.mTempViews.size();

    for(i = 0; i < centerViewsCount; ++i) {
        centerLeft = this.layoutChildLeft((View)this.mTempViews.get(i), centerLeft, collapsingMargins, alignmentHeight);
    }

    this.mTempViews.clear();

1~13行是一些基本操作;

14~58行由于没有设置这些子View,跳过;

直至198行,都只是做一些基本操作,mTempViews放进了ToolBar子类的集合

在200行遍历,点进layoutChildLeft()方法

    Toolbar.LayoutParams lp = (Toolbar.LayoutParams)child.getLayoutParams();
    int l = lp.leftMargin - collapsingMargins[0];
    left += Math.max(0, l);
    collapsingMargins[0] = Math.max(0, -l);
    int top = this.getChildTop(child, alignmentHeight);
    int childWidth = child.getMeasuredWidth();
    child.layout(left, top, left + childWidth, top + child.getMeasuredHeight());
    left += childWidth + lp.rightMargin;
    return left;

在第7行看到了子View设置的布局,我只看child.layout()方法的第二个和第四个参数,top的值是getChildTop方法得到的,点进去

    Toolbar.LayoutParams lp = (Toolbar.LayoutParams)child.getLayoutParams();
    int childHeight = child.getMeasuredHeight();
    int alignmentOffset = alignmentHeight > 0 ? (childHeight - alignmentHeight) / 2 : 0;
    switch(this.getChildVerticalGravity(lp.gravity)) {
    case 16:
    default:
        int paddingTop = this.getPaddingTop();
        int paddingBottom = this.getPaddingBottom();
        int height = this.getHeight();
        int space = height - paddingTop - paddingBottom;
        int spaceAbove = (space - childHeight) / 2;
        if (spaceAbove < lp.topMargin) {
            spaceAbove = lp.topMargin;
        } else {
            int spaceBelow = height - paddingBottom - childHeight - spaceAbove - paddingTop;
            if (spaceBelow < lp.bottomMargin) {
                spaceAbove = Math.max(0, spaceAbove - (lp.bottomMargin - spaceBelow));
            }
        }

        return paddingTop + spaceAbove;
    case 48:
        return this.getPaddingTop() - alignmentOffset;
    case 80:
        return this.getHeight() - this.getPaddingBottom() - childHeight - lp.bottomMargin - alignmentOffset;
    }

第4行的lp.gravity = 0,调用了getChildVerticalGravity方法,点进去

private int getChildVerticalGravity(int gravity) {
    int vgrav = gravity & 112;
    switch(vgrav) {
    case 16:
    case 48:
    case 80:
        return vgrav;
    default:
        return this.mGravity & 112;
    }
}

走的是default,mGravity = 80,返回的是80,在getChildTop方法会走 case 80

int alignmentOffset = alignmentHeight > 0 ? (childHeight - alignmentHeight) / 2 : 0;
return this.getHeight() - this.getPaddingBottom() - childHeight - lp.bottomMargin - alignmentOffset;

分析这两行代码,padding暂不考虑,翻译下:child.top = ToolBar的高度 - 0 - childHeight - 0 - alignmentOffset ,alignmentHeight,原来就是onLayout中

int alignmentHeight = minHeight >= 0 ? Math.min(minHeight, b - t) : 0;

alignmentHeight的值为minHeight和ToolBar的高度取最小值,alignmentOffset的值为:如果alignmentHeight大于0,则为child的高度减去alignmentHeight在除以2

top 移动了 -alignmentOffset 的高度

bottom = top + child.getMeasuredHeight(),跟着移动了 -alignmentOffset 的高度

3. 总结

原来当ToolBar设置

android:gravity="bottom"

时,子View上下移动了alignmentOffset的距离,当子View的高度比alignmentHeight小时,alignmentOffset为负,子View显示下移,而当子View的高度比alignmentHeight大时,alignmentOffset为正,子View显示上移动,经检验,也确实是这样

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容