Prasham Trivedi

Working With databinding

February 03, 2016

6 minutes to read.

Databinding is a powerful tool. It not only reduces boilerplate coding but it also helps us to deliver testable code very fast. I gave a talk to my team regarding to databinding tool. Here is the slideshow and talk excerpt.

Slides

Slides 1-7 (MV what???)

There are many programming platforms where MVC is main part of their daily workflow. Infact there are requirement to know and able to work with MVC before you even make to the interviews. MVC stands for Model, View and Controller.

In MVC, Model stands for your business data. It can be anything, your user’s information, metadata about a movie, data about a restaurant. It represents your business entities. View stands for what your end user can see. This is exactly your screen or bunch of screens. And Controller -as name suggests- controls a) How data comes to your view, b) Based on data- state of your views and c) Navigation of the screen based on data.

In Android we are having MVC since (probably) 1.0. As a Model we can use (almost) same java model that are used in Server side or desktop java projects. Activities (and since 4.0 ICS-fragments) can work as our views along side custom views and XML.#1. Along side views, Activities and Fragments can also work as controller to determine view states, and to controll navigation.

Traditionally MVC leaves too much work to do on controllers. They are responsible for both handling view states and handling navigations as well. That makes this class bigger and more difficult to test. For mobile Controllers also decide how to get data and how to save, which makes our Activities more bigger and difficult to maintain. That’s where a new framework comes into picture.MVVM, which stands for Model, View and ViewModel.

ViewModels take responsibility of handling view states from Controllers. They can also take responsibility of getting data from controller but this thing is less desired considering Android. View Models can easily define rule of handling state of your views without touching your original model, e.g. greying out closed business, making selected seats green, or hide a textview if it’s related information is not present. Making our traditional controllers more easy to read and maintain.

But they don’t replace role of activities and fragments entirely. They still are (and should be) responsible and useful for getting data from network/local storage, determine next screen etc. And Databinding comes handy to use MVVM pattern in android very easily.

Slides 8-20 (Databinding Introduction)

Databinding is introduced in Google I/O 2015 as standalone plugin. It started shipping (probably) with Android Plugin v 1.3.0 and Since 1.5.0 this plugin was part of Standard android plugin. Previously we need to define additional plugin to use databinding. But from 1.5.0 we have to say dataBinding {enabled = true} in android{} block of our build.gradle. Here is the sample

Before we had databinding, our layout xmls, had all the ids which we want to handle#2. And in Java we have to 1. Call those ids with findViewById calls, 2. Cast those generic views into the actual views. This thing was wrong on many levels. To succeed we need to ensure that all the ids we have are present in the xml file, and in the xml they have same class that we have in Java to prevent ClassCastException#3. Though ButterKnife does findViewById and casting on its own but it may fail on reasons stated above.

Just for example, before we had databinding, our xmls were looked like the one shown below.

And our java files looked like this one

Once you convert your normal xmls and javas to databinding, they will look like the samples shown below.

The xmls will look like

And our java files will look like

Here are the summary of what we have changed.

  1. In xml we have surrounded our actual view with <layout> tag, and this will become our root tag.
  2. In this layout tag, we have defined a <data> tag, which has <variable> tag.
  3. This variable tag will have two properties, Variable name: Which will be used as actual variable name and type: Which will be fully qualified class name, from where some properties will be binded.
  4. If we want to bind a property to any view attribute, we will use @{} syntax. Where we will write @{name.property}, and this property will be bound directly if there is any direct setter supplied.

The example of xml is shown here - (Credits - Official databining guide)

For Java side, following changes are required

  1. Instead of regular setContentView (or Inflater.inflate()) calls, we use DataBindingUtil.setContentView() call.
  2. This will return a generated binding class.
  3. This class is actually named as a Pascal case converted layout file + Binding added as suffix, quoting adventures-databinding-4 post

By default, a Binding class will be generated based on the name of the layout file, converting it to Pascal case and suffixing “Binding” to it. Pascal Case (by definition of MSDN) is The first letter in the identifier and the first letter of each subsequent concatenated word are capitalized. For example if our layout file is named layout_user_details.xml , converting it to Pascal case is LayoutUserDetails and suffixing Binding makes it LayoutUserDetailsBinding.

