パルカワ2

最近はFlutterをやっています

RecyclerView.AdapterでViewTypeによってViewHolderを切り分ける ViewHolderBinder を作った

RecyclerView.Adapterは、Item毎にViewTypeを指定することでItem毎にViewHolderを指定出来る。なので「itemが映画ならFilmViewHolderを利用する」とか「itemがドラマならTvDramaViewHolderを利用する」とか「positionが0ならAdViewHolderを利用する」などが出来る。

ただそれをやっていると以下のようにViewHolderのインスタンスを作成するonCreateViewHolderとViewHolderを利用するonBindViewHolderがItemViewTypeが増えるたび膨らむ。

public FilmographyAdapter(Context context) {
    this.context = context;
}

...

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case VIEW_TYPE_1: {
            return new TvDramaViewHolder(LayoutInflater.from(context).inflate(R.layout.row_filmography, parent, false));
        }
        case VIEW_TYPE_2: {
            return new FilmViewHolder(LayoutInflater.from(context).inflate(R.layout.row_filmography, parent, false));
        }
        default:  {
            throw new IllegalArgumentException("みつからんかった");
        }
    }
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    switch (holder.getItemViewType()) {
        case VIEW_TYPE_1: {
            ((TvDramaViewHolder) holder).render(items.get(position));
            break;
        }
        case VIEW_TYPE_2: {
            ((FilmViewHolder) holder).render(items.get(position));
            break;
        }
        default:  {
            throw new IllegalArgumentException("みつからんかった");
        }
    }
}

RecyclerView-Binderというのもあって、それを利用すれば上記のようにswitchで頑張る必要は なさそう。ただViewHolder以外に自分でBinderクラスを作らなければならない事, Adapterに継承が必要なことが理由で利用しなかった。

なので、aptの勉強がてら自分で作ってみた。
github.com

さっきのswitch文べったりなコードがこうなる。

public FilmographyAdapter(Context context) {
    // Binderクラスが自動で生成されるのでそれのインスタンスを持つ
    this.binder = new FilmographyAdapterViewHolderBinder(context, this);
}

...

// @ViewHolderでviewTypeとlayoutを指定してあげる
void onBindViewHolder(
        @ViewHolder(viewType = VIEW_TYPE_1, layout = R.layout.row_filmography) TvDramaViewHolder holder,
        int position) {
    holder.render(items.get(position));
}

void onBindViewHolder(
        @ViewHolder(viewType = VIEW_TYPE_2, layout = R.layout.row_filmography) FilmViewHolder holder,
        int position) {
    holder.render(items.get(position));
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // @ViewHolderで指定されたViewType, layoutを元に
    // ViewHolderのインスタンスを作成する。
    return binder.create(parent, viewType);
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // @ViewHolderで指定されたViewTypeを元に利用するViewHolderのインスタンスを決めて
    // そのインスタンスにあった onBindViewHolder() を利用する。
    binder.bind(holder, position);
}

ViewHolderのインスタンスを作成する部分は、僕はいつもこう書いてるだけなので、人間が書く必要はないなと感じた。なので、全部自動生成するようにした。

return new ItemViewHolder(layoutInflater.inflate(R.layout.hogehoge, parent, false));

その結果、ViewTypeが増えれば、 対応したViewHolderとonBindViewHolder(...)を追加してやるだけになった。

まとめ
  • RecyclerViewでViewType毎にViewHolderを分けれるのは便利
  • ただ onCreateViewHolderなどが膨らんでしまい書くコードも多い
  • なので、ViewHolderBinderというのを作った

github.com