【Android】使用Epoxy替代RecyclerView.Adapter

    科技2025-12-28  11

    Epoxy


    Epoxy通过声明式的方式帮助RecyclerView更高效的实现数据加载、UI更新等逻辑,提高列表场景的开发效率。

    https://github.com/airbnb/epoxy Epoxy is an Android library for building complex screens in a RecyclerView - airbnb/epoxy

    开发流程


    Epoxy的基本使用流程:

    创建cell的layout文件 可以使用.xml、或者 .kt创建cell的layout。推荐使用xml,搭配databinding使用起来更简单。根据cell生成EpoxyModel 在package-info.java中声明layout文件、kapt根据xml自动生成EpoxyModel代码,供EpoxyRecyclerView使用创建EpoxyController EpoxyController相当于RecyclerView的Adapter,用来将数据绑定到UI,同时定义OnClick等事件使用EpoxyController 将EpoxyController设置到EpoxyRecyclerView,完成列表的数据记载和显示

    接下来简单介绍一下每一步的具体实现。例如我们需要在列表中加载下列数据:

    data class Foo ( var title: String, var bar: List<Bar> ) data class Bar ( var body: String, var time: String )

    0. gradle


    apply plugin: 'kotlin-kapt' // 需要使用kapt android { dataBinding { enabled = true // 使用databinding } } kapt { correctErrorTypes = true } dependencies { implementation 'com.airbnb.android:epoxy:3.4.2' kapt 'com.airbnb.android:epoxy-processor:3.4.2' implementation 'com.airbnb.android:epoxy-databinding:3.4.2' }

    1. cell layout


    list_cell_a.xml

    <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" > <data> <variable name="title" type="String" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{title}" android:textSize="24sp" android:textStyle="bold" tools:text="test" /> </LinearLayout> </layout>

    list_cell_b.xml

    <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" > <data> <variable name="body" type="String" /> <variable name="time" type="String" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="8dp" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{body}" android:textSize="20sp" tools:text="test" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{time}" android:textSize="18sp" tools:text="test" /> </LinearLayout> </layout>

    2. package-info.java


    @EpoxyDataBindingLayouts({ R.layout.list_cell_a, R.layout.list_cell_b }) package com.airbnb.epoxy.sample; import com.airbnb.epoxy.EpoxyDataBindingLayouts;

    编译后,会根据layout文件,生成EpoxyModels代码,例如ListCellABindingModel_、ListCellBindingModel_等,用来绑定数据

    3. EpoxyController


    继承TypedEpoxyController,重写buildmodels,用声明式的方式填充UI的数据。

    class FooBarController : TypedEpoxyController<Foo>() { override fun buildModels(foo: Foo) { ListCellABindingModel_() .title(foo.title) // databinding .id(modelCountBuiltSoFar) .addTo(this) foo.bar.forEach { ListCellBindingModel_() .body(it.body) .time(it.time) .id(modelCountBuiltSoFar) .addTo(this) } } } 根据buildModels参数个数选择不同基类,例如Typed2EpoxyController、Typed3EpoxyController等title、body、time是layout的定义的databindingid用来区分不同类型的Cell,所有的EpoxyModels必须有唯一id

    4. use in RecyclerView


    class FooBarFragment : Fragment() { lateinit var binding: FragmentFooBarBinding //fragment的layout生成的daabinding override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { binding = DataBindingUtil.inflate(inflater, R.layout.fragment_foo_bar, container, false) return binding.root } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) val controller = FooBarController() binding.recyclerView.adapter = controller.adapter val data = Foo("title", listOf(Bar("str1","time1"),Bar("str2","time2"))) controller.setData(data) }) }

    通过Controller中设置data,数据将自动在渲染到列表中

    总结


    通过简单的实例,可以感觉到Epoxy有以下几个好处

    避免了ViewHolder的使用, 代码更少通过EpoxyController和EpoxyModel,声明式的填充数据到UI,简单直观每次数据更新后,会自动diff最小量刷新UI(类似pagging的功能)配合MvRx使用,实现完整的响应式UI
    Processed: 0.022, SQL: 9