來(lái)一波需求
有這樣一種需求冒冬,前面一個(gè)View伴逸,后面要帶著幾個(gè)標(biāo)簽缠沈,如果前面的View不太大,那么標(biāo)簽緊跟標(biāo)簽向前移動(dòng)(后三個(gè)條目)错蝴,如果前面的View就很大洲愤,余下來(lái)足夠的空間放標(biāo)簽(第一個(gè)條目)
有一個(gè)想法
自己定義一個(gè)ViewGroup,類似于水平布局的LinearLayout顷锰,優(yōu)先測(cè)量后面的View柬赐,最后將剩余的空間給第一個(gè)View,在layout的時(shí)候從左向右擺放官紫,最終實(shí)現(xiàn)效果肛宋,給新的ViewGroup起名叫SpareLayout
- 測(cè)量的時(shí)候從最后一個(gè)子View開(kāi)始測(cè)量
- 累加后面所有View的寬度,將剩余空間給第一個(gè)View
- 高度使用最大高度的View
- layout時(shí)從左向右擺放測(cè)量好的View
做一點(diǎn)實(shí)現(xiàn)
按上面的思路重寫onMeasure和onLayout方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
//余下的空間
int spareWidth = 0;
//最大高度
int maxHeight = 0;
//子view從后向前測(cè)量
for (int i = getChildCount() - 1; i > 0; i--) {
View child = getChildAt(i);
//不可見(jiàn)的跳過(guò)
if (child.getVisibility() == GONE) {
continue;
}
//測(cè)量一個(gè)子View束世,并處理padding酝陈,margin
measureChild(child, widthMeasureSpec, heightMeasureSpec);
FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
int marginWidth = lp.leftMargin + lp.rightMargin;
int marginHeight = lp.topMargin + lp.bottomMargin;
spareWidth += child.getMeasuredWidth() + marginWidth;
maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + marginHeight);
}
//最后來(lái)測(cè)量第一個(gè)View,使用的方式是AT_MOST毁涉,寬度是剩余空間
View firstChild = getChildAt(0);
FrameLayout.LayoutParams lp = (LayoutParams) firstChild.getLayoutParams();
int marginWidth = lp.leftMargin + lp.rightMargin;
int marginHeight = lp.topMargin + lp.bottomMargin;
int paddingWidth = getPaddingLeft() + getPaddingRight();
int paddingHeight = getPaddingTop() + getPaddingBottom();
int firstViewWidthSpec =
MeasureSpec.makeMeasureSpec(widthSize - spareWidth - marginWidth - paddingWidth,
MeasureSpec.AT_MOST);
measureChild(firstChild, firstViewWidthSpec, heightMeasureSpec);
maxHeight = Math.max(firstChild.getMeasuredHeight() + marginHeight, maxHeight);
//儲(chǔ)存測(cè)量結(jié)果
setMeasuredDimension(spareWidth + firstChild.getMeasuredWidth() + paddingWidth,
maxHeight + paddingHeight);
}
測(cè)量得到了每一個(gè)View應(yīng)該的大小沉帮,接下來(lái)就是擺放所有的子View,看過(guò)來(lái)onLayout()
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = 0;
//從左向右排放View
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
int leftStart = left + lp.leftMargin + getPaddingLeft();
int topStart;
//處理vertical的gravity
final int verticalGravity = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (verticalGravity) {
case Gravity.TOP:
//從上向下計(jì)算
topStart = lp.topMargin + getPaddingTop();
break;
case Gravity.CENTER_VERTICAL:
//vertical的居中贫堰,是指view居中(除去這個(gè)SpareLayout的padding和子View的margin居中)
topStart = (t + getPaddingTop() + lp.topMargin + //可以放view的空間上邊
b - getPaddingBottom() - lp.bottomMargin //可以放view的空間下邊
- child.getMeasuredHeight()) / 2 //中心線
- t; //計(jì)算出view的上邊
break;
case Gravity.BOTTOM:
//從下向上算的
topStart =
b - lp.bottomMargin - getPaddingBottom() - child.getMeasuredHeight() - t;
break;
default:
//默認(rèn)是在上面
topStart = lp.topMargin + getPaddingTop();
}
child.layout(leftStart, topStart, leftStart + child.getMeasuredWidth(),
topStart + child.getMeasuredHeight());
//累加左邊已經(jīng)使用的空間
left += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
}
這樣實(shí)現(xiàn)的效果:
提一些Tips
如果后面幾個(gè)標(biāo)簽已經(jīng)很大的情況沒(méi)有處理
以前為這個(gè)效果試驗(yàn)了各種方式穆壕,跑包的時(shí)間都比停下來(lái)寫這個(gè)控件時(shí)間長(zhǎng)得多,以后注意不要再做這樣的事
使用linearLayout的weight屬性并沒(méi)有成功