1、Measure
MeasureSpec:在Measure流程中,系统将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,
在onMeasure中根据这个MeasureSpec来确定view的测量宽高
测量模式
EXACTLY :父容器已经测量出所需要的精确大小,这也是childview的最终大小
------match_parent,精确值
ATMOST : child view最终的大小不能超过父容器的给的
------wrap_content
UNSPECIFIED: 不确定,源码内部使用
-------一般在ScrollView,ListView
2)、测量大小:根据测量模式来确定测量大小
3)源码里面的位运算
&:取出对应Mask类型的属性值
|:添加对应的属性值
& =~与非 或者(^异或):去掉Mask类型的属性值
2、View的测量
onMeasure方法里面调用setMeasuredDimension()确定当前View的大小
3、ViewGroup的测量
1、遍历测量Child,可以通过下面三个方法来遍历测量Child
measureChildWithMargins
measureChild
measureChildren
、setMeasuredDimension 确定当前ViewGroup的大小
4、假如去自定义View,ViewGroup,要如何做好Measure?
View自定义控件套路
套路:最终调用setMeasuredDimession方法来保存自己的测量宽高
final int specMode = MeasureSpec.getMode(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
/* Parent says we can be as big as we want. Just don't be larger
than max size imposed on ourselves.
*/
result = Math.min(desiredSize, maxSize);
break;
case MeasureSpec.AT_MOST:
// Parent says we can be as big as we want, up to specSize.
// Don't be larger than specSize, and don't be larger than
// the max size imposed on ourselves.
result = Math.min(Math.min(desiredSize, specSize), maxSize);
break;
case MeasureSpec.EXACTLY:
// No choice. Do what we are told.
result = specSize;
break;
}
return result;
ViewGroup绘制套路
套路:
1、测量子view的规格大小
measureChildWithMargins
measureChild
measureChildren
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
lp.leftMargin - lp.rightMargin,
MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
if (lp.height == LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
lp.topMargin - lp.bottomMargin,
MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
2、通过子view的规格大小来确定自己的大小 setMeasuredDimession
ViewGroup layout套路
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom,
boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
最后附上经典的FlowLayout
public class FlowLayout extends ViewGroup {
private int mGravity = (isIcs() ? Gravity.START : Gravity.LEFT) | Gravity.TOP;
private final List> mLines = new ArrayList>();
private final List mLineHeights = new ArrayList();
private final List mLineMargins = new ArrayList();
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.FlowLayout, defStyle, 0);
try {
int index = a.getInt(R.styleable.FlowLayout_android_gravity, -1);
if (index > 0) {
setGravity(index);
}
} finally {
a.recycle();
}
init();
}
private void init() {
invalidate();
}
/**
* {@inheritDoc}
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
int width = 0;
int height = getPaddingTop() + getPaddingBottom();
int lineWidth = 0;
int lineHeight = 0;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
boolean lastChild = i == childCount - 1;
if (child.getVisibility() == View.GONE) {
if (lastChild) {
width = Math.max(width, lineWidth);
height += lineHeight;
}
continue;
}
measureChildWithMargins(child, widthMeasureSpec, lineWidth, heightMeasureSpec, height);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int childWidthMode = MeasureSpec.AT_MOST;
int childWidthSize = sizeWidth;
int childHeightMode = MeasureSpec.AT_MOST;
int childHeightSize = sizeHeight;
if (lp.width == LayoutParams.MATCH_PARENT) {
childWidthMode = MeasureSpec.EXACTLY;
childWidthSize -= lp.leftMargin + lp.rightMargin;
} else if (lp.width >= 0) {
childWidthMode = MeasureSpec.EXACTLY;
childWidthSize = lp.width;
}
if (lp.height >= 0) {
childHeightMode = MeasureSpec.EXACTLY;
childHeightSize = lp.height;
} else if (modeHeight == MeasureSpec.UNSPECIFIED) {
childHeightMode = MeasureSpec.UNSPECIFIED;
childHeightSize = 0;
}
child.measure(
MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode),
MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode)
);
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
if (lineWidth + childWidth > sizeWidth) {
width = Math.max(width, lineWidth);
lineWidth = childWidth;
height += lineHeight;
lineHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
} else {
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
}
if (lastChild) {
width = Math.max(width, lineWidth);
height += lineHeight;
}
}
width += getPaddingLeft() + getPaddingRight();
setMeasuredDimension(
(modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width,
(modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height);
}
/**
* {@inheritDoc}
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mLines.clear();
mLineHeights.clear();
mLineMargins.clear();
int width = getWidth();
int height = getHeight();
int linesSum = getPaddingTop();
int lineWidth = 0;
int lineHeight = 0;
List lineViews = new ArrayList();
float horizontalGravityFactor;
switch ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK)) {
case Gravity.LEFT:
default:
horizontalGravityFactor = 0;
break;
case Gravity.CENTER_HORIZONTAL:
horizontalGravityFactor = .5f;
break;
case Gravity.RIGHT:
horizontalGravityFactor = 1;
break;
}
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
LayoutParams lp = (LayoutParams) child.getLayoutParams();
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
int childHeight = child.getMeasuredHeight() + lp.bottomMargin + lp.topMargin;
if (lineWidth + childWidth > width) {
mLineHeights.add(lineHeight);
mLines.add(lineViews);
mLineMargins.add((int) ((width - lineWidth) * horizontalGravityFactor) + getPaddingLeft());
linesSum += lineHeight;
lineHeight = 0;
lineWidth = 0;
lineViews = new ArrayList();
}
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
lineViews.add(child);
}
mLineHeights.add(lineHeight);
mLines.add(lineViews);
mLineMargins.add((int) ((width - lineWidth) * horizontalGravityFactor) + getPaddingLeft());
linesSum += lineHeight;
int verticalGravityMargin = 0;
switch ((mGravity & Gravity.VERTICAL_GRAVITY_MASK)) {
case Gravity.TOP:
default:
break;
case Gravity.CENTER_VERTICAL:
verticalGravityMargin = (height - linesSum) / 2;
break;
case Gravity.BOTTOM:
verticalGravityMargin = height - linesSum;
break;
}
int numLines = mLines.size();
int left;
int top = getPaddingTop();
for (int i = 0; i < numLines; i++) {
lineHeight = mLineHeights.get(i);
lineViews = mLines.get(i);
left = mLineMargins.get(i);
int children = lineViews.size();
for (int j = 0; j < children; j++) {
View child = lineViews.get(j);
if (child.getVisibility() == View.GONE) {
continue;
}
LayoutParams lp = (LayoutParams) child.getLayoutParams();
// if height is match_parent we need to remeasure child to line height
if (lp.height == LayoutParams.MATCH_PARENT) {
int childWidthMode = MeasureSpec.AT_MOST;
int childWidthSize = lineWidth;
if (lp.width == LayoutParams.MATCH_PARENT) {
childWidthMode = MeasureSpec.EXACTLY;
} else if (lp.width >= 0) {
childWidthMode = MeasureSpec.EXACTLY;
childWidthSize = lp.width;
}
child.measure(
MeasureSpec.makeMeasureSpec(childWidthSize, childWidthMode),
MeasureSpec.makeMeasureSpec(lineHeight - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY)
);
}
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
int gravityMargin = 0;
if (Gravity.isVertical(lp.gravity)) {
switch (lp.gravity) {
case Gravity.TOP:
default:
break;
case Gravity.CENTER_VERTICAL:
case Gravity.CENTER:
gravityMargin = (lineHeight - childHeight - lp.topMargin - lp.bottomMargin) / 2;
break;
case Gravity.BOTTOM:
gravityMargin = lineHeight - childHeight - lp.topMargin - lp.bottomMargin;
break;
}
}
child.layout(left + lp.leftMargin,
top + lp.topMargin + gravityMargin + verticalGravityMargin,
left + childWidth + lp.leftMargin,
top + childHeight + lp.topMargin + gravityMargin + verticalGravityMargin);
left += childWidth + lp.leftMargin + lp.rightMargin;
}
top += lineHeight;
}
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
/**
* {@inheritDoc}
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
/**
* {@inheritDoc}
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return super.checkLayoutParams(p) && p instanceof LayoutParams;
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void setGravity(int gravity) {
if (mGravity != gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
gravity |= isIcs() ? Gravity.START : Gravity.LEFT;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.TOP;
}
mGravity = gravity;
requestLayout();
}
}
public int getGravity() {
return mGravity;
}
/**
* @return true
if device is running ICS or grater version of Android.
*/
private static boolean isIcs() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
}
public static class LayoutParams extends MarginLayoutParams {
public int gravity = -1;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FlowLayout_Layout);
try {
gravity = a.getInt(R.styleable.FlowLayout_Layout_android_layout_gravity, -1);
} finally {
a.recycle();
}
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}