Understanding Android LayoutInflater: Usage, inflate Methods, Factory and Factory2
This article explains how Android's LayoutInflater converts XML layout files into View objects, demonstrates the various ways to obtain and use a LayoutInflater, details the overloads of the inflate method, and shows how to customize view creation with Factory and Factory2 interfaces, including practical code examples.
1. Introduction
Following the previous article "Android: A Cup of Iced Americano – Finding setContentView", this piece continues the discussion of LayoutInflater , which is responsible for turning XML layout files into View objects at runtime.
In Android applications, UI is described by XML layout files that define the structure and appearance of widgets. To use these layouts in code, the system must inflate them into actual View instances, and that is exactly what LayoutInflater does.
If you have never used LayoutInflater , you might want to skip the details for now.
2. Usage
Common ways to obtain a LayoutInflater instance:
Obtain via system service LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(resource, root, attachToRoot);
Obtain via Activity's getLayoutInflater() View view = getLayoutInflater().inflate(resource, root, attachToRoot);
Obtain via View's static inflate() method View view = View.inflate(resource, root, attachToRoot);
Obtain via LayoutInflater.from() View view = LayoutInflater.from(this).inflate(resource, root, attachToRoot);
3. inflate
All the usages above eventually call static methods inside LayoutInflater . For example:
public static LayoutInflater from(@UiContext Context context) {
LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}The core inflate method has three overloads:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
return inflate(parser, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
// ... implementation that parses XML and creates the view hierarchy ...
}The final overload ultimately delegates to:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
// ... parsing logic ...
if (TAG_MERGE.equals(name)) {
// handle
tag when root is non‑null and attachToRoot is true
} else {
View temp = createViewFromTag(root, name, inflaterContext, attrs);
// ... attach to root based on attachToRoot flag ...
}
return result;
}
}Key parameters:
@Nullable ViewGroup root : The parent container for the top‑level view. If null , the inflated view has no parent.
boolean attachToRoot : Determines whether the inflated view is immediately added to root . When false , the view is returned for later attachment.
4. Factory2 and Factory
The inflate process eventually calls createViewFromTag , which in turn uses tryCreateView to give developers a hook for custom view creation.
public final View tryCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
return view;
}Factory allows a simple callback to replace or augment the default view creation:
LayoutInflater inflater = LayoutInflater.from(context);
inflater.setFactory(new LayoutInflater.Factory() {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
if (name.equals("HarmonyOSGreateAgain")) {
return new HarmonyOSView(context, attrs);
}
return null; // fall back to default
}
});Factory2 extends Factory by adding a parent parameter, giving more control over the view hierarchy:
private Factory2 mFactory2;
public interface Factory2 extends Factory {
View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs);
}Both factories can be set only once via setFactory or setFactory2 . Attempting to set them again throws an IllegalStateException :
public void setFactory(Factory factory) {
if (mFactorySet) {
throw new IllegalStateException("A factory has already been set on this LayoutInflater");
}
// ...
}AppCompat implements its own factory to provide backward‑compatible Material Design widgets. The delegate installs the factory only if none is present:
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
}
}5. Use Cases
By installing a custom Factory2 , you can modify view creation without writing a new view class—for example, applying a custom background based on XML attributes.
object BackgroundLibrary {
fun inject(context: Context?): LayoutInflater? {
val inflater = if (context is Activity) context.layoutInflater else LayoutInflater.from(context)
if (inflater?.factory2 == null) {
inflater.factory2 = BackgroundFactory()
}
return inflater
}
}
class BackgroundFactory : LayoutInflater.Factory2 {
private var mViewCreateFactory: LayoutInflater.Factory? = null
private var mViewCreateFactory2: LayoutInflater.Factory2? = null
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
var view: View? = null
if (mViewCreateFactory2 != null) {
view = mViewCreateFactory2!!.onCreateView(name, context, attrs)
if (view == null) view = mViewCreateFactory2!!.onCreateView(null, name, context, attrs)
} else if (mViewCreateFactory != null) {
view = mViewCreateFactory!!.onCreateView(name, context, attrs)
}
return setViewBackground(name, context, attrs, view)
}
override fun onCreateView(parent: View?, name: String, context: Context, attrs: AttributeSet): View? {
return onCreateView(name, context, attrs)
}
fun setInterceptFactory(factory: LayoutInflater.Factory) { mViewCreateFactory = factory }
fun setInterceptFactory2(factory: LayoutInflater.Factory2) { mViewCreateFactory2 = factory }
}Injecting the factory in an Application class ensures it runs for every activity:
class FuckApplication : Application() {
init {
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
BackgroundLibrary.inject(activity)
}
// ... other callbacks omitted ...
})
}
}6. Conclusion
All the source code referenced in this article can be found at the linked repository. Thanks to the BackgroundLibrary for providing a practical example of using LayoutInflater.Factory to customize view creation.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.