The changes in activity should be as shown here.

  1. If layout binding has EditText with id userName, Variable of that binding class will have a field called userName which will be EditText
  2. To bind your Java class to layout, binding will have a setNAME() method which will map your object with databinding’s name. For each variable called name in xml tag, there will be `setName()` method in binding class.
  3. The object you pass for databinding must have public getter of a property for each @{name.property} written in your layout.

Your ideal model for above two examples are shown here.

Once this thing is setup, we can use basic databinding working, which can use your java module to shown data into xml. Which is called one way binding.

Slides 21-rest (Advanced Databinding)

One way binding is good to have a read-only data. But with this framework we can not have form input and validation, also it will be difficult to control view states based on user input. With little tweaks here and there, we can use databinding to form input and validation.

Two way databinding will work with Observable pattern, and in databinding library, we already have couple of classes that support some of primitive types with Observable pattern.(#4)[#4]. They will have setters and getters, and they will update their value in almost real time. There are observables for all the primitive types. By default they don’t have Observable Class for string. To use observable with string, I am using this class.

You can use two way binding in layout with following syntax

Note that we have used binding attribute, with @{eventViewModel.toAllDay} which returns BindableBoolean object. Note that switch compat does not have a binding property to get or set. This is a custome property, that’s the reason why we have added app:binding instead of android:binding. By default our system does not know what to do with this attribute. So to let our system know, we have to write a binding adapter, which will listen to this binding attribute and change accordingly. One example BindingAdapter looks like below.

The BindingAdapter will have collection of static methods, which can be used to handle some custom properties of the views. Each static method will have first argument of a view, this will be the same class or super class of the view where the properties are required to change. Note that we have passed CompoundButton instead of SwitchCompat, which will allow me to use same binding logic for any class which extends CompoundButton.

This binding methods will also allow method overloading. That means I can write same binding property for Button, EditText and RatingBar, and can write three different methods with same @BindingAdapter({"binding"}) annotation, and I will be sure that I will execute these binding correctly. The below gist shows how this can be achieved.

Just like multiple binding methods for same annotation is possible, you can write same methods to bind more than one properties. In twoway-binding.java example, there is a method called changeViewDependency which listens to visibleIf and inverse properties at the same time. If you write Binding Adapter method for multiple properties, all properties are required to be there in XML. If any of them is missing, your code will stop compiling. If you want them optional, you can write requireAll = false, to make all properties optional#5.

There are some more points to take care with binding adapters. First, you don’t need to have binding adapter class in all your layout xmls. Databinding plugin will look methods with @BindingAdapter annotations automatically. That makes all binding logic a centralized code in your project. So you have to be extra careful to provide multiple bindings with same name and same data type on same view. If you’re handling Lazy image loading where at once place your error drawable comes from resource and at other place it will be a URL, it is desired to have a single Binding method to handle both with extra care.

THE END.

Further reading and source

I have covered all the talks and some questions I have faced in this post. This talk was based on my programming experiences after reading following posts and following their examples.

To know how powerful databinding is, I leave you to read below examples.

  • Custom fontfaces using databinding - Lisa Wray - G+ post

  • Visibility using Boolean - Lisa Wray - G+ post

  • Apply Tints - Louis G Vale - G+ post

  • Datepicker databinding - George Mount - Blog post

  • RTL support with padding - Prasham Trivedi - G+ post

Footnotes

  1. Well Activities and Fragments should never be views but you can use those classes to define View classes and inflate them as we want. That's why they are part of views for me.
  2. Defining ids for every view is good, because system will save view states with ids in `saveInstanceState` and reuse it if activity-fragment is restarted.
  3. If we have multiple xmls across different configuration (for example `layout` and `layout-land`). We have to ensure ids are present in both the file with correct class.
  4. Observable from RxJava and observable from Databinding are two different things, they can co-exist, but not to be mixed.
  5. This is take all or none scenario, you either require all the attributes, or you either have all-but-one attributes missing. Be careful with requireAll=false.
comments powered by Disqus