什么叫"專業(yè)性"痒钝,如果以我現(xiàn)在的經(jīng)驗(yàn)來解釋是:一個(gè)人在處理某個(gè)問題時(shí),不依靠他人鸯隅,自己獨(dú)自解決問題的能力澜建。在當(dāng)前這個(gè)社會(huì)中,專業(yè)性是社會(huì)進(jìn)步的基石滋迈,看看身邊的事物霎奢,都是由專業(yè)的人做的,各行各業(yè)各司其職饼灿,推動(dòng)著人類文明的進(jìn)步幕侠;對(duì)于個(gè)人來說,專業(yè)性是指?jìng)€(gè)人在某個(gè)行業(yè)中的精湛程度碍彭,精湛程度越高晤硕,擁有的社會(huì)地位越高。
前幾天在公司做項(xiàng)目時(shí)庇忌,遇到一個(gè)問題:
當(dāng)ToolBar的gravity設(shè)置為bottom時(shí)舞箍,子類的下面顯示不全,如圖皆疹,子類是一個(gè)藍(lán)色背景的TextView
1. 問題分析
1.1 ToolBar是V7包里面的疏橄,路徑為android.support.v7.widget.Toolbar
1.2 ToolBar繼承的是ViewGroup
1.3 gravity是ToolBar的一個(gè)自定義的屬性
1.4 ToolBar子類的布局?jǐn)[放是在ToolBar.onLayout中操作子類的layout方法來確定位置的
1.5 當(dāng)子類設(shè)置layout_marginBottom屬性時(shí),可完整顯示該子類
2. ToolBar源碼分析
當(dāng)設(shè)置gravity為bottom時(shí),在ToolBar的構(gòu)造函數(shù)中獲得該屬性
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行由于沒有設(shè)置這些子View晃酒,跳過;
直至198行窄绒,都只是做一些基本操作贝次,mTempViews放進(jìn)了ToolBar子類的集合
在200行遍歷,點(diǎn)進(jìn)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設(shè)置的布局彰导,我只看child.layout()方法的第二個(gè)和第四個(gè)參數(shù)蛔翅,top的值是getChildTop方法得到的,點(diǎn)進(jìn)去
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位谋,調(diào)用了getChildVerticalGravity方法山析,點(diǎn)進(jìn)去
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方法會(huì)走 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 移動(dòng)了 -alignmentOffset 的高度
bottom = top + child.getMeasuredHeight(),跟著移動(dòng)了 -alignmentOffset 的高度
3. 總結(jié)
原來當(dāng)ToolBar設(shè)置
android:gravity="bottom"
時(shí)组哩,子View上下移動(dòng)了alignmentOffset的距離等龙,當(dāng)子View的高度比alignmentHeight小時(shí),alignmentOffset為負(fù)伶贰,子View顯示下移蛛砰,而當(dāng)子View的高度比alignmentHeight大時(shí),alignmentOffset為正黍衙,子View顯示上移動(dòng)泥畅,經(jīng)檢驗(yàn),也確實(shí)是這樣