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显示上移动,经检验,也确实是这样

你可能感兴趣的:(ToolBar的gravity属性设置为bottom时的源码分析)