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