批注 2020-02-10 212314.png
先上完整代碼:
public class TagLayout extends ViewGroup {
List<Rect> childrenBounds = new ArrayList<>();
public TagLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
Rect childBound = childrenBounds.get(i);
child.layout(childBound.left, childBound.top, childBound.right, childBound.bottom);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int lineMaxWidth = 0;
int heightUse = 0;
int widthUse = 0;
int lineMaxHeight = 0;
int specMode=MeasureSpec.getMode(widthMeasureSpec);
int specWidth = MeasureSpec.getSize(widthMeasureSpec);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUse);
if (specMode!=MeasureSpec.UNSPECIFIED&&widthUse + child.getMeasuredWidth() > specWidth) {
// 另起一行
widthUse = 0;
heightUse += lineMaxHeight;
lineMaxHeight=0;
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUse);
}
Rect childBound;
if (childrenBounds.size() <= i) {
childBound = new Rect();
childrenBounds.add(childBound);
} else {
childBound = childrenBounds.get(i);
}
childBound.set(
widthUse,
heightUse,
widthUse + getMeasuredWidth(),
heightUse + getMeasuredHeight());
widthUse += child.getMeasuredWidth();
lineMaxWidth = Math.max(lineMaxWidth, widthUse);
lineMaxHeight = Math.max(lineMaxHeight, child.getMeasuredHeight());
}
int width = lineMaxWidth;
int height = lineMaxHeight+heightUse;
setMeasuredDimension(width, height);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
}
## 準備工作
定義一個容器來存儲子View的位置信息:
```java
List<Rect> childrenBounds = new ArrayList<>();
先從onMeasure()階段開始
定義了四個量:
int widthUse = 0;//使用了多少寬度
int heightUse = 0;//使用了多少高度
int lineMaxWidth = 0; //當前最大寬度
int lineMaxHeight = 0;//當前最大高度
這個布局是個橫向排列的過程覆醇,我們用一個widthUse
來記錄當前行橫向使用的寬度捆姜,則計算下個子View位置時候,只需在此基礎上累加即可识啦。
而lineMaxHeight
用來記錄當前行的子View中的最大高度负蚊,當換行時候傳遞給heightUse
,使下一階段的子View添加下沉高度颓哮。
這段代碼是什么意義:
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUse);
這其實是安卓幫我們做了一個自動計算的過程:
LayoutParams layoutParams = child.getLayoutParams();
int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);
int childWidthMode;
int childWidthSize;
switch (layoutParams.width) {
case LayoutParams.MATCH_PARENT:
switch (specWidthMode) {
case MeasureSpec.EXACTLY:
case MeasureSpec.AT_MOST:
childWidthMode = MeasureSpec.EXACTLY;
childWidthSize = specWidthSize - useWidth;
break;
case MeasureSpec.UNSPECIFIED:
childWidthMode = MeasureSpec.UNSPECIFIED;
childWidthSize = 0;
break;
}
break;
case LayoutParams.WRAP_CONTENT:
break;
}
可以類比為如上的代碼家妆,根據(jù)開發(fā)者傳入的參數(shù)類型進行數(shù)值的自動適配
onLayout()階段
循環(huán)設置布局參數(shù)
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
Rect childBound = childrenBounds.get(i);
child.layout(childBound.left, childBound.top, childBound.right, childBound.bottom);
}