Android: Epoxy vs. Standard RecyclerView with Multiple View Types

Matthew Meehan
5 min readMar 10, 2021

--

Image by Sergei Tokmakov

RecyclerViews are an essential component of any android app; they display lists of data to the user in “recycled” viewHolders. Basic usage of a RecyclerView generally leverages only a single type of viewHolder. However, in order to create more complex views, a recyclerView can be made to handle multiple different viewHolders. In an effort to demonstrate this concept, two apps were prepared: one using the “traditional” approach with no 3rd party dependencies and the other using Airbnb’s Epoxy Library. Information on Epoxy can be found here.

First, let’s take a look at the “traditional” app, affectionately named the Multi Recycler App. This app will contain a single recyclerView to hold two different types of viewHolders for fake news articles, and one viewHolder for the section header. To get started, an enum class was created containing the types needed to describe each of these items.

As seen here, three different types of items will be used in the app: a HEADER item which will contain only text, a HEADLINE article which will contain a title and brief description, and a PHOTO article which will contain a photo, title, and a description. To ensure each of these items are assigned a type, an interface was created with a type field.

Each of these items would now inherit from the base model and contain an ArticleType. Now to create models to contain the desired data.

Next the layouts for each of these models would need to be assembled. Note that it is not necessary to follow these layouts exactly; they are just included to provide context and to indicate that databinding is being leveraged to bind the model to the view.

With these components finished, it’s time to get started on the tricky part. The main difference between the “traditional” approach and the Epoxy approach is the RecyclerView Adapter. The role of a RecyclerView Adapter is to provide the binding from the data to the views in a RecyclerView. Therefore, in this app, the heavy-lifting for handling multiple viewHolder types is contained in the Adapter logic. The key is to override the function getItemViewType(int position), which returns the type of the item at the passed in position for the purpose of view recycling. By default this returns the integer 0, assuming that the adapter only contains a single viewType. By overriding this with the ArticleTypes defined earlier (which can be identified by their ordinal, or position within the enum) the Adapter will now pass different viewTypes to onCreateViewHolder, where it can be used to distinguish which article viewHolder to display.

This adapter is where the real magic is happening. The ArticleTypes from each item in the data are being returned from get ItemViewType to identify the three different viewHolders in onCreateViewHolder. Each viewHolder is created with its corresponding itemBinding, and finally in onBindViewHolder, the data item from the list is being databound to the view. By adding test data and setting this adapter to the recyclerView, the final product would look something like this.

Pretty cool right? Not bad for not using any libraries to help.

Now to examine the Epoxy approach. Epoxy is a library developed by Airbnb to streamline this exact use case; too often would the developers find themselves rewriting complex, boilerplate adapter code so that their recyclerViews could handle multiple view types. Epoxy leverages two simple components: the EpoxyModel’s that describe how views should be displayed in a recyclerView, and the EpoxyController to describe what items to show with what data.

First things first, let’s take a look at EpoxyModels. EpoxyModels can be created programmatically from custom views or viewHolders using annotations, but for this example the databinding approach will be taken. To add Epoxy to the project with databinding support, these dependencies will need to be put in build.gradle. Replace the $epoxyVersion with the latest version of Epoxy.

implementation 'com.airbnb.android:epoxy:$epoxyVersion'
implementation 'com.airbnb.android:epoxy-databinding:$epoxyVersion'
kapt 'com.airbnb.android:epoxy-processor:$epoxyVersion'

To begin using Epoxy in conjunction with databinding, Epoxy needs to identify which of the layouts in the project are Models. This can be achieved by annotating any class with EpoxyDataBindingPattern and defining your layoutPrefix like so.

Note this is a java file, as currently epoxy cannot process this annotation in Kotlin. In this example app, the prefix is denoted as “viewholder_”, which means that layout file names beginning with this prefix will be generated into EpoxyModels. Accordingly, the layouts viewholder_article_header.xml, viewholder_headline_article.xml, and viewholder_photo_article.xml from before will be copied to this app and have EpoxyModels automatically generated.

And that’s all there is to it! The models can now be added to Epoxy’s EpoxyRecyclerView by calling withModels to use the view’s EpoxyController.

binding.recycler.withModels {
this.articleHeader {
id("Articles")
model(
ArticleHeaderModel(
title = "Articles"
)
)
}
this.headlineArticle {
id("Headline1")
model(
HeadlineArticleModel(
title = "Headline1",
description = "Description1"
)
)
}
this.photoArticle {
id("Headline2")
model(
PhotoArticleModel(
title = "Headline2",
description = "Description2",
photoUrl = "SomeUrl"
)
)
}
}

In Kotlin, Epoxy generates these extension functions for each Epoxy Model; no complex adapter logic required! The id will be the android id tag for the view (required for all EpoxyModels), while the model is the databinding variable declared in the layout from earlier. It’s that simple; just add epoxy, specify the layout prefix, name the layout models, and it’s ready to roll.

The example app takes this process one step further by adding an interface for databinding the models to the EpoxyRecyclerView. First, a base class is created with a single build function, taking the EpoxyController as a parameter.

This way, the list of models can be iterated through and built in this BindingAdapter, which again leverages the EpoxyRecylcerView’s EpoxyController.

The Models from the Multi Recycler App can be reused here. However, instead of extending the base class that contained the ArticleType field, these will extend the newly defined ArticleEpoxyModel. These classes will handle their own EpoxyModel creation (using the mentioned Kotlin extension functions) with the values they are instantiated with.

Finally, the list, or in this case the viewModel containing the list can be databound to the view.

The final product of the Epoxy version of the app looks like this.

As you can see, the end product of these apps looks just about identical; the difference being the implementation under the hood. Epoxy can be a very powerful tool for eliminating complex adapter logic to implement the same multiple viewholder pattern again and again. Hopefully this served as a good introduction to how this library can be useful!

The source code for these projects can be found on Github for the Multi Recycler App and the Epoxy App.

--

--

Matthew Meehan
Matthew Meehan

Responses (2)