自定义View:OnMeasure的重写

作者: tmyzh | 来源:发表于2018-03-20 20:08 被阅读201次

自定义View的布局工作内容

测量阶段:从上到下递归地调用每个 View 或者 ViewGroup 的 measure() 方法,使他们的onMeasure工作,测量他们的尺寸并计算它们的位置。
布局阶段:从上到下递归地调用每个 View 或者 ViewGroup 的 layout() 方法,使他们的onLayout工作,把测得的它们的尺寸和位置赋值给它们。

view与ViewGroup的测量方式不一样

View的测量

修改尺寸

View 在 onMeasure() 中会修改自己的尺寸然后保存;

图片.png
super.onMeasure系统帮助计算一次宽高
自定义正方形图片,在onMeasure里面计算并且修改自己的尺寸并且调用setMeasuredDimension保存新的宽高
总结成三步

1 重写 onMeasure() 方法,并在里面调用 super.onMeasure(),触发原有的自我测量;
2 在 super.onMeasure() 的下面用 getMeasuredWidth() 和 getMeasuredHeight() 来获取到之前的测量结果,并使用自己的算法,根据测量结果计算出新的结果;
3 调用 setMeasuredDimension() 来保存新的结果。

完全自己测量尺寸

注意
这里就不用调用super.onMeasure();
在父View计算子View的尺寸时结果要满足父 View 给出的的尺寸限制
限制的分类:
1 UNSPECIFIED:不限制
2 AT_MOST:限制上限
3 EXACTLY:限制固定值
这个限制来自于onMeasure方法的两个参数

   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int measureWidth=200;
        int measureHeight=300;
        //resolveSize 系统根据子View的需求和子View的限定来计算
//        resolveSize(measureWidth,widthMeasureSpec);
        measureWidth=reSize(measureWidth,widthMeasureSpec);
        measureHeight=reSize(measureHeight,heightMeasureSpec);

        Log.e("yzh","width--"+measureWidth+"---heigt--"+measureHeight);
        setMeasuredDimension(measureWidth,measureHeight);
    }

reSize是我们根据View的需求和View的限定来自己计算的一个方法

  public int reSize(int size,int measureSpec){
        int specMode =MeasureSpec.getMode(measureSpec);
        int specSize=MeasureSpec.getSize(measureSpec);
        switch (specMode){
            case MeasureSpec.UNSPECIFIED:
                Log.e("yzh","MeasureSpec.UNSPECIFIED");
                return size;
            case MeasureSpec.AT_MOST:
                Log.e("yzh","MeasureSpec.AT_MOST"+specSize);
                if(size<=specSize){
                    return size;
                }else{
                    return specSize;
                }
            case MeasureSpec.EXACTLY:
                Log.e("yzh","MeasureSpec.EXACTLY"+specSize);
                return specSize;
            default:
                Log.e("yzh","default");
                return size;
        }
    }

做了几组测试(xml中是一个linearlayout 包含了我们的view)

//    LinearLayout--match_parent,match_parent   view_wrap_content,wrap_contentwrap  限制上限 view要自行计算宽高
//    03-20 10:34:54.711 3409-3409/com.example.issuser.rxtest E/yzh: MeasureSpec.AT_MOST1080
//03-20 10:34:54.711 3409-3409/com.example.issuser.rxtest E/yzh: MeasureSpec.AT_MOST1776
//03-20 10:34:54.711 3409-3409/com.example.issuser.rxtest E/yzh: width--200---heigt--300
/    linearlayout 200 match  view wrap  限制上限 view要自行计算宽高
//    03-20 10:38:23.477 4108-4108/com.example.issuser.rxtest E/yzh: MeasureSpec.AT_MOST600
//03-20 10:38:23.477 4108-4108/com.example.issuser.rxtest E/yzh: MeasureSpec.AT_MOST1776
//03-20 10:38:23.477 4108-4108/com.example.issuser.rxtest E/yzh: width--200---heigt--300
 //LinearLayout -match  view_50 xml有准确的诉求 以这个诉求优先 不用重新计算
//03-20 10:32:37.489 1230-1230/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY150
//03-20 10:32:37.489 1230-1230/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY150
//03-20 10:32:37.489 1230-1230/com.example.issuser.rxtest E/yzh: width--150---heigt--150
    //LinearLayout wrap  view 50 xml有准确的诉求 以这个诉求优先 不用重新计算
//    03-20 11:12:15.638 10778-10778/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY150
//03-20 11:12:15.638 10778-10778/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY150
//03-20 11:12:15.638 10778-10778/com.example.issuser.rxtest E/yzh: width--150---heigt--150
//    Linearlayout 200 match  view——match
///yzh: MeasureSpec.EXACTLY600
//03-20 10:40:30.828 4693-4693/com.example.issuser.rxtest E/yzh: MeasureSpec.EXACTLY1776
//03-20 10:40:30.828 4693-4693/com.example.issuser.rxtest E/yzh: width--600---heigt--1776
//    LinearLayout 200 wrap  view  match   限制上限 view要自行计算宽高
//    03-20 10:41:01.413 4988-4988/? E/yzh: MeasureSpec.EXACTLY600
//03-20 10:41:01.413 4988-4988/? E/yzh: MeasureSpec.AT_MOST1776
//03-20 10:41:01.413 4988-4988/? E/yzh: width--600---heigt--300

总结
EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;

ViewGroup的测量
ViewGroup 在 onMeasure() 中会调用所有子 View 的 measure() 让它们进行自我测量(onMeasure),并根据子 View 计算出的期望尺寸来计算出它们的实际尺寸和位置(会以子 View 给出的期望尺寸来优先作为实际尺寸)然后保存。同时,它也会根据子 View 的尺寸和位置来计算出自己的尺寸然后保存;

image.png

最后计算viewGroup的宽高

  /** 
         * 如果是wrap_content设置为我们计算的值 
         * 否则:直接设置为父容器计算的值 
         */  
        setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth  
                : width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight  
                : height);  

相关文章

网友评论

    本文标题:自定义View:OnMeasure的重写

    本文链接:https://www.haomeiwen.com/subject/jxwmqftx.html