Android Data Binding - Christopher Schott / Florian Fetzer

slides at http://slidr.io/Plinzen/android-data-binding#1

  • binding between a data model and a view For Android a xml layout resource
  • it was presented at Google I/O 2015
  • The binding object is a completly automatically generated object which enables the view acces to the data model

What do I need:

  • Minimum API-Level 7 (2.1)
  • Android Studio 1.3.0-beta1 or higher
  • "com.android.databinding:dataBinder:1.0-rc0"
  • Apply databinding plugin -> apply plugin: 'com.android.databinding'

Simple Example:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
      <variablename="movie"type="com.kupferwerk.androiddatabinding.model.Movie“/>
   </data>
   <TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content“
      android:id=“@+id/titleandroid:text="@{movie.title}" />
</layout>

Start development with the creation of a new layout-xml-file. The root-tag of a layout-file with Android Data Binding support has to be a -Tag. It is followed by a -Tag and a View-Root-Element, which is in our small sample only a TextView. In the -section we can define variables, which define a property that maybe used within this layout. A variable has a name, to identify it in this layout and a type, which references to a Java-class in this case. Within the layout we can access the variable and its attributes with an @-sign followed by the attirbute in curly brackets.

private ActivityDetailBinding binding;
@Overrideprotected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  binding = DataBindingUtil.setContentView(this, R.layout.activity_detail);
  binding.setMovie(movie);
}

In the activity, we can now use the method DataBindingUtil.setContentView to set the root-layout of this activity. The class ActivityDetailBinding is auto-generated by the Android Data Binding library. The name of this Binding-class is generated from the XML-layout filename, by converting it to Pascal-Case and adding „Binding“ to it. Theses auto generated classes are placed in the package .databinding.Currently we were sometimes facing the problem that Android Studio will not find these classes. But the compilation of the Project runs successful and the app works. After changing something in the xml-layout the failures in Android Studio disappear, we observed. As last step we have to set the data model to the binding, to transfer the model data to the ui. As an additional hint: This binding class has also a reference to all UI-Elements with an ID. If you want to access any property of an UI-element, do not use findViewById. Instead use binding. to access the view.

Usage in Lists

@Override
public void onBindViewHolder(ViewHolder viewHolder, final int i) {
   viewHolder.binding.setMovie(movies.get(i));
}

@Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {   
   // inflate view
   ListItemBinding binding = DataBindingUtil.inflate(layoutInflater,R.layout.list_item, viewGroup, false);
   return new ViewHolder(binding);
}
  • In the method onCreateViewHolder the corresponding binding instance has to be created .
  • In onBindViewHolder the data model is set to the binding instance.

    public static class ViewHolder extends RecyclerView.ViewHolder {
     private final ListItemBinding binding;
    
     ViewHolder(final ListItemBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
     }
    }
    

    Imports

    <layout>
      <data>
          <import type="com.kupferwerk.androiddatabinding.DateUtils“/>
          <variable name="movie" type="com.kupferwerk.androiddatabinding.model.Movie“/>   
      </data>
      <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{DateUtils.formatDate(movie.publication)}“/>
    </layout>
    

    In the -section of a layout file we could define Imports. The import element works in the same way, as the import-Statement in Java. It eases the access of any Class. In this case we use the import-tag to make a DateUtils class available in the XML-Layout. In the TextView-Element we use a method of this class to format the Date-Object of your data model in the human readable version.

Custom Setter

public class BindingUtils {
    @BindingAdapter ({"bind:imageUrl"})
    public static void loadImage(ImageView view, String url) {
        Picasso.with(view.getContext()).load(url).into(view);
    }
}
<ImageView android:layout_width="match_parent" android:layout_height="wrap_content" app:imageUrl="@{movie.imageUrl}" />

Another cool features of Android Data Binding is the possibility to provide Custom Setters. In this sample we use the feature to load pictures via Picasso from the web. With the @BindingAdapter annotation we create a new property. The annotation has to be added to a method with the parameters View which can handle this property Property data type. The method handles the setting of this new property in the UI. Custom namespaces - “bind“ - are ignored during matching.

Observables

public class Movie extends BaseObservable {
   private String title;

   @Bindable
   public String getTitle() {
      return title;
   }

   public void setTitle(String title) {
      this.title = title;
      notifyPropertyChanged(BR.title);
   }
}

Android Data Binding also supports Observables. If an object attribute will be changed, it will be propagated to the bound UI elements immediately. There are several possibilities to create Observable classes. One version is to extend your model from BaseObservable, add the @Bindable annotation and call the notifiyPropertyChange-Method, if an attribute has been changed. The Id which has to be provide to this method will be given from the BR-class. All getter methods-annotated with @Bindable will be added to this BR class.

Supporting Ressources

It is supported to format strings and using plurals.

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

Expression Language

android:visibility="@{user.age < 13 ? View.GONE : View.VISIBLE}"
android:text="@{movie.genre ?? movie.title}"

You can also add some expressions to your layout:

  • Like the conditional parameter which can be really useful
  • Another expression you can add is the Null Coalescing Operator -> android:text="@{user.displayName != null ? user.displayName : user.lastName}"

Problems

  • IDE-Support currently poor
  • Error indicated, but it compiles and works
  • No code completion in layout
  • Only one way binding is currently implemented.
  • API not stable

References