1. The introduction

In Android, when we need to dynamically add a View, we usually load the layout first. There are two ways to load the layout:

// The first way
View.inflate(this,R.layout.xx,null);
// The second way
LayoutInflater.from(this).inflate(R.layout.xx,null.false);
Copy the code

Generally experienced people will tell you that the second way is reliable, so if you choose to use the first way, it is not impossible, if not good, there will be some strange things:

2. The strange one

Now I have a layout of the Activity class, which only has a LInearLayout. I need to dynamically add a TextView to the LInearLayout. I set the height of the TextView to 100DP and the background color to red. Look directly at the following code:

//activity_activity_inflate<? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/ll_container"
    android:orientation="vertical"/ > ___________________________________________________________//item_child<? xml version="1.0" encoding="utf-8"? > <TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="#ff5232"
    android:text="Hello world"
    android:textColor="#ffffff"
    android:layout_width="match_parent"
    android:id="@+id/tv"
    android:layout_height="100dp" />

Copy the code

Dynamically add TextView

LinearLayout ll_container = findViewById(R.id.ll_container);
View child = View.inflate(this,R.layout.item_child,null);
ll_container.addView(child);

Copy the code

Operation result diagram:

👉 we set the height of the TextView to 100dp, how can we display wrap_content ❔

3. Strange

Let’s modify our dynamically added code:

LinearLayout ll_container = findViewById(R.id.ll_container);
View child = View.inflate(this,R.layout.item_child,ll_container);
ll_container.addView(child);

Copy the code

The third argument changes null to ll_container and runs again:

oh.. Mygod, the program directly crashed, error log message is as follows:

The specified child already has a parent. You must call removeView(a) on the child's parent first.

Copy the code

Of course, in our use of RecyclerView adapter, also want to load the layout, improper use, will also appear to write the effect, that is what is going on?

This assumes that you have some understanding of the layout loading process, and that no matter which layout loading method is used, it will eventually be called here:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    finalResources res = getContext().getResources(); .final XmlResourceParser parser = res.getLayout(resource);
    try {
        return inflate(parser, root, attachToRoot);
    } finally{ parser.close(); }}Copy the code

LayoutInflater#inflate method, with three parameters. The inflate method is the one we are focusing on if we follow up, and I only leave the important code behind:

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

        try {
            // Look for the root node.
            int type;
            while((type = parser.next()) ! = XmlPullParser.START_TAG && type ! = XmlPullParser.END_DOCUMENT) {// Empty
            }

            if(type ! = XmlPullParser.START_TAG) {throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }

            final String name = parser.getName();

            if (DEBUG) {
                System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * *");
                System.out.println("Creating root view: "
                        + name);
                System.out.println("* * * * * * * * * * * * * * * * * * * * * * * * * *"); }...// Temp is the root view that was found in the xml
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                ViewGroup.LayoutParams params = null;

                if(root ! =null) {/ / -- -- -- -- -- -- -- -- -- 2
                    if (DEBUG) {
                        System.out.println("Creating params from root: " +
                                root);
                    }
                    // Create layout params that match root, if supplied
                    params = root.generateLayoutParams(attrs);
                    if(! attachToRoot) {// Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        temp.setLayoutParams(params);
                    }
                }
                rInflateChildren(parser, temp, attrs, true);
                
                if(root ! =null && attachToRoot) {/ / -- -- -- -- -- -- -- -- -- 3
                    root.addView(temp, params);
                }

                if (root == null| |! attachToRoot) {/ / -- -- -- -- -- -- -- -- -- 4result = temp; }}return result;
    }

Copy the code

Note 1: Assign root to result, which is returned;

Note 2: Root! =null, the value of the attribute we set in the XML is converted to: params, and attachToRoot, if false, is entered into the if condition, which sets LayoutParams for the temp we parsed from the XML.

Final View temp = createViewFromTag(root, name, inflaterContext, attrs); This is just a View, if the TextView written in the XML is interpreted as a Temp TextView, with no width, height, margins, etc.

Note 3: Root! =null && attachToRoot(true), the View parsed from the XML will be automatically added to root with LayoutParams.

Note 4: the root = = null | |! AttachToRoot, change result to temp, XML parsed. Note that there are no LayoutParams, which means that the attribute values we set in the XML will be invalid.

👉 To review the problems we encountered earlier. We passed:

LinearLayout ll_container = findViewById(R.id.ll_container);
View child = View.inflate(this,R.layout.item_child,null);
ll_container.addView(child);

Copy the code

How does addView. Correspond to the source code? Follow through the layers of calls to here:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    returninflate(resource, root, root ! =null);
}
Copy the code

The third argument we pass is null, which means:

return inflate(resource, null.false);
Copy the code

Remap the three parameters of the inflate:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
Copy the code

Root == null,attachToRoot = false. Another way to write it is:

View.inflate(this,R.layout.item_child,null); This is equivalent to setting root == NULL,attachToRoot = false

According to our previous analysis, only root! =null, params will be assigned.

if(root ! =null) { params = root.generateLayoutParams(attrs); . }Copy the code

See, the next step is to enter the code in comment 4:

if (root == null| |! attachToRoot) {/ / -- -- -- -- -- -- -- -- -- 4
     result = temp;
}

Copy the code

Temp is the View parsed from the XML (without any params).

👉 Second question, why do we write so crash report error, take the previous take a look:

LinearLayout ll_container = findViewById(R.id.ll_container);
View child = View.inflate(this,R.layout.item_child,ll_container);
ll_container.addView(child);
Copy the code

Unlike the first time, I pass ll_container, what does that mean? Means our root! =null && attachToRoot is true. So here we are in our code at comment 3:

 if(root ! =null && attachToRoot) {/ / -- -- -- -- -- -- -- -- -- 3
       root.addView(temp, params);
 }
Copy the code

Root. AddView (temp, params); This is equivalent to ll_container.addview (temp,params); Return the root.

We need to pay attention to:

View child = View.inflate(this,R.layout.item_child,ll_container);
Copy the code

The sentence itself doesn’t crash the program, it does:

ll_container.addView(child);
Copy the code

According to the previous analysis, the child has been added to the ll_container. If you add a child to another container, the system does not allow it. Each child can have only one paraent. So when you execute that code, the system says:

See, so we want to add the child to the ll_container, just by:

View.inflate(this,R.layout.item_child,ll_container);
Copy the code

This line is enough, the following line is gild the lily. Well, that’s the end of today’s sharing. If you have any questions, see you in the comments section