概要
EpoxyはAirbnb製のライブラリである。特に、複数のViewTypeを持つタイプのRecyclerViewの実装が楽になる。
導入
build.gradleに以下を追加。
apply plugin: 'kotlin-kapt'
dependencies {
// epoxy
compile 'com.airbnb.android:epoxy:2.7.3'
kapt 'com.airbnb.android:epoxy-processor:2.7.3'
compile 'com.airbnb.android:epoxy-databinding:2.7.3'
}
kapt {
correctErrorTypes = true
}
レイアウト
RecyclerViewを使用してheader2種類、contents2種類の計4種類のレイアウトが含まれた以下のような画面を作成する。
Epoxyを使わない方法だと、RecyclerViewに紐付けるAdapter内で4種類のViewTypeに応じて処理を分けるような実装が必要になると思われる。
メインのレイアウト
<layout
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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
</layout>
header1のレイアウト
<layout xmlns:android="http://schemas.android.com/apk/res/android" >
<data>
<variable name="title" type="String" />
</data>
<LinearLayout
android:background="#ff0000"
android:gravity="left|center_vertical"
android:layout_width="match_parent"
android:layout_height="100dp">
<TextView
android:text="@{title}"
android:textStyle="bold"
android:layout_marginLeft="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
header2のレイアウト
<layout xmlns:android="http://schemas.android.com/apk/res/android" >
<data>
<variable name="title" type="String" />
</data>
<LinearLayout
android:background="#00ff00"
android:gravity="right|center_vertical"
android:layout_width="match_parent"
android:layout_height="80dp">
<TextView
android:text="@{title}"
android:textStyle="italic"
android:layout_marginRight="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
contents1のレイアウト
<layout xmlns:android="http://schemas.android.com/apk/res/android" >
<data>
<variable name="text" type="String" />
</data>
<LinearLayout
android:gravity="center"
android:background="#eeeeee"
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:text="@{text}"
android:textSize="35sp"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
contents2のレイアウト
<layout xmlns:android="http://schemas.android.com/apk/res/android" >
<data>
<variable name="text" type="String" />
</data>
<LinearLayout
android:gravity="center"
android:background="#888888"
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:text="@{text}"
android:textSize="18sp"
android:fontFamily="serif-monospace"
android:textColor="#ffffff"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
package-info.java
EpoxyにレイアウトファイルからBindingModel_クラスを自動生成してもらうために、package-info.javaを作成し、以下のように記述する。
@EpoxyDataBindingLayouts({
R.layout.header1,
R.layout.header2,
R.layout.contents1,
R.layout.contents2
})
package example.test.epoxytest;
import com.airbnb.epoxy.EpoxyDataBindingLayouts;
作成したpackage-info.javaは以下のように配置する。
ここまで作成したら、一旦ビルドすることでHeader1BindingModel_, Contents1BindingModel_, Header2BindingModel_, Contents2BindingModel_がEpoxyによって自動生成される。
Dataクラス作成
コンテンツのデータを格納するクラスを作成する。
class Data(val header1: String, val contents1: List<String>, val header2: String, val contents2: List<String>)
Controller作成
Adapterの代わりにControllerを作成する。
class TestController : TypedEpoxyController<Data>() {
override fun buildModels(data: Data) {
Header1BindingModel_()
.title(data.header1)
.id(modelCountBuiltSoFar)
.addTo(this)
for (item in data.contents1) {
Contents1BindingModel_()
.text(item)
.id(modelCountBuiltSoFar)
.addTo(this)
}
Header2BindingModel_()
.title(data.header2)
.id(modelCountBuiltSoFar)
.addTo(this)
for (item in data.contents2) {
Contents2BindingModel_()
.text(item)
.id(modelCountBuiltSoFar)
.addTo(this)
}
}
}
Adapterを実装するのと比べると簡潔である。
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val controller = TestController()
controller.setData(Data("header1", listOf("aa", "ab", "ac"),
"header2", listOf("ba", "bb", "bc", "bd", "be", "bf")))
binding.recyclerView.adapter = controller.adapter
binding.recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
}
}
ControllerにDataを設定し、Controller内部で生成されたadapterをRecyclerViewに紐付ける。
まとめ
Epoxyを使うことでボイラープレートコードが減り、少ない記述で複数のviewTypeを持つRecyclerViewの実装ができた。