Inherit ViewGroup to learn onMeasure and onLayout

When inheriting the ViewGroup class, you need to override two methods, onMeasure and onLayout.

1, call setMeasuredDimension method void android. in method onMeasure. view. View. setMeasuredDimension(int measuredWidth, int measuredHeight)
In onMeasure(int, int), you must call setMeasuredDimension(int width, int height) to store the measured width and height values. If you do not do so, the exception IllegalStateException will be triggered.
2, call the child's measure method in method onMeasure.

void android.view.View.measure(int widthMeasureSpec, int heightMeasureSpec)

This method is used to measure the size of the view. The parent view uses the width and height parameters to provide constraint information. In fact, the measurement of view is completed in the onMeasure(int, int) method. Therefore, only onMeasure(int, int) methods can and must be overridden. The parameter widthMeasureSpec provides the specification of the horizontal space of the view, and the parameter hightmeasurespec provides the specification of the vertical space of the view.

3. Parse onMeasure(int, int) method

void android.view.View.onMeasure(int widthMeasureSpec, int heightMeasureSpec)

Measure the view and its contents to determine the width and height of the view. This method is called in measure(int, int) and must be rewritten to accurately and effectively measure the content of the view.

When overriding this method, you must call setMeasuredDimension(int, int) to store the measured width and height values. Execution failure will trigger an IllegalStateException. It is legal and valid to call onMeasure(int, int) of the parent view.

The basic measurement data of view takes its background size by default, unless a larger size is allowed. The subview must override onMeasure(int, int) to provide a more accurate measurement of its content. If overridden, the subclass ensures that the measured height and width are at least the minimum height and width of the view (obtained through getSuggestedMinimumHeight() and getSuggestedMinimumWidth()).

4. Parse onLayout(boolean, int, int, int, int) method

void android.view.ViewGroup.onLayout(boolean changed, int l, int t, int r, int b)

Call scene: called when the view sets the size and position for its children. Child views, including children, must override the onLayout(boolean, int, int, int, int) method and call their respective layout(int, int, int, int) methods.

Parameter Description: parameter changed indicates that the view has a new size or position; Parameter l represents the Left position relative to the parent view; The parameter t represents the Top position relative to the parent view; The parameter r represents the Right position relative to the parent view; Parameter b represents the Bottom position relative to the parent view

5. Parse view Measurespec class
android.view.View.MeasureSpec
The MeasureSpec object encapsulates the layout specification and is passed from the parent view to the child view. Each MeasureSpec object represents the specification of width or height.
The MeasureSpec object contains a size and a mode, where the mode can take one of the following three values:
UNSPECIFIED, 1073741824 [0x40000000], if there is no provision, it means that there is no provision added to the child view.
EXACTLY, 0 [0x0], which means that the parent view determines the exact size for the child view.
AT_MOST, - 2147483648 [0x80000000], the sub view can be as large as possible within the specified size.

Here is an example demo:
Step 1: customize a View to implement the ViewGroup interface, that is, customize the ViewGroup:

import android.content.Context;  
import android.util.AttributeSet;  
import android.view.View;  
import android.view.ViewGroup;  
  
public class MyViewGroup extends ViewGroup {  
  
    public MyViewGroup(Context context) {  
        super(context);  
    }  
  
    public MyViewGroup(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
  
    public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
    }  
  
    /** 
     * Calculate the size of the control 
     */  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        int measureWidth = measureWidth(widthMeasureSpec);  
        int measureHeight = measureHeight(heightMeasureSpec);  
        // Calculates the size of all child controls in a custom ViewGroup  
        measureChildren(widthMeasureSpec, heightMeasureSpec);  
        // Sets the size of the custom control MyViewGroup  
        setMeasuredDimension(measureWidth, measureHeight);  
    }  
  
    private int measureWidth(int pWidthMeasureSpec) {  
        int result = 0;  
        int widthMode = MeasureSpec.getMode(pWidthMeasureSpec);// Get mode  
        int widthSize = MeasureSpec.getSize(pWidthMeasureSpec);// Get size  
  
        switch (widthMode) {  
        /** 
         * mode There are three cases, and the values are measurespec UNSPECIFIED, MeasureSpec. EXACTLY, 
         * MeasureSpec.AT_MOST.  
         *  
         *  
         * MeasureSpec.EXACTLY Is the exact size, 
         * When we put the layout of the control_ Width or layout_ When height is specified as a specific value, such as andorid 
         * :layout_width="50dip",Or FILL_PARENT is the exact size when the control size has been determined. 
         *  
         *  
         * MeasureSpec.AT_MOST Is the maximum size, 
         * When the layout of the control_ Width or layout_height is specified as WRAP_CONTENT 
         * ,The control size generally changes with the control's subspace or content. At this time, the control size can be as long as it does not exceed the maximum size allowed by the parent control 
         * . Therefore, the mode at this time is AT_MOST, size gives the maximum size allowed for the parent control. 
         *  
         *  
         * MeasureSpec.UNSPECIFIED There are few cases where the size is not specified. Generally, the parent control is AdapterView, 
         * The pattern passed in through the measure method. 
         */  
        case MeasureSpec.AT_MOST:  
        case MeasureSpec.EXACTLY:  
            result = widthSize;  
            break;  
        }  
        return result;  
    }  
  
    private int measureHeight(int pHeightMeasureSpec) {  
        int result = 0;  
  
        int heightMode = MeasureSpec.getMode(pHeightMeasureSpec);  
        int heightSize = MeasureSpec.getSize(pHeightMeasureSpec);  
  
        switch (heightMode) {  
        case MeasureSpec.AT_MOST:  
        case MeasureSpec.EXACTLY:  
            result = heightSize;  
            break;  
        }  
        return result;  
    }  
  
    /** 
     * The purpose of overriding onLayout is to specify the display position of the view. The sequence of method execution is after onMeasure, because the view must only know the size, 
     * To determine how to place it 
     */  
    @Override  
    protected void onLayout(boolean changed, int l, int t, int r, int b) {  
        // Record the total height  
        int mTotalHeight = 0;  
        // Traverse all child views  
        int childCount = getChildCount();  
        for (int i = 0; i < childCount; i++) {  
            View childView = getChildAt(i);  
  
            // Gets the view size calculated in onMeasure  
            int measureHeight = childView.getMeasuredHeight();  
            int measuredWidth = childView.getMeasuredWidth();  
  
            childView.layout(l, mTotalHeight, measuredWidth, mTotalHeight  
                    + measureHeight);  
  
            mTotalHeight += measureHeight;  
  
        }  
    }  
  
}

Step 2: layout file:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:background="#00f0f0"  
    tools:context=".MainActivity" >  
  
    <net.loonggg.viewgroup.MyViewGroup  
        android:id="@+id/myViewGroup"  
        android:layout_width="480dp"  
        android:layout_height="300dp"  
        android:background="#0f0f0f" >  
  
        <TextView  
            android:layout_width="200dp"  
            android:layout_height="100dp"  
            android:background="#000000"  
            android:gravity="center"  
            android:text="first TextView" />  
  
        <TextView  
            android:layout_width="100dp"  
            android:layout_height="200dp"  
            android:background="#ffffff"  
            android:gravity="center"  
            android:text="the second TextView" />  
    </net.loonggg.viewgroup.MyViewGroup>  
  
</RelativeLayout>

Related video
layout process of ViewGroup

Keywords: Android view layout

Added by klik on Wed, 15 Dec 2021 12:23:46 +0200