Integrating Jetpack Compose UI in an Existing Epoxy Codebase

Rajesh Eswarlal
2 min readAug 31, 2023

As the Android development ecosystem evolves, incorporating new technologies into existing codebases becomes a strategic move to leverage the latest advancements while retaining the robustness of the application. In this context, integrating Jetpack Compose UI components into an established Airbnb Epoxy RecyclerView codebase can offer significant advantages in terms of reusability, maintainability, and an enhanced user experience.

Lets jump into coding !!!

Step 1: Consider an Epoxy model in your current codebase that has a EpoxyRecyclerView to display a list of items using traditional Android views:

@EpoxyModelClass(layout = R.layout.epoxy_item_layout)
abstract class SampleEpoxyModel : EpoxyModelWithHolder<SampleEpoxyModel.Holder>() {

@EpoxyAttribute
lateinit var title: String

override fun bind(holder: Holder) {
holder.titleTextView.text = title
}

class Holder : EpoxyHolder() {
lateinit var titleTextView: TextView

override fun bindView(itemView: View) {
titleTextView = itemView.findViewById(R.id.titleTextView)
}
}
}

Step 2: Create a simple compose component which we want to integrate in the above epoxy model

@Composable
fun ComposeItem(name: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(text = "Hello, $name!")
}
}

Step 3 : Now, let’s integrate a Jetpack Compose UI component into this existing Epoxy model

@EpoxyModelClass(layout = R.layout.epoxy_item_layout)
abstract class ComposeEpoxyModel : EpoxyModelWithHolder<ComposeEpoxyModel.Holder>() {

@EpoxyAttribute
lateinit var title: String

override fun bind(holder: Holder) {
// Set up the Compose UI within the existing view holder
val composeContainer = holder.itemView.findViewById<ComposeView>(R.id.composeContainer)
composeContainer.setContent {
ComposeItem(title = title)
}
}

class Holder : EpoxyHolder() {
lateinit var itemView: View

override fun bindView(itemView: View) {
this.itemView = itemView
}
}
}

Incase, If we want to create a Epoxy model without xml then,

@ModelView(
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
saveViewState = true
)
class ComposeEpoxyModel @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {

@EpoxyAttribute
lateinit var title: String

@Composable
override fun Content() {
ComposeItem(title = title)
}
}

Step 4 : Create an Epoxy controller that populates the RecyclerView with instances of your Epoxy model

class MyEpoxyController : TypedEpoxyController<List<String>>() {
override fun buildModels(data: List<String>?) {
data?.forEach { name ->
composeEpoxyModel {
id(name)
name(name)
}
}
}
}

Step 5: Finally, integrate your Epoxy controller with a RecyclerView in your activity or fragment


class MainActivity : AppCompatActivity() {

private val epoxyController = MyEpoxyController()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val recyclerView: EpoxyRecyclerView = findViewById(R.id.recycler_view)
recyclerView.setController(epoxyController)

val data = listOf("Alice", "Bob", "Charlie")
epoxyController.setData(data)
}
}

Yaaaay 🎉, With Epoxy and Jetpack Compose joining forces, our app’s UI journey just got a turbocharged upgrade! 🚀🎈

--

--