18

I have used data-binding for a while, even now it is not available for JDK 8 and API 24 now. I still find a way to use the data binding in a easier way. But when I use the following way to do the exact two-way data binding(In my mind, the two-way data binding is the thing like here(What is two way binding?), somethings strange is happened.

1. Two-way databinding(in xml)

android:text="@={testStr}"

This is not mentioned in the official documentation(https://developer.android.com/topic/libraries/data-binding/index.html, this page is usually updated, may be it is changed now). But it is available to bind the variable to the xml.

2. ObservableField for the attributes

Example from here (https://developer.android.com/topic/libraries/data-binding/index.html#observablefields)

private static class User {
   public final ObservableField<String> firstName =
       new ObservableField<>();
   public final ObservableField<String> lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}

3. Extend the model class to the BaseObservable

private static class User extends BaseObservable {
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   @Bindable
   public String getLastName() {
       return this.lastName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       notifyPropertyChanged(BR.firstName);
   }
   public void setLastName(String lastName) {
       this.lastName = lastName;
       notifyPropertyChanged(BR.lastName);
   }
}

The model class must be extended to the BaseObservable class, and also the getter method must be annotated with "@Bindable" and the setter method need to call the method notifyPropertyChange() with corresponding naming in the binding xml.

My question is, I would like to know the drawback and the advantages for three binding methods. Of course, I know the first one will be easier. But some moment I found in the documentation and in some website. And it disappeared in the next moment. The official documentation is changed without any clear announcement. I still wonder should I use the first method so I have to prepare to change the the method 2 or 3.

Student_XML2WAY.java

public class Student_XML2WAY {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int pAge) {
        age = pAge;
    }
    public String getName() {
        return name;
    }
    public void setName(String pName) {
        name = pName;
    }
}

Student_ObserField.java

public class Student_ObserField {
    private ObservableInt age;
    private ObservableField<String> name;
    public Student_ObserField() {
        age = new ObservableInt();
        name = new ObservableField<>();
    }
    public ObservableInt getAge() {
        return age;
    }
    public ObservableField<String> getName() {
        return name;
    }
}

Student_Extend.java

public class Student_Extend  extends BaseObservable{
    private int age;
    private String name;

    @Bindable
    public int getAge() {
        return age;
    }
    public void setAge(int pAge) {
        age = pAge;
        notifyPropertyChanged(BR.student3);
    }
    @Bindable
    public String getName() {
        return name;
    }
    public void setName(String pName) {
        name = pName;
        notifyPropertyChanged(BR.student3);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="student1"
            type="example.com.testerapplication.sp.bean.Student_XML2WAY"/>

        <variable
            name="student2"
            type="example.com.testerapplication.sp.bean.Student_ObserField"/>

        <variable
            name="student3"
            type="example.com.testerapplication.sp.bean.Student_Extend"/>

    </data>

    <LinearLayout

        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
      >

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

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

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{student3.name}"/>
        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="update"/>
    </LinearLayout>
</layout>

Activity class

public class MainActivity extends AppCompatActivity {
    private Student_XML2WAY mStudent1;
    private Student_ObserField mStudent2;
    private Student_Extend mStudent3;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.activity_main, null, false);
        mStudent1 = new Student_XML2WAY();
        mStudent1.setName("XML First");
        mStudent2 = new Student_ObserField();
        mStudent2.getName().set("ObserField Second");
        mStudent3 = new Student_Extend();
        mStudent3.setName("Extend Third");
        binding.setStudent1(mStudent1);
        binding.setStudent2(mStudent2);
        binding.setStudent3(mStudent3);
        setContentView(binding.getRoot());
        binding.btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mStudent1.setName("Student1");
                mStudent2.getName().set("Student2");
                mStudent3.setName("Student3");
            }
        });
    }
}
Community
  • 1
  • 1
