In this article, we are going to learn the basics of DataBinding with an emphasis on explaining how data binding is implemented within an Android Studio project.
Data Binding
DataBinding is one of the Android Jetpack Architecture Component suggested by android. The Data Binding Library is a support library that allows you to directly connect the views in a user interface layout to the methods and data located in other objects within an app without the need to write code.
The Key Components of Data Binding
By default, an Android Studio project is not configured for data binding support. In fact, a number of different elements need to be combined before an app can begin making use of data binding. These involve the project build configuration, the layout XML file, data binding classes and use of the data binding expression language. Don’t worry these are actually quite simple steps which we will cover in detail.
The Project Build Configuration
Before a project can make use of data binding it must first be configured to make use of the Android Data Binding Library and to enable support for data binding classes and the binding expression syntax.
Open the build.gradle located under app and enable data binding under android module. Once enabled, Sync the project.
app/build.gradle
android {
dataBinding {
enabled = true
}
compileSdkVersion 29
buildToolsVersion "29.0.2"
:
:
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
The Data Binding Layout File
The user interfaces for an app are typically contained within an XML layout file. Before the views contained within one of these layout files can take advantage of data binding, the layout file must first be converted to a data binding layout file.
XML layout files define the hierarchy of components in the layout starting with a top-level or root view. This root view takes the form of a layout container such as a ConstraintLayout, FrameLayout or LinearLayout :
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> . . . </androidx.constraintlayout.widget.ConstraintLayout>
In order to be able to use data binding, the layout hierarchy must have a layout component as the root view which, in turn, becomes the parent of the current root view.
To convert the above layout file into data binding layout file we have to do the following changes:
<?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"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> . . . </androidx.constraintlayout.widget.ConstraintLayout> </layout>
The Layout File Data Element
The data binding layout file needs some way to declare the classes within the project to which the views in the layout are to be bound. Having declared these classes, the layout file will also need a variable name by which to reference those instances within binding expressions.
This is achieved using the data element, an example of which is shown below:
<?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.databindingdemo.User" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> . . . </androidx.constraintlayout.widget.ConstraintLayout> </layout>
The above data element declares a new variable named user of type User .
Note: it is necessary to declare the full package name of the User class when declaring the variable.
Imports, variables, and includes
The Data Binding Library provides features such as imports, variables, and includes.
- Imports make easy to reference classes inside your layout files.
- Variables allow you to describe a property that can be used in binding expressions.
- Includes let you reuse complex layouts across your app.
Imports
The data element can also import other classes that may then be referenced within binding expressions elsewhere in the layout file.
For example, the following code example imports the View class to the layout file:
<data>
<import type="android.view.View"/>
</data>
Importing the View class allows you to reference it from your binding expressions. The following example shows how to reference the VISIBLE and GONE constants of the View class:
<TextView android:text="@{user.lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
To import the User class in binding layout file we have to write:
<data> <import type="com.example.databindingdemo.User" /> <variable name="user" type="User" /> </data>
Variables
Variable element describes a property that may be set on the layout to be used in binding expressions within the layout file. You can use multiple variable elements inside the data element.
The following example declares the user, image, and note variables:
<data> <import type="android.graphics.drawable.Drawable"/> <variable name="user" type="com.example.User"/> <variable name="image" type="Drawable"/> <variable name="note" type="String"/> </data>
Includes
Variables may be passed into an included layout’s binding from the containing layout by using the app namespace and the variable name in an attribute. The following example shows included user variable from the name.xml and contact.xml layout files:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto"> <data> <variable name="user" type="com.example.User"/> </data> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <include layout="@layout/name" bind:user="@{user}"/> <include layout="@layout/contact" bind:user="@{user}"/> </LinearLayout> </layout>
The Binding Classes
The Data Binding Library generates binding classes for each data binding layout file and is used to access the layout’s variables and views.
This is a subclass of the Android ViewDataBinding class and will be named based on the layout filename using word capitalization and the Binding suffix. The binding class for a layout file named main_activity.xml file, therefore,will be named MainActivityBinding.
This class holds all the bindings from the layout properties (for example, the user variable) to the layout’s views and knows how to assign values for the binding expressions.
Create a binding object
Although the binding class is generated automatically, code still needs to be written to create an instance of the class based on the corresponding data binding layout file. Fortunately, this can be achieved by making use of the DataBindingUtil class.
The initialization code for an Activity or Fragment will typically set the content view or “inflate” the user interface layout file.
In the case of an existing Activity class, the code to achieve this can be found in the onCreate() method and will read as follows:
setContentView(R.layout.activity_main);
To create the binding class instances within an Activity class is to modify this initialization code as follows:
ActivityMainBinding binding; binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
If you are using data binding items inside a Fragment, ListView, or RecyclerView adapter, you may prefer to use the inflate() methods of the bindings classes or the DataBindingUtil class, as shown in the following code example:
FragmentMainBinding binding;
binding = FragmentMainBinding.inflate(layoutInflater, viewGroup, false);
//or
binding = DataBindingUtil.inflate(inflater, R.layout.main_fragment, container, false);
View view = binding.getRoot();
return view;
Data Binding Variable Configuration
The data binding layout file contains the data element which contains variable elements consisting of variable names and the class types to which the bindings are to be established. For example:
<data> <variable name="person" type="com.example.databindingdemo.User" /> </data>
The generated binding class has a setter and getter for each variable declared in the layout. The variables take the default managed code values until the setter is called—null for reference types, 0 for int, false for boolean, etc.
The person variable knows that it will be binding to an instance of a User class but has not yet been connected to an actual User object instance.
This is performed via a call to the setPerson() method(setter method of person variable) of the data binding instance as shown below:
ActivityMainBinding binding;
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
User user= new User("Arun","Chandravanshi");
binding.setPerson(user);
Binding Expressions
Binding expressions use a declarative language to define how a particular view interacts with bound objects.
Expressions can, for example, include mathematical expressions, method calls, string concatenations, access to array elements and comparison operations.
A binding expression begins with an @ symbol followed by the expression enclosed in curly braces ({}).
Consider, for example, a User instance containing variables named age, name. Assume that this class has been assigned to a variable named user within the data binding layout file and needs to be bound to TextView objects. If this value was stored as a String object, this would be declared within the layout file as follows:
<?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.databindingdemo.User" /> <variable name="handlers" type="com.example.databindingdemo.MyHandler" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_margin="50dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.age}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{handlers::onButtonClick}" android:text="CLICK" /> </LinearLayout> </layout>
String concatenation
String concatenation may also be used. For example, to include the word “years” after the age value the following expression would be used:
android:text='@{user.age+" years"}'
Note: Since the appended string is wrapped in double-quotes, the expression is now encapsulated with single quotes to avoid syntax errors.
ternary statements
The expression syntax also allows ternary statements to be declared. In the following expression, we are setting the visibility of a view to GONE or VISIBLE depending on the value of age.
android:visibility="@{user.age > 13 ? View.GONE : View.VISIBLE}"
Expressions may also be constructed to access specific elements in a data array:
@{user.nameArray[3]}
A binding expression on a Button, for example, might declare which method on an object is called in response to a click.
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{handlers::onButtonClick}" android:text="CLICK" />
Binding expressions provide a rich and flexible language in which to bind user interface views to data and methods in other objects and this article has only covered the most common use cases. To learn more about binding expressions, review the Android documentation.
Event and Listener Bindings
Not just the data, we can also bind the click and other events on UI elements. To bind a click event, you need to create a class with necessary callback methods.
Below we have a class that handles the button click event.
public class MyClickHandlers { public void onButtonClicked(View view) { Toast.makeText(getApplicationContext(), "button clicked!", Toast.LENGTH_SHORT).show(); } }
To bind the event, we use the same <variable> tag with the path to handler class. Below android:onClick=”@{handlers::onButtonClicked}” binds the button click to onButtonClicked() method.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="handlers" type="com.example.databindingdemo.MyClickHandlers" /> </data> <RelativeLayout ...> <Button ... android:onClick="@{handlers::onButtonClicked}" /> </RelativeLayout> </layout>
Another option, and one which provides the ability to pass parameters to the method, is referred to as a listener binding. The following expression uses this approach to call a method on the same handlers instance with no parameters:
android:onClick='@{() -> handlers.methodOne()}'
The following expression calls a method that expects three parameters:
android:onClick='@{() -> handlers.methodTwo(user.name, 10, "A String")}'
To bind the events, binding.setHandlers(handlers) is called from the activity.
Data Binding Basic Example
Let’s say we want to display user information from a User POJO class. We generally display the info in a TextView using setText() method. But instead of manually calling setText for each user property, DataBinding allows us to bind the values automatically.
The below POJO class creates an User object with firstName and lastName fields.
public class User { public final String firstName; public final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }
Data binding layout file
- To enable DataBinding in a layout, the root element should start with <layout> tag followed by <data> tag.
- Inside <data> tag, a <variable> tag takes two attributes `name` and `type`. name attribute will be alias name and type should be of object model class.
- To bind a value, @ annotation should be used. In the below layout, user firstName and lastName are bound to TextView using @{user.firstName} and @{user.lastName}.
<?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> <variable name="user" type="com.example.databindingdemo.User" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_margin="50dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="25sp" android:text="@{user.firstName}"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="25sp" android:layout_marginTop="15dp" android:text="@{user.lastName}"/> </LinearLayout> </layout>
- The expression @{user.firstName} used for the android:text attribute accesses the firstName field in the former class.
- The generated binding classes follow the naming convention considering the layout file name in which binding is enabled. For the layout activity_main.xml, the generated binding class will be ActivityMainBinding (Binding suffix will be added at the end).
Creating a Binding object
- To bind the data in UI, you need to inflate the binding layout first using the generated binding classes. Below, ActivityMainBinding inflates the layout first and binding.setUser() binds the User object to the layout.
You can notice here, we haven’t used findViewById() anywhere.
public class MainActivity extends AppCompatActivity { ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); binding = DataBindingUtil.setContentView(this,R.layout.activity_main); User user= new User("Arun","Chandravanshi"); binding.setUser(user); } }
If you run the app, you can see the user details displayed in TextViews.
That’s all for this article. In the next article, we will talk about how to implement recyclerView in our app using Data Binding library.