Observable Data Object refers to the capability of an object to notify others(listeners) about the changes in its data. The data binding library allows us to make objects, fields, or collections observable.
Any plain-old object can be used for data binding, but modifying the object doesn’t automatically cause the UI to update.
Data binding can be used to give your data objects the ability to notify other objects, known as listeners, when its data changes. When one of these observable data objects (objects, fields, collections) is bound to the UI and a property of the data object changes, the UI is updated automatically.
In the previous article, I have explained about how to work with data binding with the help of a demo. In this article, we will talk about how to make objects, fields, or collections observable using Data Binding. If you are new to DataBinding then read this article.
Let’s understand each of these observables(objects, fields, or collections) with the help of example.
Observable Fields
If your object class has fewer properties to be updated or if you don’t want to observe every field in the object, you can use ObservableFields to update the UI.
You can declare the variable as ObservableField and when the new data is set, the UI will be updated.
You can use the following primitive-specific classes to make fields observable:
- ObservableBoolean
- ObservableByte
- ObservableChar
- ObservableShort
- ObservableInt
- ObservableLong
- ObservableFloat
- ObservableDouble
- ObservableParcelable
Let’s understand with an example how to make fields observable.
1 .I have created a class named User. To make the fields firstName,lastName and age observable modify it as shown below:
User.java
package com.example.databindingexample;
import androidx.databinding.ObservableField;
import androidx.databinding.ObservableInt;
public class User{
public final ObservableField<String> firstName = new ObservableField<>();
public final ObservableField<String> lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
2 .Open MainActivity.java. To test this(observable field), I am updating user data by assigning a new value to the fields on button click. You can see the UI updated on button click right away.
MainActivity.java
package com.example.databindingexample; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.Toast; import com.example.databindingexample.databinding.ActivityMainBinding; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; public class MainActivity extends AppCompatActivity { ActivityMainBinding binding; ClickHandler handler; User user; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); user = new User(); user.firstName.set("Arun"); user.lastName.set("Chandravanshi"); user.age.set(28); binding.setUser(user); handler = new ClickHandler(binding.edtFname, binding.edtLname, binding.edtAge); binding.setHandler(handler); } public class ClickHandler { EditText edtFirstName, edtLastName, edtAge; public ClickHandler(EditText edtFirstName, EditText edtLastName, EditText edtAge) { this.edtFirstName = edtFirstName; this.edtLastName = edtLastName; this.edtAge = edtAge; } public void updateUser(View view) { user.firstName.set(edtFirstName.getText().toString()); user.lastName.set(edtLastName.getText().toString()); user.age.set(Integer.parseInt(edtAge.getText().toString())); Toast.makeText(getApplicationContext(), "Name: " + user.firstName.get() + " " + user.lastName.get() + "\nAge: " + user.age.get(), Toast.LENGTH_LONG).show(); } } }
To set a value inside the field use the set() and to get the value use get() of the ObservableField class.
The ObservableField class consists of set() which internally calls notifyChange() (notifies listeners that the value of this property has changed and now the UI will be updated with the new values).
3 .The activity.xml file consists of a button on click of which we are updating fields(firstName, lastName, age) with the EditText values.
To access the observable fields inside views we have to write:
android:text="@{user.firstName}"
Where firstName is the User object property name.
activity_main.xml
<?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="user" type="com.example.databindingexample.User" /> <variable name="handler" type="com.example.databindingexample.MainActivity.ClickHandler" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="30dp" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="15dp" android:background="@drawable/background"> <TextView android:id="@+id/tv_firstName" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" android:layout_marginTop="10dp" android:text="@{user.firstName}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold"/> <TextView android:id="@+id/tv_lastName" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:textSize="20sp" android:text="@{user.lastName}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold" /> <TextView android:id="@+id/tv_age" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="20sp" android:layout_marginTop="10dp" android:text="@{String.valueOf(user.age)}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold"/> </LinearLayout> <EditText android:id="@+id/edt_fname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="40dp" android:textSize="22sp" android:hint="Enter First Name" /> <EditText android:id="@+id/edt_lname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:textSize="22sp" android:hint="Enter Last Name" /> <EditText android:id="@+id/edt_age" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:textSize="22sp" android:hint="Enter Age" /> <Button android:id="@+id/btn_update" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{handler::updateUser}" android:layout_gravity="center" android:textSize="15sp" android:background="@drawable/background" android:layout_marginTop="30dp" android:padding="18dp" android:text="UPDATE USER" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold" /> </LinearLayout> </layout>
When you run the code It will look like this:
Observable Collections
A Collection is a group of individual objects represented as a single unit. DataBinding provides two Observable Collection classes:
ObservableArrayList: class which extends ArrayList<> and is useful when the key is an integer.
ObservableArrayMap: class which extends ArrayMap<K, V> and is useful when the key is a reference type, such as String,
Let’s understand with an example how to make Collections observable.
ObservableArrayList
1 .Inside MainActivity.java, I have created an ObservableArrayList named list which holds String data. To test this( ObservableArrayList), I am updating list by setting new values at particular keys on button click. You can see the UI updated on button click right away.
MainActivity.java
package com.example.databindingexample; import android.os.Bundle; import android.view.View; import android.widget.EditText; import com.example.databindingexample.databinding.ActivityMainBinding; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import androidx.databinding.ObservableArrayList; public class MainActivity extends AppCompatActivity { ActivityMainBinding binding; ClickHandler handler; ObservableArrayList<String> list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); list = new ObservableArrayList<>(); list.add("Arun"); list.add("Chandravanshi"); binding.setUserlist(list); handler = new ClickHandler(binding.edtFname, binding.edtLname); binding.setHandler(handler); } public class ClickHandler { EditText edtFirstName, edtLastName; public ClickHandler(EditText edtFirstName, EditText edtLastName) { this.edtFirstName = edtFirstName; this.edtLastName = edtLastName; } public void updateName(View view) { list.add(0, edtFirstName.getText().toString()); list.add(1, edtLastName.getText().toString()); } } }
The ObservableArrayList<> class consists of add() which internally calls notifyAdd(). notifyAdd method causes the UI to be updated with the new value.
2 .In activity_main.xml inside <data> tag we will define a variable named userlist pointing to ObservableArrayList class object.
To access list data inside views we have to write like this:
android:text="@{userlist[0]}"
activity_main.xml
<?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> <import type="androidx.databinding.ObservableArrayList"/> <variable name="userlist" type="ObservableArrayList<String>" /> <variable name="handler" type="com.example.databindingexample.MainActivity.ClickHandler" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="30dp" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="35dp" android:background="@drawable/background"> <TextView android:id="@+id/tv_firstName" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="30sp" android:layout_marginTop="10dp" android:text="@{userlist[0]}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold"/> <TextView android:id="@+id/tv_lastName" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:textSize="30sp" android:text="@{userlist[1]}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold" /> </LinearLayout> <EditText android:id="@+id/edt_fname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="60dp" android:textSize="22sp" android:hint="Enter First Name" /> <EditText android:id="@+id/edt_lname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:textSize="22sp" android:hint="Enter Last Name" /> <Button android:id="@+id/btn_update" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{handler::updateName}" android:layout_gravity="center" android:textSize="15sp" android:background="@drawable/background" android:layout_marginTop="30dp" android:padding="18dp" android:text="UPDATE NAME" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold" /> </LinearLayout> </layout>
ObservableArrayMap
1 .Inside MainActivity.java, I have created an ObservableArrayMap named list with key of type String and which holds String data. To test this( ObservableArrayMap), I am updating list by setting new values at particular keys on button click. You can see the UI updated on button click right away.
MainActivity.java
package com.example.databindingexample; import android.os.Bundle; import android.view.View; import android.widget.EditText; import com.example.databindingexample.databinding.ActivityMainBinding; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import androidx.databinding.ObservableArrayMap; public class MainActivity extends AppCompatActivity { ActivityMainBinding binding; ClickHandler handler; ObservableArrayMap<String, String> list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); list = new ObservableArrayMap<>(); list.put("firstName", "Arun"); list.put("lastName", "Chandravanshi"); binding.setUserlist(list); handler = new ClickHandler(binding.edtFname, binding.edtLname); binding.setHandler(handler); } public class ClickHandler { EditText edtFirstName, edtLastName; public ClickHandler(EditText edtFirstName, EditText edtLastName) { this.edtFirstName = edtFirstName; this.edtLastName = edtLastName; } public void updateName(View view) { list.put("firstName", edtFirstName.getText().toString()); list.put("lastName", edtLastName.getText().toString()); } } }
The ObservableArrayMap<K, V> class consists of put() which internally calls notifyChange(K) (notifies listeners that the value of this key has changed and now the UI will be updated with the new value).
2 .In activity_main.xml inside <data> tag we will define a variable named userlist pointing to ObservableArrayMap class object.
To access list data inside views we have to write like this:
android:text="@{userlist.firstName}"
where firstName is the key name.
activity_main.xml
<?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> <import type="androidx.databinding.ObservableArrayMap"/> <variable name="userlist" type="ObservableArrayMap<String,String>" /> <variable name="handler" type="com.example.databindingexample.MainActivity.ClickHandler" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="30dp" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="35dp" android:background="@drawable/background"> <TextView android:id="@+id/tv_firstName" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="30sp" android:layout_marginTop="10dp" android:text="@{userlist.firstName}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold"/> <TextView android:id="@+id/tv_lastName" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:textSize="30sp" android:text="@{userlist.lastName}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold" /> </LinearLayout> <EditText android:id="@+id/edt_fname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="60dp" android:textSize="22sp" android:hint="Enter First Name" /> <EditText android:id="@+id/edt_lname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:textSize="22sp" android:hint="Enter Last Name" /> <Button android:id="@+id/btn_update" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{handler::updateName}" android:layout_gravity="center" android:textSize="15sp" android:background="@drawable/background" android:layout_marginTop="30dp" android:padding="18dp" android:text="UPDATE NAME" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold" /> </LinearLayout> </layout>
When you run the code for both ObservableArrayList and ObservableArrayMap it will look like this:
Observable Objects
The Data Binding library provides the BaseObservable class which implements the listener registration mechanism. To make the object observable, extend the class from BaseObservable.
- To make a property observable, use @Bindable annotation on getter method.
- Call notifyPropertyChanged(BR.property) in setter method to update the UI whenever the data is changed.
- The BR class will be generated automatically when data binding is enabled.
Let’s understand with an example how to make Object observable.
1 .Below is the modified User class that extends BaseObservable. You can notice here notifyPropertyChanged() is called after assigning new values.
User.java
package com.example.databindingexample; import androidx.databinding.BaseObservable; import androidx.databinding.Bindable; public class User extends BaseObservable { public String firstName, lastName; @Bindable public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; notifyPropertyChanged(BR.firstName); } @Bindable public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; notifyPropertyChanged(BR.lastName); } }
We must specify the getter method of the specific field with the @Bindable annotation to generate the specific field inside BR class which we will pass from the notifyPropertyChanged() inside the setter method.
2 .The generated BR class for the app is shown below:
BR.java
package com.example.databindingexample; public class BR { public static final int _all = 0; public static final int firstName = 1; public static final int lastName = 2; public static final int handler = 3; public static final int user = 4; }
BR class also consists of variable names defined inside the activity_main.xml file.
3 .Inside activity_main.xml file, I have defined two variables with name user (a reference for User class object) and handler(a reference for ClickHandler class object) inside <data> tag:
To access the object property inside views we have to simply write :
android:text="@{user.firstName}"
Wher firstName is the User object property name.
activity_main.xml
<?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="user" type="com.example.databindingexample.User" /> <variable name="handler" type="com.example.databindingexample.MainActivity.ClickHandler" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="30dp" android:orientation="vertical" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="35dp" android:background="@drawable/background"> <TextView android:id="@+id/tv_firstName" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="30sp" android:layout_marginTop="10dp" android:text="@{user.firstName}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold"/> <TextView android:id="@+id/tv_lastName" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:textSize="30sp" android:text="@{user.lastName}" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold" /> </LinearLayout> <EditText android:id="@+id/edt_fname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="60dp" android:textSize="22sp" android:hint="Enter First Name" /> <EditText android:id="@+id/edt_lname" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="30dp" android:textSize="22sp" android:hint="Enter Last Name" /> <Button android:id="@+id/btn_update" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{handler::updateName}" android:layout_gravity="center" android:textSize="15sp" android:background="@drawable/background" android:layout_marginTop="30dp" android:padding="18dp" android:text="UPDATE NAME" android:textColor="@android:color/holo_blue_dark" android:textStyle="bold" /> </LinearLayout> </layout>
4 .Open MainActivity.java. To test this(observable object), I am changing user object properties on button click. You can see the UI updated on button(UPDATE NAME) click right away.
MainActivity.java
package com.example.databindingexample; import android.os.Bundle; import android.view.View; import android.widget.EditText; import com.example.databindingexample.databinding.ActivityMainBinding; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; public class MainActivity extends AppCompatActivity { ActivityMainBinding binding; User user; ClickHandler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); user = new User(); user.setFirstName("Arun"); user.setLastName("Chandravanshi"); binding.setUser(user); handler = new ClickHandler(binding.edtFname,binding.edtLname); binding.setHandler(handler); } public class ClickHandler { EditText edtFirstName, edtLastName; public ClickHandler(EditText edtFirstName, EditText edtLastName) { this.edtFirstName = edtFirstName; this.edtLastName = edtLastName; } public void updateName(View view) { user.setFirstName(edtFirstName.getText().toString()); user.setLastName(edtLastName.getText().toString()); } } }
When you run the app It will look like this:
I hope this article will help you in understanding how to work with Observable data objects using DataBinding.