在做项目的时候需要自定义View,当时对onMeasure()还不是很了解,现在了解了一些,希望能和大家分享。
一、自定义View
自定义View的绘制
1.测量——onMeasure():决定View的大小
2.布局——onLayout():决定View在ViewGroup中的位置
3.绘制——onDraw():如何绘制这个View。
下面简单了解一下自定义View的绘制过程:
第一步:
当activity启动的时候,触发初始化view过程的是由Window对象的DecorView调用View(具体怎样从xml中读取是用LayoutInflater.from(context).inflate)对象的 public final void measure(int widthMeasureSpec, int heightMeasureSpec)方法开始的,这个方法是final类型的,也就是所有的子类都不能继承该方法,保证android初始化view的原理不变。具体参数类值,后面会介绍。
第二步:
View的measure方法onMeasure(widthMeasureSpec,heightMeasureSpec),该方法进行实质的view大小计算。view的大小是由父view和自己的大小据定的。onMeasure的实质就是把Match_parent和wrap_content转换为实际大小 。
第三步:
当measure结束时,回到DecorView,计算大小计算好了,那么就开始布局了,开始调用view的 public final void layout(int l, int t, int r, int b),该还是也是final类型的,目的和measure方法一样。layout方法内部会调用onlayout(int l, int t, int r, int b )方法,二ViewGroup将此方法abstract的了,所以我们继承ViewGroup的时候,需要重新该方法。该方法的本质是通过measure计算好的大小,计算出view在屏幕上的坐标点
第四步:
measure过了,layout过了,那么就要开始绘制到屏幕上了,所以开始调用view的 public void draw(Canvas canvas)方法,此时方法不是final了,原因是程序员可以自己画,内部会调用ondraw,我们经常需要重写的方法。
二、onMeasure()
一段例子:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(0,widthMeasureSpec), getDefaultSize(0,heightMeasureSpec));
int childWidthSize = getMeasuredWidth();
int childHeightSize = getMeasuredHeight();
//高度和宽度一样
heightMeasureSpec = widthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
1、onMeasure的参数来源和代表的意思
onMeasure(widthMeasureSpec,heightMeasureSpec)
两个参数是父控件传递过来的,代表父控件的规格。
widthMeasureSpec:宽 详细 测量值
heightMeasureSpec:高 详细 测量值
MeasureSpec参数的值为int型,分为高32位和低16为,高32位保存的是specMode,低16位表示specSize
2、setMeasuredDimension( )
setMeasuredDimension用来存储测量的宽高值。
3、getDefaultSize( )
/**
* 作用是返回一个默认的值,如果MeasureSpec没有强制限制的话则使用提供的大小.否则在允许范围内可任意指定大小
* 第一个参数size为提供的默认大小,第二个参数为测量的大小
*/
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
// Mode = UNSPECIFIED,AT_MOST时使用提供的默认大小
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
// Mode = EXACTLY时使用测量的大小
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
4、getMeasuredWidth( )
getMeasuredWidth用来得到某个View想要在parent view里面占的大小
5、MeasureSpec类
MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。
三种模式:
MeasureSpec.UNSPECIFIED:父视图不对子视图施加任何限制,子视图可以得到任意想要的大小;
MeasureSpec.EXACTLY:父视图希望子视图的大小是specSize中指定的大小;
MeasureSpec.AT_MOST:子视图的大小最多是specSize中的大小。
它常用的三个函数:
static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)
static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)
static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)