最近在学习自定义View,其中的onMeasure方法一直不是很理解,今天练习了一下,下面写一下自己的一些体会。
首先,
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
方法有两个参数,我们就先只说其中的一个,因为两个意思是差不多的,对比理解即可。widthMeasureSpec虽然看起来只是一个整型的参数,其实它封装了Mode和Size两个整型,具体的实现方法这里就不阐述,使用
int specMode =MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
来分别获取,specSize看起来就比较好理解,那specMode又是什么意思呢,我们在XML文件中配置控件的时候会使用android:layout_width="wrap_content"或者match_parent或者fill_parent。这里的Mode就是指的这个。Mode一共有三种参数
EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
UNSPECIFIED:表示子布局想要多大就多大,很少使用
先看张图
再来我们的祖传代码
<com.zhy.customview02.view.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
zhy:image1="@drawable/lmj"
android:padding="10dp"
zhy:imageScaleType1="fillXY"
zhy:titleText1="妹子1111~"
zhy:titleTextColor1="#ff0000"
zhy:titleTextSize1="12sp" />
<com.zhy.customview02.view.CustomView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:padding="10dp"
zhy:image1="@drawable/lmj"
zhy:imageScaleType1="fillXY"
zhy:titleText1="妹子1111~"
zhy:titleTextColor1="#ff0000"
zhy:titleTextSize1="12sp" />
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 设置宽
*/
int specMode =MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if(specMode == MeasureSpec.EXACTLY) //match_parent
{
mWidth = specSize;
Log.e("match_parent_specSize", specSize+"");
Log.e("match_parent_mImage.getWidth()", mImage.getWidth()+"");
}else{
//由图片决定宽度
int desireByImg = getPaddingLeft()+getPaddingRight()+mImage.getWidth();
Log.e("desireByImg", desireByImg+"");
Log.e("getPaddingLeft", getPaddingLeft()+"");
Log.e("getPaddingRight", getPaddingRight()+"");
Log.e("mImage.getWidth()", mImage.getWidth()+"");
//由字体决定宽度
int desireByTitle = getPaddingLeft()+getPaddingRight()+mTextRect.width();
Log.e("desireByTitle", desireByTitle+"");
Log.e("getPaddingLeft()", getPaddingLeft()+"");
Log.e("getPaddingRight()", getPaddingRight()+"");
Log.e("mTextRect.width()", mTextRect.width()+"");
if(specMode == MeasureSpec.AT_MOST)//wrap_content
{
int desrie = Math.max(desireByImg, desireByTitle);
mWidth = Math.min(desrie, specSize);
}
//Log.e("mWidth", mWidth+"");
}
当Mode是 MeasureSpec.EXACTLY即我们xml中设置的 match_parent,当前的mWidth就直接等于specSize。这种情况比较简单,甚至可以不用重写onMeasure方法
当Mode是MeasureSpec.AT_MOST即我们xml中设置的wrap_content,我们需要手动的去测量宽和高。比如上图的d第二个例子中,可以明显的看出是match_parent的。再来说说第一个,这个设置为wrap_content,整个控件由文字和图片组成,我们先获取图片和文字谁决定需要的宽度大,然后和specSize进行比较,再去小的值,因为上面已经说过,AT_MOST:表示子布局限制在一个最大值内。
关于图片决定的宽度如何计算,在上面的图中,可以看出
int desireByImg = getPaddingLeft()+getPaddingRight()+mImage.getWidth();
padding就是指内边距,是控件边缘和内容之间的距离。这个算出来的desireByImg就是图片决定的整个宽度大小。
在我们测量完宽高之后还要调用
setMeasuredDimension(mWidth, mHight);
完成整个onMeasure方法之后,我们就可以在onDraw方法中进行View的绘制,使用getMeasuredWidth()和getMeasuredHeight()方法来获取控件的宽和高,如果之前没有重写onMeasure方法,会抛出异常。
对于什么时候需要重写onMeasure方法,如果自定义的CustomView采用默认的onMeasure函数,行为如下:
(1) CustomView设置为 match_parent 或者 wrap_content 没有任何区别,其显示大小由父控件决定,它会填充满整个父控件的空间。
(2) CustomView设置为固定的值,则其显示大小为该设定的值。
如果你的自定义控件的大小计算就是跟系统默认的行为一致的话,那么你就不需要重写onMeasure函数了。
最后在顺便提一下实现这例子的过程中遇到的一下问题
canvas.drawText (String text, float x, float y, Paint paint);
这个方法中的x和y参数,x比较好理解,就是文字绘制的起始位置,但是Y就比较复杂,它是整个文字的baseline在屏幕上的位置.盗张图片
关于文字的绘制以后有时候再好好学习一下
参考文章http://blog.csdn.net/lmj623565791/article/details/24252901