How to use Epoxy with ViewHolder (Part 2)
Simple Listing using Epoxy ViewHolder
Like in previous part we have seen how simple to list item using data class.
In this post, we gonna use View Holder to list items on RecyclerView/EpoxyRecyclerView
Listing with ViewHolder is similar like Recycler.ViewHolder but in Epoxy we extend to EpoxyModelWithHolder
2. Show List using ViewHolder
Here we need a helper class KotlinEpoxyHolder that we can get from epoxy sample or from our source code.
package com.dastnaiqbal.epoxysample.helper
import android.view.View
import com.airbnb.epoxy.EpoxyHolder
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
/**
* A pattern for easier view binding with an [EpoxyHolder]
*
* Se https://github.com/airbnb/epoxy/blob/2.19.0/kotlinsample/src/main/java/com/airbnb/epoxy/kotlinsample/helpers/KotlinEpoxyHolder.kt
*/
abstract class KotlinEpoxyHolder : EpoxyHolder() {
private lateinit var view: View
override fun bindView(itemView: View) {
view = itemView
}
protected fun <V : View> bind(id: Int): ReadOnlyProperty<KotlinEpoxyHolder, V> =
Lazy { holder: KotlinEpoxyHolder, prop ->
holder.view.findViewById(id) as V?
?: throw IllegalStateException("View ID $id for '${prop.name}' not found.")
}
/**
* Taken from Kotterknife.
* https://github.com/JakeWharton/kotterknife
*/
private class Lazy<V>(
private val initializer: (KotlinEpoxyHolder, KProperty<*>) -> V
) : ReadOnlyProperty<KotlinEpoxyHolder, V> {
private object EMPTY
private var value: Any? = EMPTY
override fun getValue(thisRef: KotlinEpoxyHolder, property: KProperty<*>): V {
if (value == EMPTY) {
value = initializer(thisRef, property)
}
@Suppress("UNCHECKED_CAST")
return value as V
}
}
}
Once we add helper class then we just need to extend our ItemVH class to KotlinEpoxyHolder and define our binding ui which we want to access
Layout itemdataclass.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent">
<TextView
android:id="@+id/tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="TextView" />
</androidx.constraintlayout.widget.ConstraintLayout>
So like we are using existing itemdataclass.xml layout, and In that we only have one textview where we want to show our text. so ItemVH binding class will be like this
class ItemVH : KotlinEpoxyHolder() {
val tv by bind<TextView>(R.id.tv)
}
To define layout for ViewHolder we use annotation @EpoxyModelClass and our ItemViewHolder class will be extend to EpoxyModelWithHolder. and we just define our UI stuff which we want to bind or we want to show on our UI using @EpoxyAttribute annotation, so If we see we are not directly binding list item. instead we have created our own title variable to set values
@EpoxyModelClass(layout = R.layout.itemdataclass)
abstract class ItemViewHolder : EpoxyModelWithHolder<ItemVH>() {
private val TAG = this::class.java.simpleName
@EpoxyAttribute
lateinit var title: String
override fun bind(view: ItemVH) {
super.bind(view)
view.tv.text = title
}
}
Once our ItemViewHolder class ready then we can directly use it in Controller. Creating controller, it’s same like previous example
Epoxy Controller: SimpleViewHolderController.kt
class SimpleViewHolderController : TypedEpoxyController<List<String>>() {
override fun buildModels(data: List<String>?) {
data?.forEachIndexed { index, s ->
itemViewHolder {
id(index)
title(s)
}
}
}
}
And the last step to attach controller in Epoxy Recyclerview
package com.dastnaiqbal.epoxysample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.dastnaiqbal.epoxysample.dataclass.SimpleDataController
import com.dastnaiqbal.epoxysample.viewholder.SimpleViewHolderController
import kotlinx.android.synthetic.main.activity_main.*
/**
*
* "Iqbal Ahmed" created on 22/08/2020
*/
class MainActivity : AppCompatActivity() {
private val dataClassController by lazy {
SimpleDataController()
}
private val viewHolderController by lazy {
SimpleViewHolderController()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/**
* Listing using Data Clas
*/
// rv.adapter = dataClassController.adapter
// dataClassController.setData(ArrayList<String>().apply {
// repeat(10) {
// add("Item Data Class #$it")
// }
// })
/**
* Listing using ViewHolder
*/
rv.adapter = viewHolderController.adapter
viewHolderController.setData(ArrayList<String>().apply {
repeat(10) {
add("Item View Holder #$it")
}
})
}
}
Congratulations You populated your list in Epoxy using ViewHolder
In this post, we used Kotlin ViewHolder as an Epoxy Model and Simple Controller,
In the Next post, we gonna use Databinding as an Epoxy Model and Instead of creating a separate adapter, we gonna use extension method to populate data on Epoxy RecyclerView.
SourceCode Link: https://github.com/DastanIqbal/EpoxySample/tree/epoxy/viewholder
Happy Coding !!