Android Data Binding Tutorial
This post describes one of the most interesting feature in Android recently introduced and still in beta version: Android Data Binding. Using data binding, you create a link between the presentation layer (the app UI) and the underlying data model that holds the information you want to show. The UI widgets content like TextView, EditText and so on are somehow bound to the data stored in java class. Every time the data changes the UI widget bound to it is updated, so that you don’t have to worry anymore to update the UI by yourself. If the app does not use Android data binding, it is necessary to find the view and update the content.
Setup Android data binding
As told before, this feature is still in beta version, so as the first thing let’s set the right dependencies in the top level build.gradle:
dependencies { classpath "com.android.tools.build:gradle:1.3.0" classpath "com.android.databinding:dataBinder:1.+" }
by the way, make sure you downloaded gradle 2.4. Now the dependencies are ready and it is time to modify build.gradle in the app:
apply plugin: 'com.android.databinding'
How to use Android data binding
Now that the environment is ready, it is possible to code our Android app. As example, we will create a simple weather app that shows the temperature and other information, in this case to show to data the app will use the data binding.
As the first thing, we create the layout, that will be very simple but it will contains some important things we should notice:
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="data" type="survivingwithandroid.com.androiddatabinding.model.Data" /> </data> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" > <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/toolbar" android:layout_height="wrap_content" android:layout_width="match_parent" android:background="@color/primary" android:elevation="4dp" app:theme="@style/ThemeOverlay.AppCompat.Dark"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:layout_below="@id/toolbar" android:text="@{data.city}" android:textSize="18dp" android:id="@+id/weather_icon"/> <ImageView android:layout_width="60dp" android:layout_height="60dp" android:layout_centerInParent="true" android:src="@{data.icon}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:text="@{data.descr}" android:layout_marginTop="10dp" android:layout_below="@id/weather_icon"/> <GridLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_marginRight="15dp" android:layout_alignParentBottom="true" android:layout_marginBottom="15dp" android:rowCount="1" android:columnCount="4" android:layout_centerHorizontal="true" android:layout_centerVertical="true"> <TextView android:id="@+id/temp" android:layout_width="60dp" android:layout_height="60dp" android:text="@{data.temp}" android:layout_row="0" android:layout_column="0" android:layout_gravity="center" /> <TextView android:id="@+id/press" android:layout_width="60dp" android:layout_height="60dp" android:text="@{data.pressure}" android:layout_row="0" android:layout_column="1" android:layout_gravity="center" /> <TextView android:id="@+id/hum" android:layout_width="60dp" android:layout_height="60dp" android:text="@{data.humidity}" android:layout_row="0" android:layout_column="2" android:layout_gravity="center" /> <TextView android:id="@+id/wind" android:layout_width="60dp" android:layout_height="60dp" android:text="@{data.wind}" android:layout_row="0" android:layout_column="3" android:layout_gravity="center" /> </GridLayout> </RelativeLayout> </layout>
As root of the app UI layout there is the layout, and then the variable we want to use in our layout are declared (line 3-5). It is important to declare these variables because they will be used in the binding process. At line 4, it is stated that the variable data is the type of survivingwithandroid.com.androiddatabinding.model.Data that contains the data we want to show. In other world this class is a POJO that is bound the UI. In the TextView widgets, the value of each field of the POJO class is bound to the corresponding android:text so the value is shown automatically.
Binding variable to the object
Now the layout is ready and it is possible to bind the class field to the UI widget. In onCreate method we get the reference to the current layout of the Activity in a different way compared to the usual way:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); binding = DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setData(data); .... }
where data is defined as:
private Data data = new Data(); //survivingwithandroid.com.androiddatabinding.model.Data;
Notice that at line 5 we set the activity layout and then we resolve the variable used in the layout. If we run the app in this way, we notice that the UI data is updated only once at the beginning, when the underlaying class field, bound to the UI, changes the UI does not reflect the change. This happen because it is necessary create a listener between the UI and the data field.
The first step is that the our POJO Data class extends BaseObservable:
public class Data extends BaseObservable { .... }
Now it is necessary to bind the single class field to the UI, for example for temperature field:
public class Data extends BaseObservable { .. @Bindable public String getTemp() { return temp; } public void setTemp(String temp) { this.temp = temp; notifyPropertyChanged(BR.temp); } .. }
The @Bindable
annotation is used to make a reference between the UI and the field and the notifyPropertyChanged
informs the listener that the underlaying filed is changed and it is necessary to update the view. Running an example, using the Android data binding the result is:
Conclusion
As we have noticed Android data binding is a very interesting and powerful feature and it can simplify a lot app building. Anyway it is still in beta version and there are some small problems: for example even if i tried to update the ImageView using the same way and setting the resource id it did not work. Is it my mistake?!!
Reference: | Android Data Binding Tutorial from our JCG partner Francesco Azzola at the Surviving w/ Android blog. |