Measuring and layout

ViewGroup drawing process

Rendering process is divided into three steps: measuring, layout, drawing, respectively onMeasure (), onLayout (), onDraw ()
  • onMeasure (): measure the size of the current control, advice (only recommended for the formal layout, as to whether to use depends onLayout () function
  • onLayout (): Use layout () function to lay out all the child controls
  • onDraw (): The position of the layout drawing

onMeasure () function and MeasureSpec

  • Design layout drawing two processes: process layout and measurement process, the measurement process by measure () function to achieve, is traversed top-down tree View, each View passed down during the circulation in the details of size, when the measurement process is completed after all View their dimensions are stored, then the layout process layout function is implemented by (), the function is a top-down, in this process, each parent View place by calculating the amount of help good dimensional sub View
onMeasure () function
//widthMeasureSpec: 当前父类传递过来的建议值的宽
//heightMeasureSpec:当前父类传递过来的建议值的高
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
MeasureSpec composition
  • Mode + size it is made of two parts, widthMeasureSpec heightMeasure and converted to binary numbers are 32, a schematic front 2, 30 indicates size
  • ** Pattern Classification: ** UNSPECIFIED (unspecified) EXACTLY the (full) AT_MOST (up)
    • UNSPECIFIED: parent element is applied not bound by any child elements, sub-elements can be obtained in any desired size, corresponding to the binary value: 00000000000000000000000000000000
    • EXACTLY :; decide the exact size of the parent element of the child element, the child element is defined in a given boundary ignored in the size of its own, a corresponding binary value: 01000000000000000000000000000000
    • AT_MOST: subelement up to reach a value of specified size, corresponding to the binary value: 10000000000000000000000000000000
  • Pattern extraction **: ** widthMeasureSpec and heightMeasureSpec by mode and numeric values, and the first two digits of the binary representation model
    eg: simulation mode Extraction:
int MODE_MASK = 0xc0000000;
public static int getMode(iint measureSpec){
    return (measureSpec & MODE_MASK);
}

public static int getSize(int measureSpec){
    return (measureSpec & ~MODE_MASK);
}

//Android中提供的提取模式的方法
//获取模式
MeasureSpec.getMode(int spec)
//获取数值
MeasureSpec.getSize(int spec)

//具体使用
int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
int measureWidthMode = MeasureSpec.getMode(widthMesureSpec);
int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
  • Mode of use:
  • The relationship between XML and layout modes:
    • wrap_content->MeasureSpec.AT_MOST
    • match_parent->MeasureSpec.EXACTLY
    • Specific values ​​-> MeasureSpec.EXACTLY
onLayout () function
  • onLayout in ViewGroup of () function is the default behavior
//派生自ViewGroup的类都必须去实现这个函数然后在内部按照规则在子布局中进行布局
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);

eg:

public class MyLinLayout extends ViewGroup {
    public MyLinLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取父类传过来的建议的宽度和高度值
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
        int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);


        int height = 0;
        int width = 0;
        int count = getChildCount();
        //通过测量所有的子控件来决定他所占位置的大小
        for(int i = 0;i<count;i++){
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();
            height += childHeight;
            width = Math.max(childWidth, width);
        }
        setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY)?measureWidth:width, (measureHeightMode== MeasureSpec.EXACTLY?measureHeight:height));


    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int top = 0;
        int count = getChildCount();
        for(int i = 0;i<count;i++){
            View child = getChildAt(i);
            int childHeight = child.getMeasuredHeight();
            int childWidth = child.getMeasuredWidth();
            child.layout(0, top, childWidth,top+childHeight);
            top+=childHeight;
        }
    }
}

() Distinction getMeasuredWidth () function and getWidth

  • getMeasuredWidth () function measure () can be obtained after the end of the process width value, and getWidth () function to be () after the end of the process in order to obtain the values ​​in the layout
  • The value of the function getMeasuredWidth () is performed by a set setMeasuredDimension () function; the value of the function getWidth () is set by a layout (left, top, right, bottom) Function

When container is laid

  • It layout onLayout () function in all his child controls, it also has its own natural parent control, one level up from the respective parent control to complete their layout, there is a ViewRoot at the top of all the controls, it is all the controls ancestor node

Method to get the child control margin value

  • Acquisition method and examples:
    • To customize the parameters given to layout_margin ViewGroup support subspace, the ViewGroup custom class must override generateLayoutParams () function, and returns an object of the derived class ViewGroup.MarginLayoutParams function
<?xml version="1.0" encoding="utf-8"?>
<com.example.adminstator.myviewdesign.fengzhuangkongjian.ViewHuiZhi.MyLinLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:context=".fengzhuangkongjian.ViewHuiZhi.ViewDrawActivity">


    <TextView
        android:text="第一个View"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="#ff0000"/>
    <TextView
        android:text="第二个View"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        android:background="#00ff00"/>
    <TextView
        android:text="第三个View"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="30dp"
        android:background="#0000ff"/>
</com.example.adminstator.myviewdesign.fengzhuangkongjian.ViewHuiZhi.MyLinLayout>
  • However, since there is no margin added to the calculated layout of OnLayout (). So we need to rewrite the following three functions:
