自定义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