Android中自定义ViewGroup最重要的就是onMeasure和onLayout方法,都需要重写这两个方法,ViewGroup绘制 的过程是这样的:onMeasure → onLayout → DispatchDraw
其实我觉得官方文档解释有大大的问题,刚开始一直很疑惑onMeasure和onLayout是什么意思,看了很多资料后豁然开朗,总结如下
首先要知道ViewGroup是继承View的,后面的解释跟View有关。ViewGourp可以包含很多个View,View就是它的孩子,比如LinearLayout布局是一个ViewGroup,在布局内可以放TextEdit、ImageView等等常用的控件,这些叫子View,当然不限于这个固定的控件。
onMeasure → onLayout → DispatchDraw:onMeasure负责测量这个ViewGroup和子View的大小,onLayout负责设置子View的布局,DispatchDraw就是真正画上去了。
onMeasure
官方解释:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); //获取ViewGroup宽度 int height = MeasureSpec.getSize(heightMeasureSpec); //获取ViewGroup高度 setMeasuredDimension(width, height); //设置ViewGroup的宽高 int childCount = getChildCount(); //获得子View的个数,下面遍历这些子View设置宽高 for (int i = 0; i < childCount; i++) { View child = getChildAt(i); child.measure(viewWidth, viewHeight); //设置子View宽高 } }
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int opticalWidth = insets.left + insets.right; int opticalHeight = insets.top + insets.bottom; measuredWidth += optical ? opticalWidth : -opticalWidth; measuredHeight += optical ? opticalHeight : -opticalHeight; } mMeasuredWidth = measuredWidth; //这就是保存到类变量 mMeasuredHeight = measuredHeight; mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; }
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ......... // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); .......... }
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int mTotalHeight = 0; // 当然,也是遍历子View,每个都要告诉ViewGroup int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); // 获取在onMeasure中计算的视图尺寸 int measureHeight = childView.getMeasuredHeight(); int measuredWidth = childView.getMeasuredWidth(); childView.layout(left, mTotalHeight, measuredWidth, mTotalHeight + measureHeight); mTotalHeight += measureHeight; } }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec); //获取真实width int height = MeasureSpec.getSize(heightMeasureSpec); //获取真实height setMeasuredDimension(width, height); //设置ViewGroup的宽高 for (int i = 0; i < getChildCount(); i++) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); //遍历孩子设置宽高 } }
public static class MeasureSpec { private static final int MODE_SHIFT = 30; // private static final int MODE_MASK = 0x3 << MODE_SHIFT; public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); } }