自定义View 之 onMeasure的理解

自定义View一般需要重写onDraw()方法进行界面的绘制
如画一个矩形:
@Override
protected void  onDraw(Canvas canvas) {
    super.onDraw(canvas) ;
    canvas.drawRect(getLeft() ,getTop() ,getRight() ,getBottom() , mPanit) ;
}

其他具体的绘制方法请参见API
在布局中引入自定义的View时,设置layout_width和layout_height时,默认是支持具体值的(如 100p,100px),同时也支持match_parent(其实也是一个具体值,是父布局的高度和宽度),即EXACTLY模式。
如果要支持wrap_content 属性,需要重写onMeasure方法。如果不重写,wrap_content的效果是会填充整个父布局。因为他根本不知道自己的content有多大,所以只能去填充父布局。
通过代码验证一下:
MainActivity布局是一个纵向的LinerLayout ,里面放了一个高为300px的小LinerLayout,背景设为红色,以这个小LinerLayout作为自定义View的父布局。MyTextView是自定义View高度和宽度都设置为wrap_content 

<? xml version= "1.0"  encoding= "utf-8" ?>
<LinearLayout  xmlns: android = "http://schemas.android.com/apk/res/android"
    android :layout_width= "match_parent"
    android :layout_height= "match_parent"
    android :orientation= "vertical"
    android :fitsSystemWindows= "true"
    >
<LinearLayout
    android :layout_width= "match_parent"
    android :layout_height= "300px"
    android :background= "#ff0000" >
    <com.example.david.myview.myTextview
        android :layout_width= "wrap_content"
        android :layout_height= "wrap_content"  />
    <TextView
        android :layout_width= "wrap_content"
        android :layout_height= "wrap_content"
        android :text= "hello " />
</LinearLayout>


</LinearLayout>

MyTextView是一个自定义的View,通过重写onDraw()绘制一个和MyTextView一样大的绿色矩形,重写onMeasure()完成测量,同时支持wrap_content属性(这里用高度进行演示,宽度使用默认)。
public class myTextview  extends View {
    Paint  mPanit= new Paint() ;
    public myTextview(Context context) {
        super(context) ;
    }

    public myTextview(Context context AttributeSet attrs) {
        super(context attrs) ;
    }

    @Override
    protected void  onDraw(Canvas canvas) {
        super.onDraw(canvas) ;
        mPanit.setColor( 0XFF00FF00) ;
        canvas.drawRect(getLeft() ,getTop() ,getRight() ,getBottom() , mPanit) ;
    }

    @Override
    protected void  onMeasure( int widthMeasureSpec , int heightMeasureSpec) {
        setMeasuredDimension( getDefaultSize(getSuggestedMinimumWidth() widthMeasureSpec) ,measureHeight(heightMeasureSpec)) ;
        }

    private int  measureHeight( int heightMeasureSpec) {
        int result ;
        /**获取测量模式
         * wrap_content为  AT_MOST
         * match_parent和具体值为 EXACTLY*/
        int specMode=MeasureSpec. getMode(heightMeasureSpec) ;
        int specSize=MeasureSpec. getSize(heightMeasureSpec) ;
        if(specMode==MeasureSpec. EXACTLY){
           result=specSize ;
        } else{
            /**如果设置了wrap_content 则高度设置为200px 和 父布局 的最小值*/
            result = 200 ;
            if(specMode==MeasureSpec. AT_MOST){
                result=Math. min(result ,specSize) ;
            }
        }
        return result ;
    }
}
我们首先把onMeasure()方法注释掉,即不去重写,看一下效果

MyTextView即使使用了wrap_content,但是因为没有重写onMeasure(),仍然充满了父布局。
自定义View 之 onMeasure的理解_第1张图片
取消onMeasure()的注释
看下正常的效果:
自定义View 之 onMeasure的理解_第2张图片
MyTextView占据了200px ,父布局300px。


总结:一般重写onMeasure时需要给出对应情况(如内容多少)的具体大小,一般需要根据测量模式进行区分,就是说判断别人在使用这个View的时候使用的是wrap_content,match_parent还是具体值。

补充View的测量模式:
①、 AT_MOST(最大值模式),子元素至多达到指定大小的值。控件使用warp_content时,大小随子空间的变化而变化,
     只要不超过父布局的尺寸即可。
②、 EXACTLY(精确模式),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
控件使用具体值(如:100dp,100px),或者match_parent时,使用这种模式
       
③、 UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小,不常见

你可能感兴趣的:(安卓,Android开发,界面)