RecyclerView Adapter 一つに対して複数のViewHolderを持つリストを作成する

Kotlin でAndroidアプリ開発をはじめました。
まず、LegacyとなったListViewの代わりにRecyclerViewというのが新しく用意されているとの事で触ってみました。
ちゃんとしたオブジェクト指向でコードを書くのはかなり久々な事と、
AdapterとHolder、レイアウトファイルの関連が掴めていなかったのもあり、
理解に至るまで苦しみました。
後日順を追ってKotlinによるAndroid開発のメモは残しておきますが、この記事は先に残しておこうと思います。

やりたいこと:Adapter一つに対して複数のViewHolderを持つRecyclerViewを作る

例えば、RecyclerViewのアイテム一覧の一番最後は「+」アイコンで
要素の追加が出来るようなActivityを用意します。

ポイントとなるのは以下の通りです。

onCreateViewHolder

  • ViewTypeで一番最後か、そうでないかを判断し、読み込むレイアウトファイルを使い分けます
  • 予め用意したViewHolderクラスのスーパークラスにあたる RecyclerView.ViewHolder を戻り値で返します。サブクラス(用意したViewHolderクラス)は返しません

onBindViewHolder

  • 引数のPositionから一番最後か、そうでないかを判断し、読み込むViewHolderを使い分けます
  • RecyclerView.ViewHolderを引数として待ち受け、読み込むViewHolderに従ってキャストします。
    > この時厳密に読み込むViewHolderが一致していないとキャストが出来なかったり、ウィジェットの要素がNullになって落ちるので判断する際は注意する。

レイアウトファイル(xml file)

RecyclerViewを表示するアクティビティ用レイアウトファイル (activity_new_post.xml)

後述する[A]及び[B]を組み合わせたものを期待しています。

 

[A]RecyclerViewのアイテム一つを表すレイアウトファイル (layout_new_item_card.xml)

CardViewの中にEditTextとImageButtonをおいてみました。予め、専用の画像等は適切なフォルダに入れておいてください。 Image Buttonの使い方を調べておきましょう。

 

[B]RecyclerViewの一番最後の要素に表示するレイアウトファイル (layout_add_button.xml)

Image Buttonで+アイコンのみ配置しています

Holder

ViewHolder A (NewPostViewHolder.kt)

ViewHolder B (NewItemViewHolder.kt)

 

Adapter(NewPostAdapter.kt)

getItemViewメソッドでポジションを受け取り、用意したitemListとサイズが一致(=与えられた要素の位置が最終行)の場合のみ0を返却し、それ以外の行(=与えられた要素の位置が最終行以外)の場合は、1を返却します。

予め View型のオブジェクトを用意しておきます。

getItemViewTypeで得られた戻り値(0 or 1)を基に判断を進めます。 上述の通り0の場合は最終行なのでそれ用のレイアウトファイルを layoutInflaterメソッドにより読み込み、②で宣言したViewオブジェクトに代入します。その後、メソッドとしてViewHolderクラスを戻り値として返して終了します。
この時返却するViewHolderクラスは、要素に応じたものです。
最終行以外の場合も、読み込むレイアウトファイルと戻り値のViewHolderクラスが違うだけで、やっていることは一緒です。

これまでと同様に、今回はpositionの値によって最終行かそうでないかを判断しています。
myHolderに対しては、最終行だったら引数として読み込まれた holder(RecycleView.ViewHolder型)を NewItemViewHolder型にキャストといった処理をしています。最終行以外の場合も、相応の処理を行っています。
また、この時別々のクラス型にキャストしてあげる事で、ViewHolder内で宣言したウィジェット(例えば最終要素の場合は+ボタン)のsetOnClickListenerにアクセス出来るようになっています。

ちなみに、今回は+ボタンを押された際にRecyclerViewの要素が1つ増える処理が記載されています。

おまじないです。 このRecyclerViewは要素数+1は常に「+ボタン」になっています。

Activity

今回のアクティビティではユーザの操作によってRecyclerViewの要素が増える事を想定したものの為、Adapterに渡す配列はInt型で1要素のみを渡しています。

 

やってみて

ココ最近のAndroidアプリ開発は右も左も分からないまま突っ込んで、ようやくAdapterとHolderの仕組みが少し理解できたかなという気持ちです。
最初は戻り値となるViewHolderをサブクラス(NewItemViewHolder)に指定してたりして、「どうやってもう片方のViewHolderの受け取るんだよ」って躓いたりしました。スーパークラスにしておけばいいんですね・・・。

正直、上記の記述がベストプラクティスだとは思っていませんし、改善の余地があるかと思いますが、
ひとまず「動くもの」を目指して作るのは大事な事だと思っています。
その上でリファクタリングして更に理解を深められたらいいな。

いずれにしてもAdapterにあまり処理を書きたくないと思っているので、どこに移動させるのがベストか、
意見がありましたらツイッターでもコメントでもフィードバックいただけると嬉しいです。

参考

 

/以上

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください