ViewStubs and root elements

This might seem like a very obvious thing to most, but for me it was not. In some code I’ve inherited, ViewStub is used a great deal and works well, except for when I was trying to resolve the root element of the inflated content. For example the following (greatly simplified) setup:

Host view XML

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/content"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" 
    android:orientation="vertical">
 
    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

	    <ViewStub 
	        android:id="@id/stub_import"
                android:inflatedId="@id/content_import"
                android:layout="@layout/splash"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent" />

    </RelativeLayout>
</LinearLayout>
</pre>

<!--more-->

<h4>Content View XML</h4>
<pre class="prettyprint lang-xml">
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:id="@+id/root_control">

    <LinearLayout
        android:id="@+id/some_sub_control"
        android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:orientation="vertical">

    </LinearLayout>
</LinearLayout>

Then in the your Activity:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ViewStub stub = (ViewStub)this.findViewById(R.id.stub_import);
    stub.setInflatedId(R.id.content_import);
    stub.setLayoutResource(R.layout.content);
    stub.inflate();

    // rootElement will be null!!
    LinearLayout rootElement = (LinearLayout)this.findViewById(R.id.root_control);

    // subControl holds a reference to the expected control.
    LinearLayout subControl = (LinearLayout)this.findViewById(R.id.some_sub_control);
}

So what’s going on here? Well three things really! Mostly that I should pay attention more and not rush so much. Those small details you can easily overlook often turn out to be the cause of your problems. The other two things are pretty much the same thing:


android:inflatedId="@id/content_import"

// From the Java code
stub.setInflatedId(R.id.content_import);

So here, they’re instructing the ViewStub to give the inflated content a new id once the ViewStub has been replaced. Great! Why they did it twice is anyone’s guess but that was the problem. Remove that code and hey presto! It works as expected. Also, another handy tip is that you can also get a reference to the inflated content via the result of the call to inflate().

LinearLayout rootElement = (LinearLayout)stub.inflate();
// Gets you the same thing as...
LinearLayout rootElement = (LinearLayout)this.findViewById(R.id.root_control);