话不多说,上图:
onMeasure()伪代码如下:
//测量
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
for (i in 0 until childCount) {
val childView = getChildAt(i)
//1.0 获取子view的layoutParams
val layoutParams = childView.layoutParams
//1.1 layoutParams->MeasureSpec
val childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, layoutParams.width)
val childHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, layoutParams.height)
//2 获取子view的宽高
childView.measure(childWidthMeasureSpec,childHeightMeasureSpec)
val measuredWidth = childView.measuredWidth
val measuredHeight = childView.measuredHeight
/**
* 中间经过计算view group需要的宽高
* 父布局给的宽度selfWidth ,测量中子view要求view group的宽度parentNeededWidth
* 父布局给的高度selfHeight 测量中子view要求view group的高度parentNeededHeight
*/
//3 计算(度量)view group的宽高
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val realWidth = widthMode == MeasureSpec.EXACTLY ? selfWidth : parentNeededWidth
val realHeight = heightMode == MeasureSpec.EXACTLY ? selfHeight : parentNeededHeight
// 这个传递的是具体的size,不是MeasureSpec
setMeasuredDimension(realWidth, realHeight);
}
}
onLayout()伪代码如下:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// 总共的行数
int lineCount = allLines.size();
int curL = getPaddingLeft();
int curT = getPaddingTop();
for (int i = 0; i < lineCount; i++) {//
List<View> lineViews = allLines.get(i);
int lineHeight = lineHeights.get(i);
for (int j = 0; j < lineViews.size(); j++) {// 每行的view进行布局
View view = lineViews.get(j);
// 根据子view的测量宽高,计算left、top、right、bottom
int left = curL;
int top = curT;
int right = left + view.getMeasuredWidth();
int bottom = top + view.getMeasuredHeight();
view.layout(left, top, right, bottom);
// 是你调用完这个View的layout之后才会有的值,而getMeasuredWidth 是你调用完measure之后就可以获取
curL = right + mHorizontalSpacing;
}
curL = getPaddingLeft();
curT = curT + lineHeight + mVerticalSpacing;
}
}
LayoutParams是什么?与MeasureSpec有关系吗?
LayoutParams:直接从表面看就知道,是布局的参数。ViewGroup里面的一个静态类
public static class LayoutParams {
/**
* Special value for the height or width requested by a View.
* FILL_PARENT means that the view wants to be as big as its parent,
* minus the parent's padding, if any. This value is deprecated
* starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
*/
@SuppressWarnings({"UnusedDeclaration"})
@Deprecated
public static final int FILL_PARENT = -1;
/**
* Special value for the height or width requested by a View.
* MATCH_PARENT means that the view wants to be as big as its parent,
* minus the parent's padding, if any. Introduced in API Level 8.
*/
public static final int MATCH_PARENT = -1;
/**
* Special value for the height or width requested by a View.
* WRAP_CONTENT means that the view wants to be just large enough to fit
* its own internal content, taking its own padding into account.
*/
public static final int WRAP_CONTENT = -2;
public int width;
.
.
.
.
}
那么和MeasureSpec有什么关系呢?
子view通过getChildMeasureSpec()方法传入layoutparams获取对应的MeasureSpec,由于子view的测量。
MeasureSpec是什么呢?
MeasureSpec是View中的内部类,用于保存宽高的信息(int 累心,32位,高2位标识Mode:UNSPECIFIED、EXACTLY、AT_MOST,低30位标识size)
UNSPECIFIED:不对View大小做限制
EXACTLY:确切的大小:如 100dp
AT_MOST:大小不可超过某个数值,如:match_parent,做大不能超过parent