Android View的测量
- Android 的View是一个树结构,而View的大小,ViewGroup的大小,会相互影响,其中有两个重要参数
1.LayoutParams布局参数
1.理解LayoutParams
- LayoutParams就是布局参数,子View通过LayoutParams告诉父容器(ViewGroup)应该如何放置自己。
- 每个ViewGroup的子类都有自己对应的LayoutParams类,典型的如LinearLayout.LayoutParams和
FrameLayout.LayoutParams等,可以看出来LayoutParams都是对应ViewGroup子类的内部类。 MarginLayoutParams是和外间距有关的。和LayoutParams相比,MarginLayoutParams只是添加了对上下左右外间距的支持。实际上大部分LayoutParams的实现类都是继承自MarginLayoutParams,由于基本所有的父容器都是支持子View设置外间距的。
image.png
2.优先级问题
- 属性优先级问题 MarginLayoutParams主要就是添加了上下左右4种外间距。在构造方法中,先是获取了margin属性;假如该值不合法,就获取horizontalMargin;假如该值不合法,再去获取leftMargin和rightMargin属性(verticalMargin、topMargin和bottomMargin同理)。我们可以据此总结出这几种属性的优先级
- 优先级 Margin > HorizontalMargin和VerticalMargin > LeftMargin和RightMargin、TopMargin和BottomMargin
3.LayoutParams参数
- 在XML中定义View
在Java代码中直接生成View对应的实例对象 - 3.1.假如xml布局里面是具体的dp,那么layoutParams的值就是大于0。
- 3.2.假如是MATCH_PARENT
public static final int MATCH_PARENT = -1; 3.3.假如是WRAP_CONTENT
public static final int WRAP_CONTENT = -2;
LayoutParams.png
4.addView
/** * 重载方法1:增加一个子View * 假如这个子View还没有LayoutParams,就为子View设置当前ViewGroup默认的LayoutParams * * @param child */@Overridepublic void addView(View child) { addView(child, -1);} /** * 重载方法2:在指定位置增加一个子View * 假如这个子View还没有LayoutParams,就为子View设置当前ViewGroup默认的LayoutParams * * @param child * @param index View将在ViewGroup中被增加的位置(-1代表增加到末尾) */@Overridepublic void addView(View child, int index) { if (child == null) { throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup"); } LayoutParams params = child.getLayoutParams(); if (params == null) { params = generateDefaultLayoutParams(); if (params == null) { throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null"); } } addView(child, index, params);} /** * 重载方法3:增加一个子View * 使用当前ViewGroup默认的LayoutParams * * @param child * @param width 传入参数作为LayoutParams的width * @param height 传入参数作为LayoutParams的height */@Overridepublic void addView(View child, int width, int height) { final LayoutParams params = generateDefaultLayoutParams(); params.width = width; params.height = height; addView(child, -1, params);} /** * 重载方法4:增加一个子View * * @param child * @param params 使用传入的LayoutParams */@Overridepublic void addView(View child, LayoutParams params) { addView(child, -1, params);} /** * 重载方法5:增加一个子View, * * @param child * @param index View将在ViewGroup中被增加的位置 * @param params 使用传入的LayoutParams */@Overridepublic void addView(View child, int index, LayoutParams params) { super.addView(child, index, params);}5.generateDefaultLayoutParams方法
- addView源码往下找,就能找到generateDefaultLayoutParams方法,获取默认的LayoutParams
- ViewGroup 的 generateDefaultLayoutParams方法
protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);}6.checkLayoutParams方法
- addView源码一直往下找,就能找到checkLayoutParams方法
- ViewGroup 的 checkLayoutParams方法
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p != null; }- 非ViewGroup 的 checkLayoutParams方法,具体源码去看看
@Overrideprotected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p instanceof LinearLayout.LayoutParams;}2.MeasureSpec
1.测量View大小(onMeasure)
- View的大小不仅由自身所决定,同时也会受到父控件的影响,自身大小可能还会受到子view的影响,而且会屡次调用onMeasure方法。
2.MeasureSpec参数
- MeasureSpec是一个32位的int值,高2位存的是测量模式,低30位存的是具体测量大小,手机像素点几乎不可能超过这个值。
源码我们能看到定义
MeasureSpec.png
3.获取参数
@Overrideprotected void onMeasure(intwidthMeasureSpec,intheightMeasureSpec){ //取出宽度确实切数值 int widthsizeMeasureSpec.getSize(widthMeasureSpec); //取出宽度的测量模式 int widthmodeMeasureSpec.getMode(widthMeasureSpec); //取出高度确实切数值 int heightsizeMeasureSpec.getSize(heightMeasureSpec); //取出高度的测量模式 int heightmodeMeasureSpec.getMode(heightMeasureSpec);}4.三种测量模式
- 测量模式一共有三种
| 模式 | 二进制的值 | 形容 |
|---|---|---|
| UNSPECIFIED | 00 | 默认值,父控件没有给子view任何限制,子View可以设置为任意大小。 |
| EXACTLY | 01 | 表示父控件已经确切的指定了子View的大小。 |
| AT_MOST | 10 | 表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小。 |
5.获取子View的MeasureSpec(getChildMeasureSpec)
- ViewGroup要测量时,假如受到子view影响,就要先测量子view,getChildMeasureSpec方法,度量子view。
- 子view的测量,需要父布局VIewGroup的MeasureSpec,加上自己的LayoutParams布局参数来测量。
- 假如对View的宽高进行修改了,不要调用 super.onMeasure( widthMeasureSpec, heightMeasureSpec);
要调用 setMeasuredDimension( widthsize, heightsize); 这个函数。
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) { //子view的布局参数也是确定大小,那子view模式就是准确模式 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. //子view是MATCH_PARENT,那也是准确模式,父布局多大,子view就多大 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. //子view是WRAP_CONTENT,那最大不能超过父布局的大小,就为AT_MOST resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; 。 。 。 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}Android 开发艺术这书里面这张图大家应该都看过
image.png
6.确定View大小(onSizeChanged)
- 这个函数在视图大小发生改变时调用。
- 由于View的大小不仅由View本身控制,而且受父控件的影响,所以我们在确定View大小的时候最好使用系统提供的onSizeChanged回调函数。
7.获取测量后的大小(getMeasuredWidth)
- 在measure()过程结束后即可以获取到对应的值;
- 通过setMeasuredDimension()方法来进行设置的.
8.获取宽高(getWidth)
- 在layout()过程结束后才能获取到;
- 通过视图右边的坐标减去左边的坐标计算出来的
3.具体使用
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //父布局的宽高 int viewGroupWidth = MeasureSpec.getSize(widthMeasureSpec); int viewGroupHeight = MeasureSpec.getSize(heightMeasureSpec); //遍历所有子View for (int i = 0; i < getChildCount(); i++) { View childView = getChildAt(i); //假如view可见 if (childView.getVisibility() == View.VISIBLE) { //子view的布局参数 LayoutParams childLayoutParams = childView.getLayoutParams(); //子view的measureSpec,传入父布局的measureSpec,还有父布局设置的内边距,子view的大小(大于0就是确定的大小,-1是match_parent,-2是wrap_content) int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, leftPadding + rightPadding, childLayoutParams.width); int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, topPadding + bottomPadding, childLayoutParams.height); //子view调用了measure才能得出确切的宽高 childView.measure(childWidthMeasureSpec, childHeightMeasureSpec); //子View的宽高 int childMeasureWidth = childView.getMeasuredWidth(); int childMeasureHeight = childView.getMeasuredHeight(); } //再测量ViewGroup int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int realWidth = widthMode == MeasureSpec.EXACTLY ? viewGroupWidth : viewGroupNeedWidth; int realHeight = heightMode == MeasureSpec.EXACTLY ? viewGroupHeight : viewGroupNeedHeight; setMeasuredDimension(realWidth, realHeight);}4.参考资料
- 《Android开发艺术探究》
- 安卓自己设置View进阶
说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Android View的测量
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Android View的测量