Many screens across the application can have common parts: error, empty, loading states, a toolbar, a navigation bar — you name it. If a common part consists of a single view, it can be turned into a one-liner with styles. But in this topic, we'll consider other useful techniques for reusing layouts.
<include>
This special tag is used to include one layout into another. It's as simple as it goes:
<!-- layout/toolbar.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#E2E2E2"
app:title="The coolest app ever" />
<!-- layout/main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:text="…is not developed yet."
android:textSize="24sp" />
</LinearLayout>
According to the official documentation, you can specify the id and layout_ parameters directly inside the <include> tag in order to override them. Also, according to the source code, you can override visibility in the same way.
<include> is an intrinsic, not a View. Unlike "normal" views, it uses a plain layout attribute without the android: namespace.<merge>
What if you need to include several views into a container? An XML document must have a single root tag. Of course, you can wrap the included views in an extra ViewGroup, but this could give an unintended result. Also, it's better to avoid extra nesting for performance reasons.
Instead of a container, you can use the <merge> tag:
<!-- layout/content.xml -->
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#E2E2E2"
app:title="The coolest app ever" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:text="…is not developed yet."
android:textSize="24sp" />
</merge>
<!-- layout/main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/content" />
</LinearLayout>
So, Toolbar and TextView will become direct children of LinearLayout.
Since the <merge> tag doesn't know to which parent it is going to be added, the preview will become of no use at this point. To fix this, you can add some hints for Android Studio:
<merge
xmlns:...
tools:parentTag="android.widget.LinearLayout"
tools:orientation="vertical">
Unlike "normal" views, merged views will not receive attributes of the <include> tag.
ViewStub
One last thing: there's a special invisible zero-size view that acts like a lazy version of <include>. Similarly, it allows you to specify a layout to inflate, and will transfer layout_ attributes and id to the newly created layout. On the other hand, it will not actually create the whole layout until requested (and it doesn't support <merge>).
<!-- layout/toolbar.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#E2E2E2"
app:title="The coolest app ever" />
<!-- layout/main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ViewStub
android:id="@+id/toolbar"
android:layout="@layout/toolbar" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:text="…is not developed yet."
android:textSize="24sp" />
</LinearLayout>
To show the nested layout, you can either call the special ViewStub.inflate() method, or the common View.setVisibility() method. When called, ViewStub inflates the specified layout and replaces itself with it. Why are there two options to do the same thing? The first one is explicit, and the second one will work even without knowing that it's a ViewStub.
findViewById<ViewStub>(R.id.toolbar).inflate()
// or
findViewById<View>(R.id.toolbar).setVisibility(View.VISIBLE)
Conclusion
Now you know how to extract parts of a layout in order to reuse them or make the layout more concise using the <include> and <merge> tags. Also, now you can instantiate some view hierarchies on demand using ViewStub.