Search code examples
androidandroid-databinding

Android Data Binding Programmatically Instantiated View


The Android documentation does a great job of describing how one can create a binding class using a layout xml file. But I have a couple of questions.

Is there a way to create a data binding class for a custom view that is instantiated programmatically? For example, lets say I have two custom view classes and I want to bind the same view model object to them programmatically without using any xml. The classes are as follows:

class MyViewModel {
}

class MyCustomView extends View {
}

class MyAnotherCustomView extends MyCustomView {
}

Now lets say I instantiate MyCustomView/MyAnotherCustomView using:

MyCustomView customView = new MyCustomView(context);

How do I go about using data binding in this case? Is this possible using the official Android data binding framework? If not, what other frameworks/libraries are available or recommended to achieve this?

My second question is a follow up of the first question. Lets say it is not possible to achieve what I want in my first question. Then, I will have to define a my_custom_view.xml file. This will look something like this:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <com.example.name.MyCustomView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:text="@{user.firstName}"/>
</layout>

Now, if I want to use MyAnotherCustomView which is a subclass of MyCustomView keeping the binding logic the same, will I have to create a new xml file my_another_custom_view.xml just to replace MyCustomView with MyAnotherCustomView to define the same binding?


Solution

  • The answer to the first question is "No." Android data binding requires the XML to generate the binding classes.

    In your second question, you offer a solution that will work. If you go that route, one way to do it is to use the ViewDataBinding base class setters to set your variables. I can imagine a method like this:

    public void addCustomView(LayoutInflater inflater, ViewGroup container, User user) {
        ViewDataBinding binding = DataBindingUtil.inflate(inflater,
            this.layoutId, container, true);
        binding.setVariable(BR.user, user);
    }
    

    Here, I've assumed the selection of which custom View is determined by a field layoutId. Each possible layout will have to define a user variable of type User.

    I don't know the particulars of your use, but if you want to dynamically choose which custom view to load, you could use a ViewStub. You could also do the same thing with just visibility if you don't have any tremendous overhead in loading your custom Views.

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto">
       <data>
           <import type="android.view.View"/>
           <variable name="user" type="com.example.User"/>
           <variable name="viewChoice" type="int"/>
       </data>
       <FrameLayout ...>
           <!-- All of your outer layout, which may include binding
                to the user variable -->
           <ViewStub android:layout="@layout/myCustomView1"
                     app:user="@{user}"
                     android:visiblity="@{viewChoice == 1} ? View.VISIBLE : View.GONE"/>
           <ViewStub android:layout="@layout/myCustomView2"
                     app:user="@{user}"
                     android:visiblity="@{viewChoice == 2} ? View.VISIBLE : View.GONE"/>
        </FrameLayout>
    </layout>