<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.zsj.letterfilterlistview.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="111" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="222" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="222" />
</LinearLayout>
上面布局是如果测量的.
从performTraversals开始
第一个调用的方法:performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
--> mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-->onMeasure(widthMeasureSpec, heightMeasureSpec); //测量开始
-->LinearLayout.onMeasure(widthMeasureSpec, heightMeasureSpec);
-->measureVertical(widthMeasureSpec, heightMeasureSpec);
-->measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
-->measureChildWithMargins(child, widthMeasureSpec, totalWidth,
heightMeasureSpec, totalHeight);
childWidthMeasureSpec,childHeightMeasureSpec测量模式,把这两个测量模式传给自View,这里就是TextView
childWidthMeasureSpec是怎么来的.
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
//父布局的模式的大小
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
}
public class TextView extends View {
public TextView(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//widthMeasureSpec == childWidthMeasureSpec
//heightMeasureSpec == childHeightMeasureSpec
//wrap_content 对应 AT_MOST
//match_parent ,100dp 对应 EXACTLY
//模式和大小是由父布局和自己决定的
//比如 父布局是包裹内容,就算子布局是match_parent,这时候计算测量模式还是AT_MOST.
具体规则看.ViewGroup的getChildMeasureSpec方法.
}
}
--> child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
这个时候我们都会调用setMeasuredDimension().这个时候我的布局才真正指定宽度和高度.这时候mMeasuredWidth和mMeasuredHeight才开始有值.
接着执行ViewGroup的onMeasure() 方法,这时候需要指定自己的宽高.
childHeight = child.getMeasuredHeight() + share;LinearLayout的高度算法如果是垂直方向是不断叠加子View的高度.
总结:
performMeasure():用于指定和测量layout中所有控件的宽高.
对于ViewGroup,先去测量里面子孩子,根据子孩子的宽高再来计算和指定自己的宽高.
对于View,它的宽高是由自己和父布局决定的.
网友评论