Exploring The Data Binding Library For Android
All Android developers want to reduce their code size and want to make cleaner code, so this is necessary to bind your UI component to data.
In this article, learn how to set up your development environment to work with the Data Binding Library, including using Android Studio to write data binding code.
The Android data binding library gives us a way to bind data in an Android layout. It helps us remove all boilerplate codes findViewById() so you don’t have to manually update the views in code. So, in short, it helps us to speed up the process in android development.
What Is Android Data Binding?
The Android Data Binding library is a support library that lets you use a declarative style instead of programming to connect UI components in your layouts to data sources in your android app.
Data Binding in Android
As android developers, all are aware that we’re always looking for methods to make our code smaller and clearer. Aside from that, using boilerplate code like setText(), setImageResource(), and so on, you may populate your UI component with data.
There are many ways to avoid these things, and you could use the UI directly from a java or kotlin file, but the Kotlin extension is no longer supported.
ViewBinding and Data Binding are native Android features that allow you to access your UI components and link them directly to the data source.
- ViewBinding
ViewBinding is an Android feature that allows you to easily access any UI components via created Binding classes. When view binding is enabled in a build.gradle file, binding classes are generated, which include all UI component references.
If the layout file name is Cricket_Score.xml, then it will create its class with a name like CricketScoreBinding.
package com.example.databindingsample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.example.databindingsample.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main,)
}
}
In a Fragment, ViewBinding:
package com.example.databindingsample.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.example.databindingsample.databinding.FragmentSampleBinding
class SampleFragment : Fragment() {
private var _binding: FragmentSampleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
_binding = FragmentSampleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Databinding
Databinding is an Android support library that allows you to easily tie your UI components to data sources such as ViewModel.
What Are The Data Binding Types In Android?
There are two types of Data Binding: View binding and data binding. ViewBinding is an Android feature that allows you to use created Binding classes to directly access all of the UI components. Databinding is an Android support library that allows you to easily tie your UI components to data sources such as ViewModel. More details are added in the later article.
Android working with Data Binding
Android DataBinding is a means to connect the UI to business logic, allowing the user interface to update itself without the need for manual intervention. It reduces lots of boilerplate code.
Before starting, we have to enable DataBinding.
- Enabling DataBinding
To get started with this, open build.gradle and enable databinding under the android component.
app/build.gradle
android {
dataBinding {
enabled = true
}
compileSdkVersion 27
defaultConfig {
applicationId “info.latitudetechnolabs.databinding”
minSdkVersion 16
// ..
}
}
Now, enabling databinding in layout.
<layout …>
<data>
<variable
name=”…”
type=”…” />
</data>
<LinearLayout …>
<! — YOUR LAYOUT HERE →
</LinearLayout>
</layout>
The @ annotation should be used to bind a value. User name and email are tied to TextView using @user.name and @user.email in the layout below.
activity_main.xml
<?xml version=”1.0" encoding=”utf-8"?>
<layout xmlns:android=”http://schemas.android.com/apk/res/android">
<data>
<variable
name=”user”
type=”info.latitudetechnolabs.databinding.User” />
</data>
<LinearLayout 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”
android:orientation=”vertical”
android:padding=”@dimen/fab_margin”
app:layout_behavior=”@string/appbar_scrolling_view_behavior”
tools:context=”.MainActivity”
tools:showIn=”@layout/activity_main”>
<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.email}” />
</LinearLayout>
</layout>
Now, the databinding is integrated into the layout file now to rebuild the project so it can generate classes that are necessary. According to the naming standard for the layout file where binding is enabled, the produced binding classes are named according to the naming standard for the layout file. ActivityMainBinding will be the produced binding class for the layout activity main.xml.
To bind data in the UI, you must first inflate the binding layout using the produced binding classes.
ActivityMainBinding inflates and binds the layout initially. setUser() is a method that connects the User object to the layout.
MainActivity.java
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import info.latitudetechnolabs.databinding.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
user = new User();
user.setName(“Your username”);
user.setEmail(“Your Email”);
binding.setUser(user);
}
}
Now you can see that your user details will be shown in textviews.
<Include> layouts with DataBinding
Now we will enable databinding when we have layouts included.
To enable databinding, <layout> tag used in activity_main.xml. There are also <variable> and <data> tags that are used to bind the object.
activity_main.xml
<?xml version=”1.0" encoding=”utf-8"?>
<layout xmlns:bind=”http://schemas.android.com/apk/res/android">
<data>
<variable
name=”user”
type=”info.latitudetechnolabs.databinding.User” />
</data>
<android.support.design.widget.CoordinatorLayout 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”>
<android.support.design.widget.AppBarLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:theme=”@style/AppTheme.AppBarOverlay”>
<android.support.v7.widget.Toolbar
android:id=”@+id/toolbar”
android:layout_width=”match_parent”
android:layout_height=”?attr/actionBarSize”
android:background=”?attr/colorPrimary”
app:popupTheme=”@style/AppTheme.PopupOverlay” />
</android.support.design.widget.AppBarLayout>
<include
android:id=”@+id/content”
layout=”@layout/content_main”
bind:user=”@{user}” />
<android.support.design.widget.FloatingActionButton
android:id=”@+id/fab”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”bottom|end”
android:layout_margin=”@dimen/fab_margin”
app:srcCompat=”@android:drawable/ic_dialog_email” />
</android.support.design.widget.CoordinatorLayout>
</layout>
The <layout> tag is used once more in content main.xml to facilitate data binding. <data>, <variable>, and <layout> are necessary in parent and included layouts.
content_main.xml
<?xml version=”1.0" encoding=”utf-8"?>
<layout xmlns:android=”http://schemas.android.com/apk/res/android">
<data>
<variable
name=”user”
type=”info.latitudetechnolabs.databinding.User” />
</data>
<LinearLayout 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”
android:orientation=”vertical”
android:padding=”@dimen/fab_margin”
app:layout_behavior=”@string/appbar_scrolling_view_behavior”
tools:context=”.MainActivity”
tools:showIn=”@layout/activity_main”>
<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.email}” />
</LinearLayout>
</layout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
setSupportActionBar(binding.toolbar);
User user = new User();
user.setName(“Your username”);
user.setEmail(“Your email”);
binding.setUser(user);
}
}
Binding Click Listeners / Event Handling
Not only can we link data to UI elements, but we can also bind click and other events. You’ll need to develop a class with the appropriate callback methods to bind a click event.
A class that handles the FAB click event is shown below.
public class MyClickHandlers {
public void onFabClicked(View view) {
Toast.makeText(getApplicationContext(), “FAB clicked!”, Toast.LENGTH_SHORT).show();
}
}
We use the same <variable> tag with the path to the handler class to bind the event.
<layout xmlns:bind=”http://schemas.android.com/apk/res/android">
<data>
<variable
name=”handlers”
type=”info.latitudetechnolabs.databinding.MainActivity.MyClickHandlers” />
</data>
<android.support.design.widget.CoordinatorLayout …>
<android.support.design.widget.FloatingActionButton
…
android:onClick=”@{handlers::onFabClicked}” />
</android.support.design.widget.CoordinatorLayout>
</layout>
The method must return Boolean type instead of void.public boolean onButtonLongPressed()
Observables for UI Updating
Observables allow you to automatically synchronize the UI with the data without having to invoke setter methods. When the value of a property in an object changes, the UI will be changed. Extend the BaseObservable class to make the object observable.
The modified User class, which extends BaseObservable, is shown below. You’ll notice something interesting here. After assigning new values, notifyPropertyChanged is invoked.
package info.latitudetechnolabs.databinding;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
public class User extends BaseObservable {
String name;
String email;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
notifyPropertyChanged(BR.email);
}
}
Using ObservableFields to update the UI
You can use ObservableFields to update the UI if your object class has fewer properties to update or if you don’t want to observe every field in the object. You can specify the variable as an ObservableField, and the UI will be changed when new data is entered.
ObservableFields can be used to modify the same User class, as seen below.
package info.latitudetechnolabs.databinding;
import android.databinding.ObservableField;
public class User {
public static ObservableField<String> name = new ObservableField<>();
public static ObservableField<String> email = new ObservableField<>();
public ObservableField<String> getName() {
return name;
}
public ObservableField<String> getEmail() {
return email;
}
}
Instead of utilizing the setter method to change the values, you must directly assign a new value to the property.
public class MyClickHandlers {
Context context;
public MyClickHandlers(Context context) {
this.context = context;
}
public void onFabClicked(View view) {
user.name.set(“NAME”);
user.email.set(“EMAIL”);
}
}
Java Function Binding (Import)
Java functions can also be bound to UI components. If you want to do something with the value before displaying it on the UI, you can easily do so with the <import> tag.
public class BindingUtils {
public static String capitalize(String text) {
return text.toUpperCase();
}
}
To use this method in your layout, use the <import> tag to import the class, then call the method on the attribute.
<?xml version=”1.0" encoding=”utf-8"?>
<layout xmlns:android=”http://schemas.android.com/apk/res/android">
<data>
<import type=”info.latitudetechnolabs.databinding.BindingUtils” />
</data>
<LinearLayout …>
<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@{BindingUtils.capitalize(user.name)}” />
</LinearLayout>
</layout>
Explore How To Add Data Binding To Your Project
Data binding is a support library that works with Android 4.0 (API level 14) and higher devices. Here is the step by step procedure on how to add data binding in your project.
- In Your Module’s build.gradle File, Enable Data Binding
buildFeatures {
dataBinding true
}
- Make your Activity and Fragment as shown below
<?xml version=”1.0" encoding=”utf-8"?>
<layout>
<data>
<variable
name=”characterViewModel”
type=”com.example.databindingsample.ui.randomcharacter.CharacterViewModel” />
<import type=”android.view.View” />
</data>
<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=”.ui.randomcharacter.CharacterFragment”>
<androidx.recyclerview.widget.RecyclerView
android:id=”@+id/rvCharacters”
android:layout_width=”match_parent”
android:layout_height=”0dp”
android:orientation=”vertical”
app:layoutManager=”androidx.recyclerview.widget.LinearLayoutManager”
app:layout_constraintBottom_toBottomOf=”parent”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent”
tools:itemCount=”6"
tools:listitem=”@layout/item_character” />
<ProgressBar
android:id=”@+id/pbCharactersLoading”
android:layout_width=”@dimen/dp_50"
android:layout_height=”@dimen/dp_50"
android:visibility=”@{characterViewModel.isLoading ? View.VISIBLE : View.GONE}”
app:layout_constraintBottom_toBottomOf=”parent”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent” />
<androidx.appcompat.widget.AppCompatTextView
android:id=”@+id/tvContentStatus”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@string/no_data_found”
android:textColor=”@color/black”
android:textSize=”@dimen/sp_15"
android:visibility=”@{characterViewModel.contentStatus <= 0 ? View.VISIBLE : View.GONE}”
app:layout_constraintBottom_toBottomOf=”parent”
app:layout_constraintEnd_toEndOf=”parent”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent” />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Credit: fragment_character.xml hosted by GitHub
Bind your ViewModel to CharacterFragment at this point.
package com.example.databindingsample.ui.randomcharacter
import com.example.databindingsample.R
import com.example.databindingsample.common.extensions.safeObserve
import com.example.databindingsample.common.extensions.viewModel
import com.example.databindingsample.data.randomcharacter.entity.CharacterItem
import com.example.databindingsample.databinding.FragmentCharacterBinding
import com.example.databindingsample.domain.base.UiState
import com.example.databindingsample.ui.base.BaseViewModelFragment
import dagger.hilt.android.AndroidEntryPoint
import timber.log.Timber
import java.util.*
@AndroidEntryPoint
class CharacterFragment :
BaseViewModelFragment<CharacterViewModel, FragmentCharacterBinding>(R.layout.fragment_character) {
private val TAG = “CharacterFragment”
private val characterAdapter by lazy {
CharacterAdapter()
}
override fun buildViewModel(): CharacterViewModel = viewModel(viewModelFactory)
override fun getClassName(): String = “CharacterFragment”
override fun initViews() {
super.initViews()
setUpViewModel()
setUpRecyclerView()
viewModel.getCharacters(1)
}
private fun setUpViewModel() {
binding.characterViewModel = viewModel
binding.lifecycleOwner = this
}
private fun setUpRecyclerView() {
with(binding) {
rvCharacters.adapter = characterAdapter
}
}
override fun initLiveDataObservers() {
super.initLiveDataObservers()
with(viewModel) {
charactersLiveEvent.safeObserve(viewLifecycleOwner, ::handleCharacterResponse)
}
}
private fun handleCharacterResponse(uiState: UiState<ArrayList<CharacterItem>>) {
when (uiState) {
is UiState.Success -> {
characterAdapter.addAll(uiState.data)
}
is UiState.Error -> {
Timber.e(uiState.throwable)
}
}
}
}
CREDIT: CharacterFragment.kt hosted by GitHub
The item charcter.xml for the Recyclerview item can be found here.
<?xml version=”1.0" encoding=”utf-8"?>
<layout>
<data>
<variable
name=”character”
type=”com.example.databindingsample.data.randomcharacter.entity.CharacterItem” />
</data>
<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:id=”@+id/charItem”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:background=”?android:selectableItemBackground”
android:clickable=”true”
android:focusable=”true”
android:padding=”@dimen/dp_10">
<androidx.appcompat.widget.AppCompatImageView
android:id=”@+id/ivCharacterProfile”
android:layout_width=”@dimen/dp_100"
android:layout_height=”@dimen/dp_100"
android:scaleType=”centerCrop”
android:src=”@drawable/avatar”
app:layout_constraintStart_toStartOf=”parent”
app:layout_constraintTop_toTopOf=”parent”
app:profileImageUrl=”@{character.image}” />
<androidx.appcompat.widget.AppCompatTextView
android:id=”@+id/tvCharacterName”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginStart=”@dimen/dp_10"
android:text=”@{`Name : ` + character.name}”
android:textColor=”@color/black”
android:textSize=”@dimen/sp_20"
app:layout_constraintStart_toEndOf=”@id/ivCharacterProfile”
app:layout_constraintTop_toTopOf=”@id/ivCharacterProfile”
tools:text=”Devid” />
<androidx.appcompat.widget.AppCompatTextView
android:id=”@+id/tvGender”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginTop=”@dimen/dp_5"
android:text=”@{`Gender : ` + character.gender}”
android:textColor=”@color/black”
android:textSize=”@dimen/sp_20"
app:layout_constraintStart_toStartOf=”@id/tvCharacterName”
app:layout_constraintTop_toBottomOf=”@id/tvCharacterName”
tools:text=”Devid” />
<androidx.appcompat.widget.AppCompatTextView
android:id=”@+id/tvStatus”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_marginTop=”@dimen/dp_5"
android:text=”@{`Status : ` + character.status}”
android:textColor=”@color/black”
android:textSize=”@dimen/sp_20"
app:layout_constraintStart_toStartOf=”@id/tvCharacterName”
app:layout_constraintTop_toBottomOf=”@id/tvGender”
tools:text=”Devid” />
<androidx.appcompat.widget.AppCompatImageView
android:id=”@+id/ivStatus”
android:layout_width=”@dimen/dp_15"
android:layout_height=”@dimen/dp_15"
android:layout_marginStart=”@dimen/dp_10"
android:background=”@drawable/shape_status”
android:backgroundTint=”@{character.status.equalsIgnoreCase(`Alive`) ? @color/green : @color/red }”
android:elevation=”@dimen/dp_10"
app:layout_constraintBottom_toBottomOf=”@id/tvStatus”
app:layout_constraintStart_toEndOf=”@id/tvStatus”
app:layout_constraintTop_toTopOf=”@id/tvStatus” />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
CREDIT: item_character.xml hosted by GitHub
Here <data> is used to bind UI with data
<variable> is used to add the name and type that you wish to add and bind to UI.
Binding Data to XML file
package com.example.databindingsample.ui.randomcharacter
import android.view.LayoutInflater
import android.view.ViewGroup
import com.example.databindingsample.data.randomcharacter.entity.CharacterItem
import com.example.databindingsample.databinding.ItemCharacterBinding
import com.example.databindingsample.ui.base.BaseRecyclerViewAdapter
import com.example.databindingsample.ui.base.BaseRecyclerViewHolder
class CharacterAdapter :
BaseRecyclerViewAdapter<CharacterItem, CharacterAdapter.CharacterViewHolder>() {
override fun createItemViewHolder(
parent: ViewGroup,
viewType: Int
): CharacterAdapter.CharacterViewHolder {
return CharacterViewHolder(
ItemCharacterBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun bindItemViewHolder(holder: CharacterAdapter.CharacterViewHolder, position: Int) {
holder.bind(items[position])
}
inner class CharacterViewHolder(
private val itemCharacterBinding: ItemCharacterBinding
) : BaseRecyclerViewHolder<CharacterItem>(itemCharacterBinding.root) {
override fun bind(item: CharacterItem) {
with(itemCharacterBinding) {
character = item
}
}
}
}
Credit: CharacterAdapter.kt hosted by GitHub
Binding Adapter:
Binding Adapters are used to add custom setters to certain of your views’ properties. Setting an image to an ImageView, where the image is primarily loaded off the UI thread, is the most common use case I can think of.
package com.example.databindingsample.util
import androidx.appcompat.widget.AppCompatImageView
import androidx.databinding.BindingAdapter
import com.bumptech.glide.Glide
import com.example.databindingsample.R
object GlideUtils {
@JvmStatic
@BindingAdapter(“profileImageUrl”)
fun profileImageUrl(imageView: AppCompatImageView, url: String?) {
if (url?.isNotEmpty() == true) {
Glide.with(imageView.context)
.load(url)
.placeholder(R.drawable.avatar)
.into(imageView)
}
}
}
Credit: GlideUtils.kt hosted by GitHub
Final Words
By reading the whole article, you may now have enough information about android data binding. It may take a long for some and take very little time for some developers in android data binding. You can reach us at latitude technolabs for any support related to it. We have dedicated experts for android development.