SoFunction
Updated on 2025-03-11

Android rewrite ViewGroup analyzes onMeasure and onLayout methods

Android rewrite ViewGroup to analyze onMeasure() and onLayout() methods

When inheriting the ViewGroup class, two methods need to be rewrite, namely onMeasure and onLayout.

1. Call the setMeasuredDimension method in the method onMeasure

void (int measuredWidth, int measuredHeight)

In onMeasure(int, int), setMeasuredDimension(int width, int height) must be called to store the measured width and height values. If this is not done, an exception IllegalStateException will be triggered.

2. Call the child's measure method in the method onMeasure

void (int widthMeasureSpec, int heightMeasureSpec)

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

3. Parsing onMeasure(int, int) method

void (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, setMeasuredDimension(int, int) must be called to store the measured width and height values. An IllegalStateException exception is triggered by a failure of execution. Calling the parent view's onMeasure(int, int) is a legal and valid usage.

The basic measurement data of the view takes its background size by default unless larger sizes are allowed. The subview must be rewrite onMeasure(int, int) to provide more accurate measurement values ​​for its content. If rewritten, the subclass ensures that the measured height and width are at least the minimum height and width of the view (gets obtained by getSuggestedMinimumHeight() and getSuggestedMinimumWidth()).

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

void (boolean changed, int l, int t, int r, int b)

Calling scenario: Called when the view sets the size and position for its child. The child view, including the child, must rewrite the onLayout(boolean, int, int, int, int) method and call the respective layout(int, int, int, int) method.

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

5. Analytical Class


MeasureSpec object, encapsulates layout specifications and is passed from parent view to 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 no provision is added, it means that no provision is added to the subview.
  • EXACTLY, 0 [0x0], accurate, means that the parent view determines the exact size for the child view.
  • AT_MOST, -2147483648 [0x80000000], the subview 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:

package ; 
 
import ; 
import ; 
import ; 
import ; 
 
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) { 
    (widthMeasureSpec, heightMeasureSpec); 
    int measureWidth = measureWidth(widthMeasureSpec); 
    int measureHeight = measureHeight(heightMeasureSpec); 
    // Calculate the size of all child controls in a custom ViewGroup    measureChildren(widthMeasureSpec, heightMeasureSpec); 
    // Set the size of the custom control MyViewGroup    setMeasuredDimension(measureWidth, measureHeight); 
  } 
 
  private int measureWidth(int pWidthMeasureSpec) { 
    int result = 0; 
    int widthMode = (pWidthMeasureSpec);// Get the pattern    int widthSize = (pWidthMeasureSpec);// Get the size 
    switch (widthMode) { 
    /**
      * There are three cases of mode, the values ​​are, ,
      * MeasureSpec.AT_MOST. 
      *
      *
      * is the exact size,
      * When we specify the control layout_width or layout_height as a specific value, such as andorid
      * :layout_width="50dip", or FILL_PARENT, are both cases where the control size has been determined, and are all accurate sizes. 
      *
      *
      * MeasureSpec.AT_MOST is the maximum size,
      * When the control's layout_width or layout_height is specified as WRAP_CONTENT
      * , the control size generally changes with the control's child space or content. At this time, the control size should not exceed the maximum size allowed by the parent control.
      * .  Therefore, the mode at this time is AT_MOST, and size gives the maximum size allowed by the parent control. 
      *
      *
      * is the size not specified, there are not many cases, generally the parent control is AdapterView.
      * The mode passed in through the measure method. 
      */ 
    case MeasureSpec.AT_MOST: 
    case : 
      result = widthSize; 
      break; 
    } 
    return result; 
  } 
 
  private int measureHeight(int pHeightMeasureSpec) { 
    int result = 0; 
 
    int heightMode = (pHeightMeasureSpec); 
    int heightSize = (pHeightMeasureSpec); 
 
    switch (heightMode) { 
    case MeasureSpec.AT_MOST: 
    case : 
      result = heightSize; 
      break; 
    } 
    return result; 
  } 
 
  /**
    * Overriding onLayout is to specify the display position of the view. The order of method execution is after onMeasure, because the view must only know the size.
    * Only then can you 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 subviews    int childCount = getChildCount(); 
    for (int i = 0; i < childCount; i++) { 
      View childView = getChildAt(i); 
 
      // Get the view size calculated in onMeasure      int measureHeight = (); 
      int measuredWidth = (); 
 
      (l, mTotalHeight, measuredWidth, mTotalHeight 
          + measureHeight); 
 
      mTotalHeight += measureHeight; 
 
    } 
  } 
 
} 

The second step is to layout the file:

<RelativeLayout xmlns:andro 
  xmlns:tools="/tools" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="#00f0f0" 
  tools:context=".MainActivity" > 
 
  < 
    android: 
    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="Second TextView" /> 
  </> 
 
</RelativeLayout> 

Step 3:

package ; 
 
import ; 
import ; 
 
public class MainActivity extends Activity { 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    (savedInstanceState); 
    setContentView(.activity_main); 
  } 
 
} 

Thank you for reading, I hope it can help you. Thank you for your support for this site!