View的绘制
阅读博客
Android View的绘制流程
主要理解MeasureSpec,
MeasureSpec
是一个32位的二进制整数,前两位代表mode,后30位代表size;
mode分为3中
- EXACTLY
父容器测量出子View的大小;精确值,如match_parent,或者指定大小 - AT_MOST
父容器限制子view的大小,不可超过父容器可用空间,对应wrap_content - UNSPECIFIED
父容器对子view不限制,;;;;;;网上博客都说这个基本没用到, 有时间再研究
view的绘制入口
private void performTraversals() {
......
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
......
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
......
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
......
mView.draw(canvas);
......
}
Measure
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
/*-----------省略代码---------------*/
onMeasure(widthMeasureSpec, heightMeasureSpec);
/*-----------省略代码---------------*/
}
//自定义view,重写这个,且需要调用setMeasuredDimension
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected int getSuggestedMinimumWidth() {
//返回建议 View 设置最小值宽度
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
/*
以上代码:建议值是mMinWidth(默认0),或者背景大小;
getDefaultSize,获取的就是父类计算出来的子类大小。
需要确认UNSPECIFIED有没有使用到(0或者backgroud),否则result=specSize(计算大小)
*/
//最终调用,这里面只是把两值存起来,onMeasure中会使用到
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
以上是view计算过程,
View的测量过程是先测量父View再测量子View;
查看LinearLayout,RelativeLayout(? extends )的onMeasure,里面重写了,最终调用了ViewGroup的measureChildWithMargins
以LinearLayout为例
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
......
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
......
}
void measureChildBeforeLayout(View child, int childIndex,
int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
int totalHeight) {
measureChildWithMargins(child, widthMeasureSpec, totalWidth,
heightMeasureSpec, totalHeight);
}
下面是ViewGroup测量子View的入口
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
//获取子View的LayoutParam
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//通过父View的MeasureSpec和子View的margin,父View的padding计算,算出子View的MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
//通过计算出来的MeasureSpec,让子View自己测量。
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
//计算子View的大小
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// 父View是EXACTLY的
case MeasureSpec.EXACTLY:
//子View的width或height是个精确值,则size为精确值,mode为 EXACTLY
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
//子View的width或height是MATCH_PARENT,则size为父视图大小,mode为 EXACTLY
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
//子View的width或height是WRAP_CONTENT,则size为父视图大小,mode为 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;
// 2、父View是AT_MOST的
case MeasureSpec.AT_MOST:
//子View的width或height是个精确值,则size为精确值,mode为 EXACTLY
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
//子View的width或height是MATCH_PARENT,则size为父视图大小,mode为 AT_MOST
} 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;
//子View的width或height是MATCH_PARENT,则size为父视图大小,mode为 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;
// 父View是UNSPECIFIED的
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
---------------------
作者:Android_韦鲁斯
来源:CSDN
原文:https://blog.csdn.net/sinat_27154507/article/details/79748010
版权声明:本文为博主原创文章,转载请附上博文链接!
以上代码是测量子view的MeasureSpec
以上代码看完理解了,这个就很简单;
最终结论是:(懒,直接copy..)
当父View的mode是EXACTLY的时候:说明父View的大小是确定的
子View的宽或高是MATCH_PARENT:
子View的宽或高是WRAP_CONTENT:子View是包裹布局,说明子View的大小还不确定,所以子View最大不能超过父View的大小mode=AT_MOST。
子View的宽或高是具体数值:子viewd大小已经固定了,子View的大小就是固定这个数值,mode=EXACTLY。
当父View的mode是AT_MOST的时候:说明父View大小是不确定的。
子View的宽或高是MATCH_PARENT:父View大小是不确定的,子View是填充布局情况,也不能确定大小,所以View大小不能超过父View的大小,mode=AT_MOST
子View的宽或高是WRAP_CONTENT:子View是包裹布局,大小不能超过父View的大小,mode=AT_MOST。
子View的宽或高是具体数值:子viewd大小已经固定了,子View的大小就是固定这个数值,mode=EXACTLY。
需要注意一点就是,此时的MeasureSpec并不是View真正的大小,只有setMeasuredDimension之后才能真正确定View的大小。
---------------------
作者:Android_韦鲁斯
来源:CSDN
原文:https://blog.csdn.net/sinat_27154507/article/details/79748010
版权声明:本文为博主原创文章,转载请附上博文链接!
==此处有个疑点==,父类是EXACTLY(match_parent),子类是wrap_content,上面定义最终是
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
那么子view应该和父类一样大?查看完后面代码,再来回答;
以上疑问:
==答答==,上面的代码是View的onMeasure(),里面默认实现是这样子的,如果在LinearLayout,直接放一个<View/>,就会是全屏, 而如果改成Button,就有一个默认大小,而不是全屏了
实际View会重写onMeasure,最终调用setMeasuredDimension(width, height); 这个是最终计算出来的宽高!
layout
略过, 这个主要就是根据group的规则,如何去排列子view,所有view的框高都已经计算好了。
draw
六部曲,2和5忽略;主要是
1、绘制当前背景
3、绘制当前view
4、绘制Children
6、绘制scrollbars
public void draw(Canvas canvas) {
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
...
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}