自定义view测量onMeasure梳理

自定义view流程的第一步,测量

onMeasure(),测量自己的大小,为正式布局提供建议(只是建议,用不用还要看onlayout()

测量过程通过measure方法实现,是View树的自顶向下的一次遍历,每个View在循环过程中将自己的尺寸向下传递,测量完成后所有的view都清楚自己的尺寸,

通过setMeasuredDimension(width, height);设置给系统

获取当前view的位置有


// 获取Top位置
public final int getTop() {  
    return mTop;  
}  

// 其余如下:
  getLeft();      //获取子View左上角距父View左侧的距离
  getBottom();    //获取子View右下角距父View顶部的距离
  getRight();     //获取子View右下角距父View左侧的距离

与MotionEvent中 get()和getRaw()的区别

view自身中心点为坐标原点
//get() :触摸点相对于其所在组件坐标系的坐标
 event.getX();       
 event.getY();

//getRaw() :触摸点相对于屏幕默认坐标系的坐标
 event.getRawX();    
 event.getRawY();

有关measure过程

测量规格(MeasureSpec) = 测量模式(mode) + 测量大小(size)

测量模式有三种:

模式具体秒速应用场景备注
UNSPECIFIED父视图不约束子视图,即view可取任意尺寸系统内部recyclerview等
EXACTLY– 父视图为子视图制定一个确切尺寸match_parent或者100dp利用父View的剩余空间
AT_MOST父视图为子视图制定一个最大尺寸,子视图必须确保自身可适应在其内部wrap_content将大小设置为包裹view的内容

使用如下:

    // 1. 获取测量模式(Mode)
    int specMode = MeasureSpec.getMode(measureSpec)

    // 2. 获取测量大小(Size)
    int specSize = MeasureSpec.getSize(measureSpec)

    // 3. 通过Mode 和 Size 生成新的SpecMode
    int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);
    
    //设置测量的宽高值
    setMeasuredDimension(width, height);

下面是一个viewGroup中的测量:

    /**
     * 测量子view
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //每一行的高度
        int lineWidth = 0;

        //每一行的高度
        int lineHeight = 0;

        //整个viewGroup的宽度
        int allWidth = 0;

        //整个viewGroup的高度
        int allHeight = 0;

        //获得系统的建议宽高
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        //获得控件的宽高模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);


        //获得子view的数量
        int count = getChildCount();

        //遍历测量子view
        for (int i = 0; i < count; i++) {

            //获得子view对象
            View childAt = getChildAt(i);

            //测量子view,通过自view对象以及父view的测量参数
            measureChild(childAt, widthMeasureSpec, heightMeasureSpec);

            //获得子view的margin的layoutParams来获得子view的四周间距,兼容自定义viewGroup设置了margin
            MarginLayoutParams layoutParams = (MarginLayoutParams) childAt.getLayoutParams();

            //获得子view测量的宽度
            int childWidth = childAt.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;

            //获得子view测量的高度
            int childHeight = childAt.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;


            //如果现在的宽度加上一个字view的宽度大于整个viewGroup的宽度,那么就需要换行显示
            if (lineWidth + childWidth > width) {

                //换行后,刷新viewGroup的宽度,记录一个最大值
                allWidth = Math.max(lineWidth, childWidth);

                //记录viewGroup的高度
                allHeight += lineHeight;

                lineWidth = childWidth;
                lineHeight = childHeight;

            } else {
                lineHeight = Math.max(lineHeight, childHeight);
                lineWidth += childWidth;
            }


            //当时最后一行的时候,就不会换行,所以需要手动加上最后一行的高度
            if (i == count - 1) {

                allHeight += lineHeight;

                //获得最大的宽度,比较值钱宽度的最大值,以及当前行的宽度值
                allWidth = Math.max(lineWidth, allWidth);
            }

        }
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? width : allWidth, heightMode == MeasureSpec.EXACTLY ? height : allHeight);

    }
    原文作者:ccccccal
    原文地址: https://www.jianshu.com/p/690bdfbeb560
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