[Android] Use Epoxy instead of RecyclerView.Adapter

Insert picture description here

Epoxy


Epoxy helps RecyclerView to implement data loading, UI update and other logic more efficiently through a declarative way, and improve the development efficiency of list scenes.

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


Development Process


The basic use process of Epoxy:

  1. Create a cell's layout file
    can use .xml, or .ktcreate the layout cell. XML is recommended, it is easier to use with databinding.
  2. Generate EpoxyModel according to cell
    , declare layout file in package-info.java, kapt automatically generate EpoxyModel code according to xml, for use by EpoxyRecyclerView
  3. Create EpoxyController
    EpoxyController is equivalent to the Adapter of RecyclerView, used to bind data to the UI, and define events such as OnClick
  4. Use EpoxyController to
    set EpoxyController to EpoxyRecyclerView to complete the data recording and display of the list

Next, briefly introduce the specific implementation of each step. For example, we need to load the following data in the list:

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;

After compilation, it will generate EpoxyModelscode according to the layout file , such as ListCellABindingModel_, ListCellBindingModel_etc., to bind data


3. EpoxyController


Inherit TypedEpoxyController, rewrite buildmodels, and populate UI data in a declarative way.

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)
        }
    }
}
  • Choose different base classes according to the number of buildModels parameters, such as Typed2EpoxyController, Typed3EpoxyControlleretc.
  • title, body, timeIs defined layout ofdatabinding
  • idUsed to distinguish different types of Cell, all EpoxyModels must have a unique 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)
    })
}

By setting data in the Controller, the data will be automatically rendered in the list


to sum up


Through a simple example, you can feel that Epoxy has the following benefits

  1. Avoid the use of ViewHolder, less code
  2. Through EpoxyController and EpoxyModel, declaratively fill data to UI, simple and intuitive
  3. After each data update, the UI will be automatically refreshed with minimum diff (similar to pagging function)
  4. Use with MvRx to achieve a complete responsive UI

Guess you like

Origin blog.csdn.net/vitaviva/article/details/108966907