自定义view-onMeasure的理解
来源:互联网 发布:oracle数据库游标分页 编辑:程序博客网 时间:2024/04/29 07:18
参考:Android自定义View(三、深入解析控件测量onMeasure)
onMeasure方法是由父控件调用的,所有父控件都是ViewGroup的子类,
ViewGroup是一个抽象类,它里面有一个抽象方法onLayout,这个方法的作用就是摆放它所有的子控件(安排位置),
因为是抽象类,不能直接new对象,所以我们在布局文件中可以使用View但是不能直接使用 ViewGroup。
在给子控件确定位置之前,必须要获取到子控件的大小(只有确定了子控件的大小才能正确的确定上下左右四个点的坐标),
而ViewGroup并没有重写View的onMeasure方法,也就是说抽象类ViewGroup没有为子控件测量大小的能力,它只能测量自己的大小。
但是既然ViewGroup是一个能容纳子控件的容器,系统当然也考虑到测量子控件的问题,
所以ViewGroup提供了三个测量子控件相关的方法(measuireChildren\measuireChild\measureChildWithMargins),
只是在ViewGroup中没有调用它们,所以它本身不具备为子控件测量大小的能力。
为什么都有测量子控件的方法了而ViewGroup中不直接重写onMeasure方法,然后在onMeasure中调用呢?
因为不同的容器摆放子控件的方式不同,比如RelativeLayout,LinearLayout这两个ViewGroup的子类,
它们摆放子控件的方式不同,有的是线性摆放,而有的是叠加摆放,这就导致测量子控件的方式会有所差别,
所以ViewGroup就干脆不直接测量子控件,他的子类要测量子控件就根据自己的布局特性重写onMeasure方法去测量。
测量的时候父控件的onMeasure方法会遍历他所有的子控件,挨个调用子控件的measure方法,measure方法会调用onMeasure,
然后会调用setMeasureDimension方法保存测量的大小,一次遍历下来,第一个子控件以及这个子控件中的所有子控件都会完成测量工作;
然后开始测量第二个子控件…;最后父控件所有的子控件都完成测量以后会调用setMeasureDimension方法保存自己的测量大小。
值得注意的是,这个过程不只执行一次,也就是说有可能重复执行,
因为有的时候,一轮测量下来,父控件发现某一个子控件的尺寸不符合要求,就会重新测量一遍。
如果要自定义ViewGroup就必须重写onMeasure方法,在这里测量子控件的尺寸。子控件的尺寸怎么测量呢?ViewGroup中提供了三个关于测量子控件的方法,源码如下:
/** *遍历ViewGroup中所有的子控件,调用measuireChild测量宽高 */ protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } } /** * 测量某一个child的宽高 */ protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }/** * 测量某一个child的宽高,考虑margin值 */ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 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); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
onMeasure源码:
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension( getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}/** * 为宽度获取一个建议最小值 */protected int getSuggestedMinimumWidth () { 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;}
从源码我们了解到:
如果View的宽高模式为未指定,他的宽高将设置为android:minWidth/Height =”“值与背景宽高值中较大的一个;
如果View的宽高 模式为 EXACTLY (具体的size ),最终宽高就是这个size值;
如果View的宽高模式为EXACTLY (填充父控件 ),最终宽高将为填充父控件;
如果View的宽高模式为AT_MOST (包裹内容),最终宽高也是填充父控件。
如果我们的自定义控件在布局文件中,只需要设置指定的具体宽高,或者MATCH_PARENT 的情况,我们可以不用重写onMeasure方法。
但如果自定义控件需要设置包裹内容WRAP_CONTENT ,我们需要重写onMeasure方法,为控件设置需要的尺寸;默认情况下WRAP_CONTENT 的处理也将填充整个父控件。
onMeasure方法最后需要调用setMeasuredDimension方法来保存测量的宽高值。
测量控件大小是父控件发起的
父控件要测量子控件大小,需要重写onMeasure方法,然后调用measureChildren或者measureChildWithMargin方法
on Measure方法的参数是通过getChildMeasureSpec生成的
如果我们自定义控件需要使用wrap_content,我们需要重写onMeasure方法
测量控件的步骤:
父控件onMeasure->measureChildrenmeasureChildWithMargin->getChildMeasureSpec->
保存自己的大小
子控件的measure->onMeasure->setMeasureDimension->
父控件onMeasure结束调用setMeasureDimension
自定义ViewGroup的步骤定为下面几步:
1. 继承ViewGroup,覆盖构造方法
2. 重写onMeasure方法测量子控件和自身宽高
3. 实现onLayout方法摆放子控件
自定义view的方法调用流程:
View被创建onFinishInflate()onMeasure()onMeasure()onSizeChanged(),w:1080,h:1080,oldw:0,oldh0onLayout()onDraw()
- 自定义View的OnMeasure理解
- 自定义View---------->onMeasure()的理解
- 自定义view-onMeasure的理解
- 自定义View的onMeasure方法理解
- 自定义View 之 onMeasure的理解
- 自定义View的时候onMeasure()理解
- Android 自定义View onMeasure 的理解
- Android 自定义View onMeasure理解
- 自定义View的onMeasure()
- android中对自定义View的onMeasure()方法的理解
- View.onMeasure方法的理解
- View的onMeasure参数理解
- 自定义View的onMeasure过程
- 自定义View的onMeasure、onLayout
- 自定义view绕不开的OnMeasure
- 自定义View的OnMeasure问题
- android 自定义view中onMeasure()理解
- android 自定义view中onMeasure()理解
- 下载百度编辑器ueditor
- RecyclerView的使用
- 【深度相机系列二】深度相机原理揭秘--飞行时间(TOF)
- 为什么要进行内存对齐以及对齐规则
- 移动端使用PhotoSwipe 图片放大浏览
- 自定义view-onMeasure的理解
- angularjs实现
- Java Jvm运行机制原理
- 剑指offer算法题之替换空格
- Handler实现换图片
- angularjs的 优化反选
- HTML列表
- MD5加密的Java实现
- JAVA_ant详解