Android Data Binding - how to use ViewStub with data binding

Another way I did this was using the ViewStubProxy. The documentation/source code for ViewStubProxy is good.

When you create your new ViewStubProxy, pass in your ViewStub in the constructor. Then ViewStubProxy.setContainingBinding(<pass in your root layout binding>). If you don't do this you'll get nulls.

Then instead of setting the ViewStub.setOnInflateListener (the ViewStubProxy will set one internally for you), set the ViewStubProxy.setOnInflateListener to set your binding variables and LifecycleOwner.

Sample code:

   private void setBindings(){
      //usual outside layout binding steps
      binding = (MeasurementsLayoutBinding) getBinding();
      binding.setViewModel(viewModel());
      binding.setLifecycleOwner(this);

      //cartSideStubView is the root layout stubview
      cartViewStubProxy = new ViewStubProxy(cartSideStubView);
      cartViewStubProxy.setContainingBinding(binding);

      cartViewStubProxy.setOnInflateListener(((viewStub, view) -> {

         cartViewStubProxy.getBinding().setVariable(BR.viewModel, viewModel());
         cartViewStubProxy.getBinding().setLifecycleOwner(this);

        //after inflation you can find your viewstub views

         cartHitchToFixedAxleInputField = view.findViewById(R.id.cart_hitch_to_fixed_axle_input_field);       


      }));

   }

Just set the listener as the doc says :

mBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
    @Override
    public void onInflate(ViewStub stub, View inflated) {
        ViewStubBinding binding = DataBindingUtil.bind(inflated);
        binding.setModel(model);
    }
});



public void inflateViewStub(View view) {
    if (!mBinding.viewStub.isInflated()) {
        mBinding.viewStub.getViewStub().inflate();
    }
}

Declare your xml namespace, and pass the variable through that. This works with <include>, too, btw. Here's an example:

main_layout.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:my-namespace="http://schemas.android.com/apk/res-auto">
    <data>
        <variable name="myData" type="com.example.SomeModel" />
    </data>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <ViewStub
            android:id="@+id/view_stub"
            android:inflatedId="@+id/view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout="@layout/another_layout"
            my-namespace:data="@{myData}"
            />

    </RelativeLayout>
</layout>

another_layout.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- No need to declare my-namespace here -->
    <data>
        <variable name="data" type="com.example.SomeModel" />
    </data>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{data.someValue}" />

    </RelativeLayout>
</layout>

So now you just have to call inflate() and the layout will have the data. You can check the generated binding class to verify this. It even has type safety, so you can't pass any other type into data.


Just to elaborate on @andrew-classen's accepted answer above, and include the @user9113597's answer as well:

In your layout that contains the stub (e.g. my_fragment.xml)

    <data class="MyDataBinding">
        ...
    </data>

    ...
    <ViewStub 
        android:id="@+id/stub_import"
        ... />

In your activity or fragment (example below is using a fragment):

MyDataBinding mBinding;
MyViewModel mViewModel;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
    @Nullable Bundle savedInstanceState)
{
    mBinding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false); 
    mBinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() 
    {
        @Override
        public void onInflate(ViewStub stub, View inflated)
        {
            ViewDataBinding dataBinding = DataBindingUtil.bind(inflated);
            binding.setVariable(BR.mViewModel, mViewModel));
        }
    });
}

public void inflateViewStub(View view) {
if (!mBinding.viewStub.isInflated()) {
    mBinding.viewStub.getViewStub().inflate();
}

}

In your stub layout:

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="mViewModel"
            type="com.example.myapp.MyViewModel" />
    </data>
    ...