Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use android data binding to bind an interface?

i have the situation that i have a fragment, whose textfields will be filled by either a X1Object or a X2Object, which both are implementing the interface IXObject and extending the BaseObservable class provided by the Android DataBinding library, but contain additional different fields and behaviour. The IXObject contains the methods for getters and setters.

public interface IXObject {
    void setName(String name);
    String getName();
}

public class X1Object extends BaseObservable implements IXObject {
    private String name;

    @Override
    @Bindable
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
}

public class X2Object extends BaseObservable implements IXObject {...}

Then i am trying to use a single layout file for the fragment, using Android DataBinding. The layout looks like this:

<?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"
xmlns:tools="http://schemas.android.com/tools">

<data>
    <variable name="xObject" type="com.test.x.model.IXObject"/>
</data>

<LinearLayout
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{xObject.name}"/>

</LinearLayout>
</layout>

In the fragment class, i am applying the binding:

public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    MyFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false);

    View view = binding.getRoot();

    IXObject xobj = new X1Object();
    binding.setXObject(xobject);

    return view;
}

Unfortunately, using the IXObject as data binding reference, the method "addOnPropertyChangedCallback" in the BaseObservable class is never called.

Using X1Object directly for binding in the layout file and the binding class, everything works perfectly.

Can you help me how to achieve my goal to use an interface for binding?

Thank you.

like image 781
sneps Avatar asked Jun 01 '17 08:06

sneps


People also ask

How do you bind data in UI?

Bind UI to data from data models You need to deploy a data model in order to bind to data. Clone the Home example. With Amplify Studio you can bind elements in your UI component to actual backend data from your database by passing component properties with a Data model type down to the child elements.

Is data binding good in Android?

Data Binding allows you to effortlessly communicate across views and data sources. This pattern is important for many Android designs, including model view ViewModel (MVVM), which is currently one of the most common Android architecture patterns.

Can I use both data binding and view binding?

Conversely, view binding has the following limitations compared to data binding: View binding doesn't support layout variables or layout expressions, so it can't be used to declare dynamic UI content straight from XML layout files. View binding doesn't support two-way data binding.


2 Answers

When you declare a variable in your layout file you must take it literally. interfaces are not objects. I know Java supports generic inline objects where you can declare an interface as if it were an object, but in general they are not objects.

So when you say to the layout file to use a variable of the interface type, it really can't do much as it is not an object.

One thing you will notice and hate about android data binding is that it cannot infer types. So if you are trying to set a variable using an implementing class, but hoped to do it with an interface you will not have any luck.

You need to specify the class that will be used, not the interface that those classes inherit from. As the other answer pointed out, you can utilize a base class if your base class has the necessary methods.

Also quick note you can simply put the binding on the private member variable and it will still go through the getter and setters automatically.

The only other thing you could do is create an adapter that takes the interface or your interface or even Any with a cast, and sets the name for you. Which requires one more adapter in your already growing adapters class, but at least they are straight forward to do.

like image 190
Sam Avatar answered Oct 02 '22 05:10

Sam


I also can not find the way to make it work with interface but I think you can use Abstract class like this.

public abstract class IXObject extends BaseObservable {
    public abstract void setName(String name);

    @Bindable
    public abstract String getName();
}

.

public class X1Object extends IXObject {
    private String name;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
}

.

final IXObject xobj = new X1Object();
xobj.setName("nguyen");
binding.setXObject(xobj);
like image 40
Linh Avatar answered Oct 02 '22 03:10

Linh