Skip to content

Data Binding Support

Eli Hart edited this page Oct 18, 2018 · 21 revisions

Epoxy integrates with Android data binding to automatically generate EpoxyModels from databinding xml layouts.

To enable data binding support, add the epoxy databinding module as a dependency:

dependencies {
  compile 'com.airbnb.android:epoxy-databinding:2.6.0' // (or latest Epoxy version)
}

Also make sure to enable Android data binding in your build.gradle file.

android {
  dataBinding {
    enabled = true
  }
}

Automatic Model Generation

Epoxy can generate an EpoxyModel for each of your data binding layouts.

Models are generated in the root package of your module. Each model will have a setter for each of the variables in the layout, and each variable will be bound when the model is bound.

The generated model name is created by camel casing the layout file name and appending BindingModel_. The layouts must not specify a custom data binding class name or package via the class="com.example.CustomClassName" override in the layout xml.

Declaring databinding layouts

There are two ways to tell Epoxy which layouts you want models generated for:

Automatic based on naming pattern

Create a package-info.java file in any package and annotate it with EpoxyDataBindingPattern. Add your R class as an argument, as well as a prefix that all of your databinding layouts share.

Epoxy will attempt to generate a databinding model for every layout file that starts with the given prefix.

For example,

@EpoxyDataBindingPattern(rClass = R.class, layoutPrefix = "view_holder")
package com.example.package;

Your layouts would then be named like view_holder_list_item.xml, which would lead to a ListItemBindingModel_ model class being generated.

Explicitly

Create a package-info.java file in any package and annotate it with EpoxyDataBindingLayouts. Then include your layout file names in the annotation.

For example,

@EpoxyDataBindingLayouts({R.layout.header_view})
package com.example.package;

Detecting changes in data

Generally all data variables must implement equals and hashcode so Epoxy can detect when they change. Each variable is rebound individually when it changes. Read more about this under the Model Properties section.

For any variable that does not implement equals and hashcode Epoxy applies its Do Not Hash option by default. This default is generally helpful for click listeners, but there are some caveats to the behavior that should be understood to avoid bugs.

To disable this default you can set enableDoNotHash in your EpoxyDataBindingLayouts and EpoxyDataBindingPattern annotations to false.

Custom Data Binding Models

You may also subclass DataBindingEpoxyModel directly if you want to handle binding manually, although this should rarely be needed.

You can create a simple model like this:

@EpoxyModelClass(layout = R.layout.model_button)
public abstract class ButtonModel extends DataBindingEpoxyModel {
  @EpoxyAttribute @StringRes int textRes;
  @EpoxyAttribute(DoNotHash) OnClickListener clickListener;

  @Override
  protected void setDataBindingVariables(ViewDataBinding binding) {
    binding.setVariable(BR.textRes, textRes);
    binding.setVariable(BR.clickListener, clickListener);  }
  }

The setDataBindingVariables method will be called when the model is bound, giving you a chance to bind your data to the view. Alternatively you can leave the setDataBindingVariables method unimplemented and Epoxy will include a default implementation for you that binds each EpoxyAttribute field to a variable of the same name.

This simplies the model to just:

@EpoxyModelClass(layout = R.layout.model_button)
public abstract class ButtonModel extends DataBindingEpoxyModel {
  @EpoxyAttribute @StringRes int textRes;
  @EpoxyAttribute(DoNotHash) OnClickListener clickListener;
}

In this case the generated implementation of setDataBindingVariables would be exactly the same as implemented earlier. The generated code will also include a setDataBindingVariables implementation that only updates a variable if it changed from the previously bound model.

For example:

@Override
  protected void setDataBindingVariables(ViewDataBinding binding, EpoxyModel previousModel) {
    ButtonModel_ that = (ButtonModel_) previousModel;
    if (textRes != that.textRes) {
      binding.setVariable(BR.textRes, textRes);
    }
    if ((clickListener == null) != (that.clickListener == null)) {
      binding.setVariable(BR.clickListener, clickListener);
    }
  }