什么叫"专业性",如果以我现在的经验来解释是:一个人在处理某个问题时,不依靠他人,自己独自解决问题的能力。在当前这个社会中,专业性是社会进步的基石,看看身边的事物,都是由专业的人做的,各行各业各司其职,推动着人类文明的进步;对于个人来说,专业性是指个人在某个行业中的精湛程度,精湛程度越高,拥有的社会地位越高。
前几天在公司做项目时,遇到一个问题:
当ToolBar的gravity设置为bottom时,子类的下面显示不全,如图,子类是一个蓝色背景的TextView
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显示上移动,经检验,也确实是这样