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というのを作った