Tutorial Android RecyclerView Epoxy

Wisnu Kurniawan
4 min readJul 6, 2019

--

credit

Membuat sebuah tampilan dalam bentuk list merupakan hal yang common pada sebuah aplikasi. Untuk membuatnya kita bisa menggunakan RecyclerView. Namun pernahkah kalian membuat sebuah RecyclerView dengan mensupport multiple type?

Dengan RecyclerView kita dapat membuat multiple type dengan mengoverride method getItemViewType() dari kelas Adapter. Untuk kasus RecylerView dengan 3 jenis type mungkin kode kita masih belum terlihat kompleks.

Coba perhatikan pada halaman home aplikasi-aplikasi perusahaan besar seperti GOJEK, Tokopedia, Bukalapak, Traveloka, dan aplikasi lainnya. Pada halaman tersebut jenis type yang di support ada banyak bukan?. Oleh sebab itu library Epoxy hadir untuk mensimplify dalam membangun RecyclerView yang kompleks secara efisien.

Epoxy is an Android library for building complex screens in a RecyclerView

TLDR Perbedaan RecyclerView dengan Epoxy

Beberapa komponent yang di perlukan ketika membuat sebuah tampilan list sebagai berikut:

  1. Container
    RecyclerView menggunakan androidx.recyclerview.widget.RecyclerView.
    Epoxy dapat menggunakan androidx.recyclerview.widget.RecyclerView atau com.airbnb.epoxy.EpoxyRecyclerView .
  2. ViewHolder
    RecyclerView menggunakan androidx.recyclerview.widget.RecyclerView.ViewHolder . Sedangkan Epoxy menggunakan com.airbnb.epoxy.EpoxyModel<T> .
  3. Adapter
    RecyclerView menggunakan androidx.recyclerview.widget.RecyclerView.Adapter<VH> . Sedangkan Epoxy menggunakan com.airbnb.epoxy.EpoxyController .

Pada tutorial kali ini saya akan membuat sebuah aplikasi sederhana yang menampilkan dua jenis data. Data tersebut berasal dari https://api.github.com/ . Data pertama adalah profile github saya dan data kedua adalah project github saya. Berikut hasil aplikasi yang akan kita buat:

1. Setup Dependencies

apply plugin: 'kotlin-kapt'
kapt {
correctErrorTypes = true
}
// List
implementation "com.airbnb.android:epoxy:3.7.0"
kapt "com.airbnb.android:epoxy-processor:3.7.0"

2. Setup Layout Container

Pasang EpoxyRecyclerView pada activity layout kalian seperti di bawah ini.

<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/home_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/_8sdp"
android:paddingBottom="@dimen/_8sdp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

3. Setup Item View Profile

Layout ini akan menampilkan data profile github berupa avatar profile, nama, lokasi, bio, dan blog.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/_16sdp"
android:layout_marginEnd="@dimen/_16sdp"
android:layout_marginLeft="@dimen/_16sdp"
android:layout_marginRight="@dimen/_16sdp"
android:orientation="vertical">

<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="@dimen/_8sdp"
app:cardElevation="@dimen/_4sdp"
app:cardPreventCornerOverlap="false"
app:cardUseCompatPadding="true">

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/_8sdp">

<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/profile_iv"
android:layout_width="@dimen/_72sdp"
android:layout_height="@dimen/_72sdp"
tools:src="@mipmap/ic_launcher"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/_16sdp"
android:layout_marginLeft="@dimen/_16sdp"
android:layout_toEndOf="@+id/profile_iv"
android:layout_toRightOf="@+id/profile_iv"
android:orientation="vertical">

<TextView
android:id="@+id/profile_name_tv"
style="@style/Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/_14ssp"
tools:text="Wisnu Kurniawan"/>

<TextView
android:id="@+id/profile_location_tv"
style="@style/Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/_2sdp"
android:textSize="@dimen/_14ssp"
tools:text="Yogyakarta, Indonesia"/>

