In this post, I will share some new thoughts regarding LayoutInflater
What LayoutInflater is used for.
We all know that when developing Android applications, the layout is written through XML files.
Then how is the XML layout file converted into a View object in Android and displayed in the application? This is what LayoutInflater
is used for
LayoutInflater
is designed to convert XML layouts files into View objects in Android.
Maybe in the daily work you only used setContentView()
, and haven’t used LayoutInflater
too much,
this is because the Android SDK has done good encapsulation for us at the upper layer, which makes the development work easier.
If you check the source code of the setContentView() method, you will find that the bottom layer is still use LayoutInflater
:
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
Code language: JavaScript (javascript)
So how does LayoutInflater convert an XML layout into a View object?
2 steps are involved here:
1. The content in the XML file is parsed by the parser.
Code snippet for parsing the content of the xml file:
public View inflate(@LayoutRes int resource,
@Nullable ViewGroup root,
boolean attachToRoot) {
...
XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
As you can see, an XmlResourceParser object is used here for parsing the XML file. Since the specific parsing rules are too complicated, we will not follow up.
Code language: PHP (php)
2 . Use reflection to create the parsed element into a View object.
Code snippet to create View object using reflection:
public final View createView(@NonNull Context viewContext, @NonNull String name,
@Nullable String prefix, @Nullable AttributeSet attrs)
throws ClassNotFoundException, InflateException {
...
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
mContext.getClassLoader()).asSubclass(View.class);
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
}
...
try {
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
return view;
}
...
}
Code language: JavaScript (javascript)
Parameter explanation
Then the most common usage of LayoutInflater is as follows:
View view = LayoutInflater.from(context).inflate(resourceId, parent, false);
Code language: PHP (php)
The meaning of this code is to first call the from() method of LayoutInflater to obtain an instance of LayoutInflater, and then call its inflate() method to parse and load a layout, thereby converting it into a View object and returning it.
However, I think this code is extremely unfriendly to newbies, and even to many veterans.
Let’s take a look at the parameter definition of the inflate() method:
public View inflate(int resource,
@Nullable ViewGroup root,
boolean attachToRoot) {
...
}
Code language: CSS (css)
The inflate()
method receives 3 parameters. The first parameter resource
is relatively easy to understand, that is, the resource id of the XML file we want to parse and load. What does the second parameter root
, and the third parameter attachToRoot
mean?
Parameter root
We know that Android’s layout structure is a tree structure. Each layout can contain several sub-layouts, and each sub-layout can then contain sub-layouts, to construct a View of any style and present it to the user.
Therefore, each layout must have a parent layout.
This is also the role of the second parameter root
of the inflate()
method, which is to specify a parent layout for the XML layout currently to be parsed and loaded.
So can a layout have no parent layout? yes, which is why the root parameter is marked as @Nullable
But if we inflate a layout without a parent layout, how do we display it? Naturally, there is no way to display it, so it can only be added to an existing layout with addView
later. Or the layout you inflate is a top-level layout, so it doesn’t need to have a parent layout. But these scenarios are relatively rare, so in most cases, we need to specify the parent layout when using the inflate()
method of LayoutInflater
.
In addition, if the parent layout is not specified for the inflate layout, there will be another problem. Let’s explain it with an example.
Here we define a button_layout.xml layout file, the code is as follows:
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
Code language: HTML, XML (xml)
This layout file is very simple, there is only one button in it.
Next, we use LayoutInflater to load this layout file and add it to an existing layout:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout mainLayout = (LinearLayout) findViewById(R.id.main_layout);
View buttonLayout = LayoutInflater.from(this).inflate(R.layout.button_layout, null);
mainLayout.addView(buttonLayout);
}
}
Code language: JavaScript (javascript)
As you can see, here we did not specify the parent layout for button_layout
, but passed in a null
parameter. When the second parameter is passed in null
, the third parameter is meaningless, so it can be left unspecified.
But as mentioned earlier, a layout cannot be displayed without a parent layout, so we use the addView()
method to add it to an existing layout.
The code is so simple, now we can run the program, the effect is as shown below:

