Android技术知识Android进阶之路Android自定义View

Android View的测量

2020-09-18  本文已影响0人  进击的包籽

1.LayoutParams布局参数

1.了解LayoutParams

2.优先级问题

3.LayoutParams参数

4.addView

/**
 * 重载方法1:添加一个子View
 * 如果这个子View还没有LayoutParams,就为子View设置当前ViewGroup默认的LayoutParams
 *
 * @param child
 */
@Override
public void addView(View child) {
    addView(child, -1);
}
 
 
/**
 * 重载方法2:在指定位置添加一个子View
 * 如果这个子View还没有LayoutParams,就为子View设置当前ViewGroup默认的LayoutParams
 *
 * @param child
 * @param index View将在ViewGroup中被添加的位置(-1代表添加到末尾)
 */
@Override
public void addView(View child, int index) {
    if (child == null) {
        throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
    }
    LayoutParams params = child.getLayoutParams();
    if (params == null) {
        params = generateDefaultLayoutParams();
        if (params == null) {
            throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
        }
    }
    addView(child, index, params);
}
 
 
/**
 * 重载方法3:添加一个子View
 * 使用当前ViewGroup默认的LayoutParams
 *
 * @param child
 * @param width  传入参数作为LayoutParams的width
 * @param height 传入参数作为LayoutParams的height
 */
@Override
public void addView(View child, int width, int height) {
    final LayoutParams params = generateDefaultLayoutParams();
    params.width = width;
    params.height = height;
    addView(child, -1, params);
}
 
 
/**
 * 重载方法4:添加一个子View
 *
 * @param child
 * @param params 使用传入的LayoutParams
 */
@Override
public void addView(View child, LayoutParams params) {
    addView(child, -1, params);
}
 
 
/**
 * 重载方法5:添加一个子View,
 *
 * @param child
 * @param index  View将在ViewGroup中被添加的位置
 * @param params 使用传入的LayoutParams
 */
@Override
public void addView(View child, int index, LayoutParams params) {
    super.addView(child, index, params);
}

5.generateDefaultLayoutParams方法

protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}

6.checkLayoutParams方法

 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return  p != null;
   }
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
       return p instanceof LinearLayout.LayoutParams;
}

2.MeasureSpec

1.测量View大小(onMeasure)

2.MeasureSpec参数

3.获取参数

@Override
protected void onMeasure(intwidthMeasureSpec,intheightMeasureSpec){
      //取出宽度的确切数值
      int widthsizeMeasureSpec.getSize(widthMeasureSpec);
      //取出宽度的测量模式
      int widthmodeMeasureSpec.getMode(widthMeasureSpec);
      //取出高度的确切数值
      int heightsizeMeasureSpec.getSize(heightMeasureSpec);
      //取出高度的测量模式
      int heightmodeMeasureSpec.getMode(heightMeasureSpec);
}

4.三种测量模式

模式 二进制的值 描述
UNSPECIFIED 00 默认值,父控件没有给子view任何限制,子View可以设置为任意大小。
EXACTLY 01 表示父控件已经确切的指定了子View的大小。
AT_MOST 10 表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小。

5.获取子View的MeasureSpec(getChildMeasureSpec)

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    //父控件的测量模式
    int specMode = MeasureSpec.getMode(spec);
    //父控件的测量的大小
    int specSize = MeasureSpec.getSize(spec);
 
    //减去内边距
    int size = Math.max(0, specSize - padding);
 
    int resultSize = 0;
    int resultMode = 0;
 
    switch (specMode) {
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
        //父布局精确模式
        if (childDimension >= 0) {
            //子view的布局参数也是确定大小,那子view模式就是精确模式
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size. So be it.
            //子view是MATCH_PARENT,那也是精确模式,父布局多大,子view就多大
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            //子view是WRAP_CONTENT,那最大不能超过父布局的大小,就为AT_MOST
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
   。
   。  
   。
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

6.确定View大小(onSizeChanged)

7.获取测量后的大小(getMeasuredWidth)

8.获取宽高(getWidth)

3.具体使用

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //父布局的宽高
    int viewGroupWidth = MeasureSpec.getSize(widthMeasureSpec);
    int viewGroupHeight = MeasureSpec.getSize(heightMeasureSpec);

    //遍历所有子View
    for (int i = 0; i < getChildCount(); i++) {
        View childView = getChildAt(i);
        //如果view可见
        if (childView.getVisibility() == View.VISIBLE) {
            //子view的布局参数
            LayoutParams childLayoutParams = childView.getLayoutParams();

            //子view的measureSpec,传入父布局的measureSpec,还有父布局设置的内边距,子view的大小(大于0就是确定的大小,-1是match_parent,-2是wrap_content)
            int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, leftPadding + rightPadding, childLayoutParams.width);
            int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, topPadding + bottomPadding, childLayoutParams.height);

            //子view调用了measure才能得出确切的宽高
            childView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

            //子View的宽高
            int childMeasureWidth = childView.getMeasuredWidth();
            int childMeasureHeight = childView.getMeasuredHeight();

           

    }


    //再测量ViewGroup
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    int realWidth = widthMode == MeasureSpec.EXACTLY ? viewGroupWidth : viewGroupNeedWidth;
    int realHeight = heightMode == MeasureSpec.EXACTLY ? viewGroupHeight : viewGroupNeedHeight;

    setMeasuredDimension(realWidth, realHeight);
}

4.参考资料

上一篇 下一篇

猜你喜欢

热点阅读