When is the View’s default LayoutParams generated and what is the default value

View# mLayoutParams attributes:

/**
 * The layout parameters associated with this view and used by the parent
 * {@link android.view.ViewGroup} to determine how this view should be
 * laid out.
 * {@hide}
 */
protected ViewGroup.LayoutParams mLayoutParams;
Copy the code

The only thing that can be modified is the View#setLayoutParams(viewgroup.layoutparams params) method.

If we don’t manually set the viewGroup.layoutParams property to the View, does it have a default value? The answer is yes.

Two ways to add a View

There are two ways to add a View. One is to add it to XML, and we get the View via View#findViewById(). The other is through a series of overloaded methods on ViewGroup#addView().

XML add

One way to add XML code is to write it directly to the activity’s XML layout file, setting the layout file with the Activity#setContentView() method. The other is to parse an XML file into a View through the LayoutInflater#inflate method, which is what we use when setting layout files for the Fragment or custom views.

The common View.inflate(Context Context, int resource, ViewGroup root) method is Internally, the LayoutInflater#inflate(int Resource, ViewGroup root, Boolean attachToRoot) method is also called.

LayoutInflater# inflate method

Let’s look at the LayoutInflater#inflate method first:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    final Resources res = getContext().getResources();
    if (DEBUG) {
        Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                + Integer.toHexString(resource) + ")");
    }

    final XmlResourceParser parser = res.getLayout(resource);
    try {
        returninflate(parser, root, attachToRoot); } finally { parser.close(); }}Copy the code

The first step is to generate the XmlResourceParser object from the layout file. The second step is to convert the Parser object into a View object using the inflate(XmlPullParser Parser Parser, @Nullable ViewGroup root, Boolean attachToRoot) method.

Next look at the inflate(XmlPullParser parser parser, @nullable ViewGroup root, Boolean attachToRoot) method and remove unnecessary code for simplicity:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        
    View result = root;

    // Look for the root node.
    int type; // Find the root nodewhile ((type= parser.next()) ! = XmlPullParser.START_TAG &&type! = XmlPullParser.END_DOCUMENT) { // Empty } final String name = parser.getName();if (TAG_MERGE.equals(name)) {
        rInflate(parser, root, inflaterContext, attrs, false);
    } else{ // 1 final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; / / 2, 3if(root ! = null) { // Create layout params that match root,if supplied
            params = root.generateLayoutParams(attrs);
            if(! attachToRoot) { temp.setLayoutParams(params); } } // Inflate all children under temp against its context. // 4 rInflateChildren(parser, temp, attrs,true);

        if(root ! = null && attachToRoot) { root.addView(temp, params); }if(root == null || ! attachToRoot) { result = temp; }} / / 5return result;
}
Copy the code

The method is straightforward, passing in the XmlPullParser parameter and the ViewGroup object root(which can be null) and returning a created View. Our task is to find where to set LayoutParams for the newly created View.

We only look at the logic we care about:

Create a root View object temp from createViewFromTag (); Through root. GenerateLayoutParams (attrs) method will be temp width and height attributes into LayoutParams set temp. If root is null, the parent layout of Temp is not determined, and there is no need to set LayoutParams. LayoutParams will be set when it is added to other layouts. 4, rInflateChildren method, add temp child View 5, return the root View (temp must be included in the root View)

Next we look at the rInflateChildren method that adds children to the View, which will eventually call the rInflate method and, as usual, remove the irrelevant code and look only at the ones you care about:

void rInflate(XmlPullParser parser, View parent, Context context,
        AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

    final int depth = parser.getDepth();
    int type;
    boolean pendingRequestFocus = false;

    while (((type= parser.next()) ! = XmlPullParser.END_TAG || parser.getDepth() > depth) &&type! = XmlPullParser.END_DOCUMENT) { final String name = parser.getName();if (TAG_REQUEST_FOCUS.equals(name)) {
            ...
        } else {
            final View view = createViewFromTag(parent, name, context, attrs);
            final ViewGroup viewGroup = (ViewGroup) parent;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
            rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params); }}if(finishInflate) { parent.onFinishInflate(); }}Copy the code

1. Start the while loop and call createViewFromTag to generate the View based on the acquired property. The createViewFromTag method generates a View object by calling a two-parameter constructor (Context Context, @Nullable AttributeSet attrs) via reflection.

ViewGroup#generateLayoutParams to obtain the attrs width and height of the sub-view, that is, we set the android:layout_width and android:layout_height attributes for the View in the layout. Generate the corresponding LayoutParams parameter based on the width and height, and then add the view to the corresponding parent, setting the LayoutParams parameter to the generated view object (explained later).

3. Before adding a View, the rInflateChildren method is recursively called to complete the addition of children of the current View.

It should be noted that the creation is done in depth-first traversal mode.

Let’s focus on how the ViewGroup#generateLayoutParams method generates the width and height of the child View into the LayoutParams parameter.

public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LayoutParams(getContext(), attrs);
}
Copy the code

It calls the constructor of ViewGroup’s inner class LayoutParams, so let’s see:

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();
}
Copy the code

ViewGroup_Layout attributes are retrieved using Context and attrs, and the setBaseAttributes method reads the layout_width and layout_height attributes in the resource file. We then set the width and height properties for LayoutParams. Details are as follows:

/**
 * Extracts the layout parameters from the supplied attributes.
 *
 * @param a the style attributes to extract the parameters from
 * @param widthAttr the identifier of the width attribute
 * @param heightAttr the identifier of the height attribute
 */
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
    width = a.getLayoutDimension(widthAttr, "layout_width");
    height = a.getLayoutDimension(heightAttr, "layout_height");
}
Copy the code

The setBaseAttributes method assigns the layout_width and layout_height attributes of the layout file to the width and height attributes of LayoutParams respectively, thus completing the construction of the corresponding LayoutParams of the child View.

Each child View is created with the LayoutParams attribute, which comes from the width and height attributes of the child View.

Activity# the setContentView method

Now let’s look at how the XML set by the Activity#setContentView method is converted into a View object. How the LayoutParams attribute was added during the transformation.

Activity#setContentView

public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
Copy the code

PhoneWindow#setContentView(int layoutResID)

public void setContentView(int layoutResID) {
    ...
    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext());
        transitionTo(newScene);
    } else{ mLayoutInflater.inflate(layoutResID, mContentParent); }... }Copy the code

You can see that the LayoutInflater#inflate method is eventually called to parse the XML into a View and add it to the mContentParent. A concrete implementation of LayoutInflater#inflate can be seen in the analysis above.

ViewGroup#addView()

Let’s take a look at a few overloaded methods of ViewGroup#addView:

addView(View child)
addView(View child, int index)
addView(View child, int width, int height)
addView(View child, LayoutParams params)
addView(View child, int index, LayoutParams params)
Copy the code

There are two types of LayoutParams and LayoutParams.

If the method contains LayoutParams, set LayoutParams to the view; The addView(View Child, int index) method is used to generate a default LayoutParams.

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);
}
Copy the code

It can be seen that if the view is not set LayoutParams, through generateDefaultLayoutParams () method to generate a, we see what the default generated LayoutParams:

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

As you can see, the default LayoutParams gives the width and height wrap_content.

conclusion

Based on the above analysis, we can draw the following conclusions: 1. View objects generated by XML layout file will add the LayoutParams attribute by default, and its attribute values are mainly derived from the width and height attributes of the child layout. ViewGroup#addView() adds a LayoutParams attribute to a View that does not have a LayoutParams attribute. The default value of LayoutParams attribute is wrap_content.