<TextView
android:id="@+id/profile_bio_tv"
style="@style/Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/_2sdp"
android:textSize="@dimen/_14ssp"
tools:text="Passionate in mobile development"/>

<TextView
android:id="@+id/profile_blog_tv"
style="@style/Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/_2sdp"
android:textSize="@dimen/_14ssp"
tools:text="https://medium.com/@wisnukurniawan"/>

</LinearLayout>

</RelativeLayout>

</androidx.cardview.widget.CardView>

</LinearLayout>

4. Setup Item View Project

Layout ini akan menampilkan data projek github berupa nama, deskripsi, total star, dan bahasa pemrograman.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView
android:id="@+id/project_name_tv"
style="@style/Text.Header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/_8sdp"
android:layout_marginStart="@dimen/_16sdp"
android:layout_marginLeft="@dimen/_16sdp"
android:textSize="@dimen/_14ssp"
tools:text="Kovie"/>

<TextView
android:id="@+id/project_description_tv"
style="@style/Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/_8sdp"
android:layout_marginLeft="@dimen/_16sdp"
android:layout_marginRight="@dimen/_16sdp"
android:textColor="#4f5b62"
android:textSize="@dimen/_12ssp"
tools:text="Kovie is kotlin movie android project use MovieDb API build with MVP architecture, Dagger, and RxJava"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/_16sdp"
android:layout_marginLeft="@dimen/_16sdp"
android:layout_marginRight="@dimen/_16sdp"
android:gravity="end"
android:orientation="horizontal">
<TextView
android:id="@+id/project_stars_tv"
style="@style/Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/_16sdp"
android:layout_marginRight="@dimen/_16sdp"
android:textColor="#4f5b62"
android:textSize="@dimen/_12ssp"
tools:text="Stars: 6"/>

<TextView
android:id="@+id/project_language_tv"
style="@style/Text.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#4f5b62"
android:textSize="@dimen/_12ssp"
tools:text="Language: Kotlin"/>
</LinearLayout>

<View
android:layout_width="match_parent"
android:layout_height="@dimen/_1sdp"
android:layout_marginStart="@dimen/_16sdp"
android:layout_marginLeft="@dimen/_16sdp"
android:background="#e8e8e8"/>

</LinearLayout>

5. Setup Epoxy Model

Epoxy model adalah ViewHolder objects yang bertugas membuat model class yang digunakan untuk menampilkan single item berdasarkan value model yang telah terasosiasi. Untuk membuat Epoxy Model terdapat 3 cara, yaitu:

  1. Menggunakan custom layout
  2. Menggunakan data binding
  3. Menggunakan layout

Pada tutorial ini saya menggunakan cara ke 3. Untuk mengimplementasi cara ini kita membuat sebuah abstrak kelas yang mengextend EpoxyModelWithHolder<H> dan mengimplemen annotation @EpoxyModelClass(layout). Kemudian membuat sebuah kelas yang mengekstend EpoxyHolder() menguntuk menghubungkan id view.

  • Epoxy model profile type
@EpoxyModelClass(layout = R.layout.layout_profile_item)
abstract class ProfileView(
private val context: Context
) : EpoxyModelWithHolder<ProfileView.Holder>() {

@EpoxyAttribute
lateinit var model: Model

override fun bind(holder: Holder) {
super.bind(holder)
holder.nameTv.text = model.name
holder.bioTv.text = model.bio
holder.blogTv.text = model.blog
holder.locationTv.text = model.location
Glide
.with(context)
.load(model.avatarUrl)
.into(holder.profileIv)
}

inner class Holder : KotlinEpoxyHolder() {
val profileIv by bind<ImageView>(R.id.profile_iv)
val nameTv by bind<TextView>(R.id.profile_name_tv)
val locationTv by bind<TextView>(R.id.profile_location_tv)
val bioTv by bind<TextView>(R.id.profile_bio_tv)
val blogTv by bind<TextView>(R.id.profile_blog_tv)
}

data class Model(
override val id: String,
val name: String,
val avatarUrl: String,
val location: String,
val bio: String,
val blog: String
) : HomeItemModel

}
  • Epoxy model project type
