Android自定义控件

浅谈Android自定义VIew的测量

2017-07-26  本文已影响0人  我家老刘

目前在阅读Android群英传的第三章自定义控件架构和自定义控件,准备写下自己的学习笔记和一些心得,增强自己.如果文章有错误的地方,请各位朋友指出.文章的内容有些参考书中的东西和自己的理解.
**在此,感谢作者 **


1.Android自定义控件架构

2. Android自定义控件的测量(View的测量)

  @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
 代码示例:
  <com.example.wx.myapplication.MyView
        android:layout_below="@id/btn"
        android:layout_width="100px"
        android:layout_height="match_parent"
        android:id="@+id/my_view"
        android:background="@color/colorPrimary"
        />
<com.example.wx.myapplication.MyView
        android:layout_below="@id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/my_view"
        android:background="@color/colorPrimary"
        />

View类的onMeasure()方法默认只支持EXACTLY(精准模式),但是我们自定义的控件肯定要支持
layout_width=wrap_content,那么我们就要自己去写代码来支持AT_MOST模式.就需要在View中重写onMeasure()方法来实现.
为什么说View会默认支持EXACTLY,而灭有支持AT_MOST,我们先通过重写的onMearsure()方法中查看父类ViewonMearsure()方法,在super.onMearsure()中是这样实现的.

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

查看父类View的代码我们明白最终调用 setMeasuredDimension( )来江测量的宽高设置进去,而完成测量工作.所以在我们测量完宽高后也需要调用setMeasuredDimension()来完成测量后的设置.那么,我们去查看getSuggestedMinimumWidth()方法的实现.

protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }

此方法只返回了一个int值,并没有看见对模式的修改,那么回来我们接着看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;
    }

在这里我们发现,该方法初始化了一个resultint类型变量.并且通过MeasureSpec类的getmode()来获取测量模式和getSize()来获取测量后的值,然后进行switch.在这里我们发现有3个case,分别为MeasureSpec.UNSPECIFIED,直接将传过来的size直接返回.当caseMeasureSpec.AT_MOST的时候,我们发现什么都没有事实现,所以说View默认是不支持AT_MOST模式的.当caseMeasureSpec.EXACTLY的时候,是将测量后的值返回.

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     setMeasuredDimension(measureWidthOrHeight(widthMeasureSpec),measureWidthOrHeight(heightMeasureSpec));
    }
 private int measureWidthOrHeight(int measureSpec) {
        int result=0;
        //获取当前View的测量模式
        int mode = MeasureSpec.getMode(measureSpec);
        //精准模式获取当前Viwe测量后的值,如果是最大值模式,会获取父View的大小.
        int size = MeasureSpec.getSize(measureSpec);
        if (mode==MeasureSpec.EXACTLY){
            //当测量模式为精准模式,返回设定的值
            result=size;
        }else{
            //设置为WrapContent的默认大小
            result=50;
            if (mode==MeasureSpec.AT_MOST){
                //当模式为最大值的时候,默认大小和父类View的大小进行对比,返回最小的值
               result= Math.min(result,size);
            }
        }
        return result;
    }

onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法中,我将
super.onMeasure(int widthMeasureSpec, int heightMeasureSpec)删除,因为父类的方法中,直接进行了测量并将测量的值进行设置.在重写的onMearsure()方法中,我们需要对宽高重新测量并且支持AT_MOST模式,当然跟之前父类里面的实现原理是一样的,都是要调用setMeasuredDimension(int wdith,int height)设置测量后的值,只不过我们需要对宽高进行重新测量,重新测量的实现由measureWidthOrHeight(int measureSpec)实现.我们需要获取控件对应的测量模式,返回相对应的测量值.

上一篇 下一篇

猜你喜欢

热点阅读