パルカワ2

最近はFlutterをやっています

RecyclerViewで複雑な画面を作るためのライブラリ Epoxy

この記事は、 Android Advent Calendar 2017 - Qiita 11日目の記事です。


RecyclerViewで複雑な画面を作りたい!そんな欲求を持っている人が多いと思います。僕もそうです。でも、1個や2個のViewTypeならまだしもたくさんのViewTypeがある場合、getItemViewTypeとかで頑張った結果つらいコードになる未来が見えます。というか見てきました。

Epoxy というアプローチ

Airbnbが作ったRecyclerViewで複雑な画面を作るためのライブラリです。

github.com

ModelをCustomViewやDataBinding等から自動生成し、それらを操作するControllerを書き、そのControllerをEpoxyRecyclerViewというクラスにセットする感じです。

具体的に出来ることは、READMEに書かれてある通りなのですが、こういう事が出来ます。

This abstracts the boilerplate of view holders, diffing items and binding payload changes, item types, item ids, span counts, and more, in order to simplify building screens with multiple view types. Additionally, Epoxy adds support for saving view state and automatic diffing of item changes.

ワオ!便利!

使ってみる

サンプルアプリがあったので、それのコードを読んでいきたいと思います。

epoxy/epoxy-sample at master · airbnb/epoxy · GitHub

こういう動きをするアプリです。

f:id:hisaichi5518:20171211183913g:plain

これら全ての動作を全部追いかけると大変なので、今回は、DataBindingを利用しているAdd, Clearなどのボタンにフォーカスを当てたいと思います。 このような動きをする部分です。

f:id:hisaichi5518:20171211184004g:plain

Modelを作る

今回のサンプルアプリでDataBindingは、 button.xml で利用されています。このbutton.xmlは、リスト上にあるAdd, Clearなどを行うボタンを指しています。

epoxy/button.xml at d2e298902e64a5b634e9932ee2f16f56abfa52bd · airbnb/epoxy · GitHub

DataBindingを利用してるViewのModelを自動生成する場合は、package-info.java@EpoxyDataBindingLayoutsを利用して、Modelを自動生成することを宣言する必要があります。

epoxy/package-info.java at a46ed6c6c459f4d102a63d15aceb0e13dc5d246b · airbnb/epoxy · GitHub

@EpoxyDataBindingLayouts(R.layout.button)
@PackageModelViewConfig(rClass = R.class)
package com.airbnb.epoxy.sample;

import com.airbnb.epoxy.EpoxyDataBindingLayouts;
import com.airbnb.epoxy.PackageModelViewConfig;

これで、Modelの生成が行われ、ButtonBindingModel_ という名前のクラスが生成されます。

Controller を作る

ButtonBindingModel_SampleControllerというクラスで利用されています。コードを見ると案外シンプルです。

epoxy/SampleController.java at a46ed6c6c459f4d102a63d15aceb0e13dc5d246b · airbnb/epoxy · GitHub

今回のコードを読むにあたって、僕が重要だなと感じたのは、protected void buildModels(List<CarouselData> carousels)です。

epoxy/SampleController.java at a46ed6c6c459f4d102a63d15aceb0e13dc5d246b · airbnb/epoxy · GitHub

buildModels(List carousels)

このメソッドは、リストのデータが更新されるたびに実行されます。

buildModelsの中では、このようなコードがあり、何を表示するのか?やそのViewを表示するのか?などの操作が行われていることがわかります。

    // 表示文言の指定
    // Clickしたら、onAddCarouselClicked callbackを実行する
    addButton
        .textRes(R.string.button_add)
        .clickListener((model, parentView, clickedView, position) -> {
          callbacks.onAddCarouselClicked();
        });

    clearButton
        .textRes(R.string.button_clear)
        .clickListener(v -> callbacks.onClearCarouselsClicked())
        .addIf(carousels.size() > 0, this); // データの条件があえばViewを表示する

SampleControllerをEpoxyRecyclerViewにセットする

作成したControllerは、EpoxyRecyclerViewにセットしています。

ここは、RecyclerViewにセットも出来るようですが、EpoxyRecyclerViewを使うことで、シンプルになり更にパフォーマンスのアレコレもやってくれるそうです。

EpoxyRecyclerView · airbnb/epoxy Wiki · GitHub

    recyclerView.setController(controller);

セットしているのはこの部分

epoxy/MainActivity.java at a46ed6c6c459f4d102a63d15aceb0e13dc5d246b · airbnb/epoxy · GitHub

あとはデータの更新を行いたい時にcontroller.setData(...)を呼び出すだけです。

  private void updateController() {
    controller.setData(carousels);
  }

所感

コード読んだ部分が簡単過ぎた。

データの更新が行われるたびにループがデータ分実行されるのが気になりました。データが大きくなるとつらそう。 RecyclerViewのAdapterにあるnotifyItemInsertedのようにどこが変わったのか通知する形の方が早そうな印象は受けましたが、実際どうなのかは調べてないです。

その他のアプローチ

Githubを検索すると色々あるんですが、どれがいいのかというのは正直よくわかりませんでした。それらと比較して自分がメンテナンスするアプリへの導入を検討するといいと思います。

まとめ

RecyclerViewを利用して複雑な画面を作るためのライブラリであるEpoxyのサンプルアプリのコードを読んでみました。

複雑な画面はなるべく作りたくないですが、Epoxyには様々な機能があり、複雑な画面を作るのにとても有効だと感じました。次は、実際に開発しているアプリで試してみようと思います。

参考