@EpoxyModelClass(layout = R.layout.layout_project_item)
abstract class ProjectView(
private val context: Context
) : EpoxyModelWithHolder<ProjectView.Holder>() {

@EpoxyAttribute
lateinit var model: Model

override fun bind(holder: Holder) {
super.bind(holder)
holder.nameTv.text = model.name
holder.descriptionTv.text = model.description
holder.starsTv.text = context.getString(R.string.profile_item_stars, model.stars.toString())
holder.languageTv.text = context.getString(R.string.profile_item_language, model.language)
}

inner class Holder : KotlinEpoxyHolder() {
val nameTv by bind<TextView>(R.id.project_name_tv)
val descriptionTv by bind<TextView>(R.id.project_description_tv)
val starsTv by bind<TextView>(R.id.project_stars_tv)
val languageTv by bind<TextView>(R.id.project_language_tv)
}

data class Model(
override val id: String,
val name: String,
val description: String,
val language: String,
val stars: Int
) : HomeItemModel

}

6. Setup Epoxy Controller

Epoxy Controller bertugas untuk menentukan model apa yang di terima oleh RecyclerView. Penggunaannya cukup sederhana, kita hanya perlu mengeset data saja menggunakan Epoxy model yang telah otomatis tergenerate ketika build dan sisanya telah di handle oleh Epoxy. Sebagai contoh jika nama Epoxy model kamu HeaderView maka kelas hasil generate akan bernama HeaderView_ .

Pada tutorial ini karna saya menggunakan kotlin saya memanfaatkan extension hasil generate dari library Epoxy. Sebagai contoh jika nama Epoxy model kamu HeaderView maka extension hasil generate akan bernama headerView() .

Jangan lupa untuk mengeset attributeid yang otomatis tergenerate oleh Epoxy dari Epoxy model yang kita buat.

class HomeItemController(private val context: Context) : TypedEpoxyController<List<HomeItemModel>>() {

override fun buildModels(data: List<HomeItemModel>?) {
data?.forEach {
when (it) {
is ProfileView.Model -> {
profileView(context) {
id(it.id)
model(it)
}
}
is ProjectView.Model -> {
projectView(context) {
id(it.id)
model(it)
}
}
}
}
}
}

7. Menghubungkan seluruh komponen pada Activity

class HomeActivity : AppCompatActivity() {

private val homeViewModel: HomeViewModel by currentScope.viewModel(this)
private val homeItemController: HomeItemController by lazy { HomeItemController(this) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)

home_rv.setController(homeItemController)
home_rv.setItemSpacingDp(16)

...
}
... private fun bindData(list: List<HomeItemModel>) {
homeItemController.setData(list)
}

}

Selesai, cukup simple bukan!

Untuk lebih lanjut fitur apa saja yang telah di support oleh Epoxy, kamu dapat langsung menuju link berikut ini https://github.com/airbnb/epoxy/wiki

Source Code Project:

Credits:

https://android.jlelse.eu/nachos-tutorial-for-airbnbs-epoxy-with-kotlin-d1e682fdeb05
https://android.jlelse.eu/simplifying-recycler-view-with-epoxy-in-kotlin-nachos-tutorial-series-946d22116d57
https://medium.com/@ngengesenior/complex-recyclerview-with-epoxy-e6fe486795bf
https://medium.com/airbnb-engineering/epoxy-airbnbs-view-architecture-on-android-c3e1af150394
https://proandroiddev.com/android-paging-library-with-multiple-view-type-68f85fe1222d

Thanks for reading this article! claps 👏 if this article helpful.

--

--

No responses yet