自定义View(三)之View类的onMeasure方法详解
2017-05-20 15:52
441 查看
一,概述
在View(一)之初识自定义View中的第五条中已经简单将解过onMeasure方法,我们知道onMeasure方法的作用是测量,测量view的宽高。一般情况下测量的宽高就是实际显示的宽高,但也有特殊情况,特殊情况在ViewGroup的onLayout方法中讲解,目前先认为测量的宽高就等于实际显示的宽高。下面先讲解onMeasure方法的基本用法,然后再分析onMeasure方法的源码。
二,onMeasure方法的基本讲解
1,onMeasure方法
重写的onMeasure方法如下:protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec,heightMeasureSpec); }
onMeasure方法是View的生命周期方法,系统会自动调用。我们知到如果不重写onMeasure方法也可以,因为此时会执行View类的onMeasure方法,View类中的onMeasure方法会解析widthMeasureSpec和heightMeasureSpec参数,然后得到mMeasuredWidth和mMeasuredHeight。onMeasure方法的作用是得到测量宽高,本质就是给View的mMeasuredWidth字段和mMeasuredHeight字段赋值。
系统调用onMeasure方法时传递的参数widthMeasureSpec和heightMeasureSpec与在布局文件中设置的layout_width和layout_height有关。下面重点讲解MeasureSpec类。
2,MeasureSpec类
MeasureSpec类成为测量规则,通过这个类可以把测量模式和测量大小两个int型的数值封装成一个int型的数值。onMeasure方法的参数widthMeasureSpec和heightMeasureSpec就是封装后的数值。当然MeasureSpec类也可以把封装后的数值解析为测量模式和测量大小两个数值。封装方法如下:
//这个方法需要传递两个参数,参数一是测量大小,参数二是测量模式。 int mWidthMeasureSpec = MeasureSpec.makeMeasureSpec(300,widthMode);
解析方法如下:
//获取测量大小 int widthSize = MeasureSpec.getSize(widthMeasureSpec); //获取测量模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
MeasureSpec类的作用就是封装数据,没有其他功能。
测量大小很好理解,就是尺寸。那么测量模式是什么呢?
3,测量模式
测量模式有三种,分别如下:1,MeasureSpec.AT_MOST: 在布局文件中对应的是wrap_content;
2,MeasureSpec.EXACTLY: 在布局文件中对应的是具体的dp值和match_parent;
3,MeasureSpec.UNSPECIFIED: 未定义的,一般只在adapter的测量中,比如ListView。还有在ScrollView中
测量模式有什么作用呢?后面在onMeasure方法中会讲。
4,setMeasuredDimension方法
方法的用法://接收两个参数,分别是宽和高 setMeasuredDimension(300,300);
使用setMeasuredDimension方法可以直接指定view的宽度和高度。
三,onMeasure方法的用法
我们知道onMeasure方法的作用是测量view的大小。在代码层面的作用是给View的mMeasuredWidth字段和mMeasuredHeight字段赋值。这两个值即是view显示的宽高。给View设置宽高有三种方法:
1,使用布局文件
自定义View时可以不重写onMeasure方法,它会调用父类的onMeasure方法。onMeasure方法的两个参数与布局文件中的设置有关,所以我们在布局文件中设置layout_width和layout_height两个属性值给View设置宽高。在布局文件中,宽高指定wrap_content和match_parent都是充满屏幕的,若是固定值则显示固定值。
注:这种方式不用重新onMeasure方法。
2,使用super的onMeasure方法,
我们可以根据MeasureSpec类得到测量规则,然后将新的测量规则传递给super的onMeasure方法。代码如下:protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec);//获取测量模式 int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获取测量模式 int mWidthMeasureSpec = MeasureSpec.makeMeasureSpec(300,widthMode);//得到新的测量规则,宽度为300px int mHeightMeasureSpec = MeasureSpec.makeMeasureSpec(300,heightMode);//得到新的测量规则,高度为300px super.onMeasure(mWidthMeasureSpec, mHeightMeasureSpec); }
注:使用这种方式时,在布局文件中设置的宽高将会没有效果。
3,setMeasuredDimension方法
使用这种方法最简单://接收两个参数,分别是宽和高 setMeasuredDimension(300,300);
注:使用这种方式时,在布局文件中设置的宽高将会没有效果。
3,三种方法的优缺点
1,如果我们明确知道需要显示的宽度和高度,则不用重写onMeasure方法,直接在布局文件中设置宽度和高度就行。2,当不在布局文件中使用,或者测量的宽度和高度需要逻辑计算出来,这时就使用setMeasuredDimension方法。
3,当需要指定测量规则,又需要计算时,可以使用第二种方式。
四,onMeasure方法的源码分析
1,onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
看到这个代码好熟悉的感觉,又看到了setMeasuredDimension方法。但是此时setMeasuredDimension方法的参数是由getDefaultSize方法得到的。下面看getDefaultSize方法的源码。
2,getDefaultSize方法
getDefaultSize方法源码如下: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; }
分析:
这个方法中得到了测量模式和测量大小。如果测量模式是UNSPECIFIED,即不确定,即有Listview或ScrollView时是这种模式,这种模式返回值是size。如果是另外两种测量模式则返回测量大小。
下面看这个size值是怎么得到的,在onMeasure方法中使用getSuggestedMinimumWidth方法得到size值,getSuggestedMinimumWidth方法的源码如下:
protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }
首先判断是否有背景,如果没有背景则得到最小尺寸,如果没有 设置最小尺寸则返回0。
3,setMeasuredDimension方法
方法的源码是: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; } setMeasuredDimensionRaw(measuredWidth, measuredHeight); }
首先调用了isLayoutModeOptical方法,这是一个很重要的方法,在很多地方有使用。意思是:是否有可见的bound。这个方法的源码是:
public static boolean isLayoutModeOptical(Object o) { return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical(); }
显然对于View ,isLayoutModeOptical(this)返回值是false,
isLayoutModeOptical方法的源码是:
boolean isLayoutModeOptical() { return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS; }
mLayoutMode 的声明是:
private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
一般情况下很少会改变mLayoutMode 的值,所以isLayoutModeOptical(mParent)也返回false。
所以会直接调用setMeasuredDimensionRaw方法。这个方法的源码是:
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; }
此时给mMeasuredWidth 和mMeasuredHeight 赋值。到此完成了onMeasure方法的任务。
五,总结
1,onMeasure方法的作用
onMeasure方法的作用是得到测量宽高,本质就是给View的mMeasuredWidth字段和mMeasuredHeight字段赋值。然后使用getMeasureWidth方法和getMeasureHeight方法可以得到测量后的宽和高,然后供onLayout方法和onDraw方法使用。2,onMeasure方法的使用方式
在自定义View时,很多情况下如果该View在布局文件中使用,是不需要重写onMeasure方法的,宽高可以直接在布局文件中设置。如果不在布局文件中使用,可以直接使用setMeasuredDimension方法。如果需要全屏显示,就获取屏幕的宽度和高度。如果包裹内容显示,就获取内容的宽度和高度,如果指定宽度值和高度值,那就指定高度值和宽度值。
相关文章推荐
- [Android自定义View] 计算View尺寸方法onMeasure()
- Android自定义View实现绘制虚线的方法详解
- android 自定义view 里onMeasure方法里使用getWidth()=0
- 自定义View,关于onMeasure方法的两个参数widthMeasureSpec,heightMeasureSpec
- Android自定义View的实现方法实例详解
- Android自定义控件:图片比例适配,解决图片白边(详解View中onMeasure方法)
- Android填坑之旅(第十四篇)关于软键盘弹出未及时隐藏导致自定义View的onMeasure方法测量错误引发的血案
- 【转载】自定义View学习笔记之详解onMeasure
- Android中自定义一个View的方法详解
- 自定义View之onDraw方法详解
- Android游戏开发之旅View类详解自定义View的常用方法
- 继承ViewGroup重写onMeasure方法的详解
- 自定义view当中的onmeasure()方法详解
- 安卓自定义View基础06-View的onMeasure(),onDraw()方法详解以及Padding的处理
- Android自定义view中必不可少的哪些方法和参数详解
- 自定义view--onMeasure方法的作用
- Android -- 自定义布局View之 onMesasure() 方法详解
- 自定义View初探-onMeasure()详解
- 自定义View精炼详解第(三)课:onDraw()方法解析和小白级案例实现
- 继承ViewGroup重写onMeasure方法的详解