在我们自定义View的时候,在默认情况下,也就是继承View之后,什么都不干。我们设置这个自定义的View的宽高都为wrap_content。
public class CustomView extends View { public CustomView(Context context) { super(context); } public CustomView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
我们发现View填充了父View,也就是相对于match_parent的效果,这是为什么呢?
要想弄明白为什么。我们需要知道默认情况下,View是怎么设置这个大小的。
onMeasure的默认实现就是下面这几行代码。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
getDefaultSize是我们的关键代码,这个方法是一个工具方法,因为在自定义View的时候需要从measureSpec读取出size和mode,并且还要根据mode做判断,宽高都一样的流程,所以做了一个封装。
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; }
这里有个关键点是MeasureSpec.AT_MOST是没有break语句的,也就是说AT_MOST和EXACTLY走的是同一段代码。AT_MOST对应wrap_content,EXACTLY对应match_parent和固定值。所以wrap_content和match_parent一个效果。这也就回答了为什么wrap_content和match_parent是一个效果。
这里就不得不引申出一个问题?为什么EXACTLY能够实现match_parent的效果?
从上面的代码可以看到,关键就在于specSize这个变量。
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; } //Undefine的代码 //... //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
这里最关键的是获取size这行代码,size指的是父View还有多少空间让子View填充。switch判断里面的每个case都要做三个判断,是固定值,LayoutParams.MATCH_PARENT还是LayoutParams.WRAP_CONTENT。因为用户在xml里面设置的方式就这三种,所以都要判断,不过在MeasureSpec.AT_MOST还是在MeasureSpec.EXACTLY里面,都是赋值为这个size,也就是填充父布局大小,实现的效果也就是所谓的match_parent了。
int size = Math.max(0, specSize - padding);
参考:https://cloud.tencent.com/developer/article/1394231
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)