Android: View Binding v/s Data Binding
--
In the previous article I had talked about the benefits of using View Binding over findViewById(). I highly recommend you give it a read before going through this article.
In this article I will be talking about Data Binding and the main differences between View Binding and Data Binding and when either of them should be used in your projects.
Assuming you have already gone through View Binding, I will jump straight to Data Binding, it’s implementation and then get to the differences between View Binding and Data Binding.
Data Binding
The Data Binding Library is a support library that allows you to bind UI components in your layouts to data sources in your app using a declarative format rather than programmatically.
To enable Data Binding add the following snippet in the module level build.gradle file:
Enabling Data Binding :
android {
... buildFeatures {
dataBinding true
}
}
Generating Binding classes
Unlike View Binding, where binding classes are automatically generated for all layout files in your project once you have enabled View Binding, with Data Binding you will have to explicitly convert each layout into Data Binding layout to generate Data Binding classes.
To convert a layout into a Data Binding layout, go to the parent layout and press Option + return button, while will open a menu. From the menu presented select Convert to data binding layout and your view will be converted to a Data Binding layout.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="mainViewModel"
type="com.anubhav.data.ViewModel" />
</data>
<ConstraintLayout... /> <!-- UI layout's root element -->
</layout>
The binding variables that can be used in expressions are defined inside a data element that is a sibling of the UI layout’s root element. Both elements are wrapped in a layout tag. After this you can use the defined variable to set values to the UI elements using the declarative format.
Binding Data
A binding class is generated for each layout file. By default, the name of the class is based on the name of the layout file, converting it to Pascal case and adding the suffix Binding to it. Say, the above layout file’s name is activity_main.xml so the corresponding generated class is ActivityMainBinding. This class holds all the bindings from the layout properties to the layout's views and knows how to assign values for the binding expressions.The recommended method to create the bindings is to do it while inflating the layout, as shown in the following example:
class MainActivity : AppCompatActivity() { private val mainViewModel by viewModels<MainViewModel>() override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.mainViewModel = mainViewModel
}
}
Now in the layout file,
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="mainViewModel"
type="com.anubhav.data.ViewModel" />
</data>
<ConstraintLayout... > <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{mainViewModel.name}"/> </ConstraintLayout>
</layout>
If you are using data binding items inside a Fragment, ListView, or RecyclerView adapter, you should use inflate method of the bindings classes or the DataBindingUtil class,
val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)// orval listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
Now there can be scenarios where you cannot directly set a property to a view using the declarative format, for instance you might want to say load an image into an ImageView via a url, or set a view’s colour depending on a specific result. Data Binding provides us with Binding Adapters to accommodate such scenarios.
Binding Adapters
Binding Adapters are responsible for making the appropriate framework calls to set values. Whenever a bound value changes, the generated binding class must call a setter method on the view with the binding expression. You can allow the Data Binding Library to automatically determine the method, explicitly declare the method, or provide custom logic to select a method. A usual Binding Adapter method is a just a static method, and performs data conversion and injection for a specific custom attribute in a layout file. A usual Binding Adapter class will look like,
class DataRowBinding {
companion object {
@JvmStatic
@BindingAdapter("loadImageFromUrl")
fun loadImageFromUrl(view: ImageView, imageUrl: String) {
view.load(imageUrl) {
placeholder(R.drawable.ic_error_image)
crossfade(600)
error(R.drawable.ic_error_image)
}
}
}
The name of the custom property is defined like this,
@BindingAdapter(“loadImageFromUrl”)
Now you can use this property, loadImageFromUrl within @BindingAdapter(“loadImageFromUrl”) to set the value inside the layout element. I like to keep the name of the method same as that of the custom attribute which I have defined.
@JvmStatic
Specifies that an additional static method needs to be generated from this element if it’s a function. If this element is a property, additional static getter/setter methods should be generated.
Now you can use the custom attribute defined in the binding adapter like,
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="mainViewModel"
type="com.anubhav.data.ViewModel" />
</data>
<ConstraintLayout... ><ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
loadImageFromUrl="@{mainViewModel.imageUrl}"/></ConstraintLayout>
</layout>
If you are using a LiveData object with your binding class, you need to specify a lifecycle owner to define the scope of the LiveData object.
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.main)// Specify the current activity as the lifecycle owner.binding.setLifecycleOwner(this)
And you are good to go.
Differences between View Binding and Data Binding
- View Binding library is faster than Data Binding library as it is not utilising annotation processors underneath, and when it comes to compile time speed, View Binding is more efficient.
- The one and only function of View Binding is to bind the views in the code, while Data Binding offers some more options like Binding Expressions which allows us to write expressions the connect variables to the views in the layout.
- Data Binding library works with Observable Data objects, you don’t have to worry about refreshing the UI when underlying data changes.
- Data Binding library provides us with Binding Adapters.
- Data Binding library provides us with Two way Data Binding, this is a technique of binding your objects to xml layouts, so that both object and layout can send data to each other.
In short, there is nothing View Binding can do that Data Binding cannot,(though at the expense of longer build times) and there is a lot that Data Binding can do that View Binding cannot do.
In fact you can use both the libraries in the same project, using Data Binding for advanced use cases, and View Binding for normal use cases.
That is all folks, hope you learnt something today !