//在container()初始化子空间时,会调用generateLayoutParams(LayoutParams p)来为子控件生成对应的布局属性,但是只是默认生成layout_width, layout_height所对应的布局参数,即在正常情况下调用generateLayoutParams()函数生成的,所以说正常情况下调用该函数生成的LayoutParams实例是不可以获取到margin的值的
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
    return new MarginLayoutParams(p);
}


//从指定的xml中获取对应的layout_width和layout_height值
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new MarginLayoutParams(getContext(), attrs);
}



//如果使用默认的构造函数,就生成layout_width=”wrap_content"和layout_height="wrap_content"对应的参数
@Override
protected LayoutParams generateDefaultLayoutParams() {
    return new MarginLayoutParams(LayoutParams.MATCH_PARENT,  LayoutParams.MATCH_PARENT);
}
  • After onMeasure () and OnLayout () function are the height and width of the sub-control increase the size of margin:
MarginLayoutParams marginLayoutParams = (MarginLayoutParams)child.getLayoutParams();
int childHeight = child.getMeasuredHeight()+marginLayoutParams.topMargin+marginLayoutParams.bottomMargin;
int childWidth = child.getMeasuredWidth()+marginLayoutParams.leftMargin+marginLayoutParams.rightMargin;
Achieve generateLayoutParams () and MarginLayoutParams () function
  1. How generateLayoutParams () function to get the value of the layout
//首先调用LayoutParams()函数产生布局信息
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LayoutParams(getContext(), attrs);
}


//其次LayoutParams()函数产生布局信息
public LayoutParams(Context c, AttributeSet attrs) {
    TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
    setBaseAttributes(a,
            R.styleable.ViewGroup_Layout_layout_width,
            R.styleable.ViewGroup_Layout_layout_height);
    a.recycle();
}


//最后调用setbaseAttributes()函数来获得对应的宽和高,通过TypeArray对自定义的xml进行解析的过程
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
    width = a.getLayoutDimension(widthAttr, "layout_width");
    height = a.getLayoutDimension(heightAttr, "layout_height");
}

MarginLayoutParams () function to achieve:
public MarginLayoutParams(Context c, AttributeSet attrs) {
    super();


    TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
    setBaseAttributes(a,
            R.styleable.ViewGroup_MarginLayout_layout_width,
            R.styleable.ViewGroup_MarginLayout_layout_height);


    int margin = a.getDimensionPixelSize(
            com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
     
     //提取layout_margin的值并进行设置
    if (margin >= 0) {
        leftMargin = margin;
        topMargin = margin;
        rightMargin= margin;
        bottomMargin = margin;
    } 
    //如果用户没有设置layout_margin,而是单个设置的,就一个个提取
    else {
        int horizontalMargin = a.getDimensionPixelSize(
                R.styleable.ViewGroup_MarginLayout_layout_marginHorizontal, -1);
        int verticalMargin = a.getDimensionPixelSize(
                R.styleable.ViewGroup_MarginLayout_layout_marginVertical, -1);


        if (horizontalMargin >= 0) {
            leftMargin = horizontalMargin;
            rightMargin = horizontalMargin;
        } else {
            leftMargin = a.getDimensionPixelSize(
                    R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
                    UNDEFINED_MARGIN);
            if (leftMargin == UNDEFINED_MARGIN) {
                mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
                leftMargin = DEFAULT_MARGIN_RESOLVED;
            }
            rightMargin = a.getDimensionPixelSize(
                    R.styleable.ViewGroup_MarginLayout_layout_marginRight,
                    UNDEFINED_MARGIN);
            if (rightMargin == UNDEFINED_MARGIN) {
                mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
                rightMargin = DEFAULT_MARGIN_RESOLVED;
            }
        }


        startMargin = a.getDimensionPixelSize(
                R.styleable.ViewGroup_MarginLayout_layout_marginStart,
                DEFAULT_MARGIN_RELATIVE);
        endMargin = a.getDimensionPixelSize(
                R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
                DEFAULT_MARGIN_RELATIVE);


        if (verticalMargin >= 0) {
            topMargin = verticalMargin;
            bottomMargin = verticalMargin;
        } else {
            topMargin = a.getDimensionPixelSize(
                    R.styleable.ViewGroup_MarginLayout_layout_marginTop,
                    DEFAULT_MARGIN_RESOLVED);
            bottomMargin = a.getDimensionPixelSize(
                    R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
                    DEFAULT_MARGIN_RESOLVED);
        }


        if (isMarginRelative()) {
           mMarginFlags |= NEED_RESOLUTION_MASK;
        }
    }


    final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
    final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
    if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
        mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
    }


    // Layout direction is LTR by default
    mMarginFlags |= LAYOUT_DIRECTION_LTR;


    a.recycle();
}

  • So the reason to rewrite the function is the default function only can be extracted layout_width and layout_height parameters, only MarginLayoutParams () function to extract margin

Guess you like

Origin blog.csdn.net/qq_39424143/article/details/95079837