Long Ranger
  • 5,888
  • 8
  • 43
  • 72
  • Since I do not really understand your problem (since correctly implemented Two-Way Binding works fine for me), I would like to suggest reading in the StackOverflow documentation, there are a few [examples](http://stackoverflow.com/documentation/android/111/data-binding-library-basics/6634/built-in-two-way-data-binding#t=201608310721368479233), also, George Mount (a developer of this library) has written some [blog posts](https://medium.com/google-developers/android-data-binding-lets-flip-this-thing-dc17792d6c24#.hty4thulu) on this topic. – yennsarah Aug 31 '16 at 07:25
  • Question is updated. I have read the blog and I already used it in my application. But the drawback is not clear and the official documentation have not stated clearly about the binding method. Other method like setVariable, executePendingBindings also provided the binding function. That make a confusing meaning for me which one should I use and how to use(when to call it and where to call it). – Long Ranger Aug 31 '16 at 08:01
  • The first method you mention is not an alternative to the orther two, is the way yout declare two-way databinding. While 2° and 3° are alternatives and is up to you to decide the cheapest way in terms of time, as per docs "A little work is involved in creating Observable classes, so developers who want to save time or have few properties may use ObservableField and its siblings ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, and ObservableParcelable." – Bronx Aug 31 '16 at 09:03
  • But if there is an Object inside the model (using method 2) which is also bind with few attributes, does the nested object's attributes also be changed to Observable type? – Long Ranger Aug 31 '16 at 09:39

2 Answers2

10

Your Student_XML2WAY.java won't work with 2-way Binding, since it does not fulfill the requirements to do so (BaseObservable, Bindable or something like that).

I would use BaseObservable if I will directly access the model, just like in your Student_Extend. I will have an instance of Student_Extend in my Activity and I will set the variable in onCreate:

Student mStudent = new Student("John Doe", 42); //
binding.setStudent(mStudent);
//later:
mStudent.setAge(37);

If implemented correctly, this will also change the Age in your UI (as well as in your model).

If you do not want to access your model directly and want to use a ViewModel, I work with ObervableFields:

public class Student {
    private String name;
    private int age;
    //Corresponding setters and getters
}


public class StudentViewModel {
    private ObservableField<Student> mStudentField = new ObservableField<>();

    //if I have a large model class, and only want to use some fields, 
    //I create some getters (and setters, for the two way attributes)
    //Something like this:

    public int getAge() {
        return mStudentField.get().getAge();
    }
    public void setAge(int newAge) {
        return mStudentField.get().setAge(newAge);
    }
}

So, I create an instance of StudentViewModel in my Activity and set it to the binding. Pseudo-xml would look like this:

<layout>
    <data>
        <variable name="studentViewModel" 
                  type="locaction.of.StudentViewModel"> <!-- or do an import -->
    </data>
    <EditText 
        android:text="@={studentViewModel.age}"/>
</layout>

So, the ViewModel approach is "clearer" since you outsource almost everything that has to do with views. Put your BindingAdapter, click methods, converter methods there and keep your Activity clean. Also, you do not directly change your model. This approach can be an overkill for simple classes and projects. ;)

If you want to see a full, example that uses DataBinding and MVVM, check out Droids on roids approach on this.

yennsarah
  • 5,467
  • 2
  • 27
  • 48
  • I updated the question with click event, I tried to test the button click with the variable updated. Only the second one is updated. Even I used your View model suggestion, does the Student class attributes like 'age', 'name" also be changed to type "Observable" ?? I am afraid that would be a disaster for the medium project. It is not like the parcelable interface that can be removed and generated easily. – Long Ranger Aug 31 '16 at 09:47
  • As I said, your student1 is a POJO and has no knowledge of DataBinding. The third one should also get updated, I am a bit confused why not. It is not a good practice, but can you check if the value gets updated if you call `binding.executePendingBindings()`? I will add the `Student.class` to clarify my ViewModel example – yennsarah Aug 31 '16 at 09:57
  • OK, I understand method 1 now. Only I reset the value can update the variable and binding in the UI. For the method 3, I tried but not work. Only when I change "BR.student3" to "BR.name", it will works. I wonder how can be the "BR.name" worked and only the student3 name( but not others )is updated. – Long Ranger Aug 31 '16 at 10:05
  • Oh! Now I see it. You need to call `BR.yourPropertyName`, not the `variable name` like `student3`. If you have a property called `age`, it should be `BR.age`! – yennsarah Aug 31 '16 at 10:10
  • Thank you for your reply and help. MVVM is a good idea for the binding. In current, ViewModel and Observable Fields would be the best method to avoid a lot of change in the code modification. Extends Observable will have trouble if I used the ORM in my model. – Long Ranger Sep 01 '16 at 03:46
2

I feel that ObservableField approach is the way to go as there's no need to write getters/setters OR invoke notifyPropertyChanged.

Also, if you have a custom object ObservableField<Student> studentField, and you use android:text="@{viewModel.studentField.name}, the text does get updated when you invoke studentField.set(newStudent).

I find RxJava very useful. ObservableField can be easily converted to rx.Observable and vice versa. This allows use of Rx operators. In case you are interested, you can check the implementation here: FieldUtils.java

Manas Chaudhari
  • 276
  • 2
  • 4