Epoxy — A proxy for Adapter and complex view types!

Ashwin S
AndroidPub
Published in
4 min readJan 4, 2019

TL;DR I will be running through on how AirbnbEng’s Epoxy library helped me to bypass the need for adapters, view-holder classes, and the Diff Utils.

On a bright sunny day, I was sitting in front of my computer facing this exception,

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder

Well, the next thing I can do is to check if I am notifying the changes in the right places for the right items or go for a notify dataset change. Going with the first solution, you had to keep track of all the changed items and the second one gives you a bad UX. The next thing that came to my mind is.
Well, you guessed it right. The DiffUtil!

My problem is now a thing of the past. But wait, I forgot to describe the layout affected. It was a maintaining a feed listing and a comments thread for the feed with one or more reply threads to the comment. To put it in short, a multiple view type patient. And all I had was this generic object, each with its own properties for which I had to write the diff calculation manually. As if this wasn’t enough, I also had to add support for attachments. I had to start the process all over again with a new adapter, a new view holder and more diff calculations. I had to keep track of all these changes which I made to the list for the new feature, to the DiffUtil class and the adapter as well. From being a developer who had to worry about the business logic, I had to ensure that the UX doesn’t break by bad diffing and also worry about maintaining such a complex adapter. And Epoxy came to the rescue! With Epoxy and Data binding, all I needed was the layouts for the different view types and the list. So being a bit more greedy, I decided to rewrite the code a little so that I can sit back relaxed in the future only worrying about the new logic I had to implement.

Note: I am going to stick with the laziest way of implementing Epoxy (which needs Data-binding) in my project which drastically brings down the boiler-plate for this blog.

I started this off by adding the latest dependency,

implementation 'com.airbnb.android:epoxy:2.17.0'

For the Kotlin buffs, you may need an annotation processor which comes with Kotlin kapt,

kapt 'com.airbnb.android:epoxy-processor:2.17.0'

Going forward, I am going to stick with Kotlin.

Before proceeding to the next step, ensure that you have data binding enabled in your project.

Now let’s take a look at one of the single item layouts which I implemented for the complex view,

I have defined the data-variables to populate the view with the data from a Content data class, to navigate to a comments view and to increase the likes.

Now for the fun part,

Define a package-info.java file and add the below magical lines!

@EpoxyDataBindingPattern(rClass = R.class, layoutPrefix = "view_holder") 
//@EpoxyDataBindingLayouts({R.layout.header_view})
package com.example.epoxydemo;import com.airbnb.epoxy.EpoxyDataBindingLayouts;
import com.airbnb.epoxy.EpoxyDataBindingPattern;
  • EpoxyDataBindingLayouts annotation helps you specify the layouts for which you need to generate the View Holder classes.
  • EpoxyDataBindingPattern annotation helps you to generate view holders for the layouts with a particular name pattern.

In my case, I added these three layouts view_holder_feed_item.xml, view_holder_comment_item.xml, and view_holder_reply_comment_item.xml and all I had to do to generate the view holder for me was to specify the prefix pattern “view_holder” in the annotation.

Build the project. Et-voila! Your view holder classes are ready.

Now that we are done with the layouts and having already defined the data class for the Content, All we need to do is to add the EpoxyRecyclerView, get your list ready(contentList) and build the logic inside,

recycler_view.buildModelsWith { controller ->
run{...}
}

with

  • FeedItemBindingModel_ — The View Holder class generated corresponding to the layout view_holder_feed_item.xml.
  • id() — to provide the stable IDs which helps the Epoxy Model to differentiate the items added.
  • content(), listener(), onLiked() — generated methods based on the data variables in the layout.
  • addTo() — Adds the View Holder object to the EpoxyController which takes care of laying the objects and subsequent diffing for changes in the list.

Note that the single item views get added in the order of calling the addTo() for the individual list items.

And after I do the same with the Comments and Reply Comments, we get an app which kind of looks like,

The result 😍

--

--