It seems that there is no problem, the button can be displayed normally, indicating that the layout button_layout.xml
has been successfully loaded and added to the existing layout.
But if you try to adjust the size of the button, you will find that no matter how you adjust it, the size of the button will not change:
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="100dp"
android:text="Button" />
Code language: HTML, XML (xml)
Here we specify the width and height of the button as 300dp, and the height as 100dp, but the button remains unchanged.
Why?
Because these two values have completely lost their effect now. usually, we often use layout_width
and layout_height
to set the size of the View, and it always works as if these two properties are used to set the size of the View.
In fact, it is not. they are actually used to set the size of the View in the layout. That is to say, the View must exist in a layout first. That’s why these two properties are called layout_width
and layout_height
, not width
and height
.
And because we did not specify the parent layout for the button_layout.xml
when we use LayoutInflater
to load the layout, the layout_width
and layout_height
properties here are useless.
To be more precise, all properties starting with layout_
will have no effect.
Now we modify the code as follows:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout mainLayout = (LinearLayout) findViewById(R.id.main_layout);
View buttonLayout = LayoutInflater.from(this).inflate(R.layout.button_layout, mainLayout, false);
mainLayout.addView(buttonLayout);
}
}
Code language: JavaScript (javascript)
As you can see, the second parameter of the inflate()
method is specified as mainLayout
. That is, we specify a parent layout for the button_layout.xml
layout. In this case, the layout_width
and layout_height
properties can take effect:

So far, we have explained the role of the second parameter root
of the inflate()
method very clearly.
What does the third parameter attachToRoot mean?
Note that observing the above code, while we specified the second parameter as mainLayout
, we specified the third parameter as false
. If you try to specify the third parameter as true
, the program will simply crash.
This crash message is saying that we are adding a child View, but this child View already has a parent layout, and the parent layout needs to call removeView() to remove the child View before adding it
Why do I get such an error after modifying the third parameter? Let’s analyze it now.
First pay attention to what the name of the third parameter is, attachToRoot
. Literally, it’s asking if we want to add to root. So what is root?
public View inflate(int resource, @Nullable ViewGroup root, boolean attachToRoot) {
...
}
Code language: CSS (css)
As you can see in the above code, the root
is the second parameter. If you pass in true
, it means that it will be attached to the root
, and passing in false
means that it will not attach to the root
.
So in the code above, we initially passed false
in the third parameter of the inflate()
method, then the button_layout.xml
layout will not be added to the mainLayout
, we can manually call the addView()
method later to add it to mainLayout
.
And if the third parameter is changed to true
, it means that the button_layout.xml
layout has been automatically added to the mainLayout
. At this time, call the addView()
method again and find that the button_layout.xml
already has a parent layout, so naturally Throws the above exception.
Now we can go back and look at the code written in the past. For example, everyone must have used Fragment
. When loading a layout in Fragment
, we usually write:
public class MyFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_layout, container, false);
}
}
Code language: PHP (php)
So why the last parameter of the inflate()
method must be passed in false
?
Check the relevant source code of Fragment
, you will find that it will add the View we returned in the onCreateView()
method to a Container:
void addViewToContainer() {
// Ensure that our new Fragment is placed in the right index
// based on its relative position to Fragments already in the
// same container
int index = mFragmentStore.findFragmentIndexInContainer(mFragment);
mFragment.mContainer.addView(mFragment.mView, index);
}
Code language: JavaScript (javascript)
This situation is very similar to our previous example, that is to say, the subsequent Fragment will have an addView
operation. If we pass the third parameter of the inflate()
method to true
, then the layout from the inflate will be added directly into the parent layout. In this way, when you call addView
again later, you will find that it already has a parent layout, thus throwing the same crash message as above.
In addition to Fragment, the usage of LayoutInflater
in RecyclerView
is also based on the same reason