共通部分の実装
RecyclerViewでリスト表示するレイアウトの実装
今回はDataBindingを使用するため、DataBindingを用いてレイアウトを定義します。ここは要件により大きく変わる部分ですので説明は特にしませんが、今回は取得したanimeのurlから画像を取得し、その右にタイトルを表示する横長のレイアウトを定義しました。
<layout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="anime" type="com.example.pagingsampleapp.data.vo.Anime" /> </data> <com.google.android.material.card.MaterialCardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:layout_width="80dp" android:layout_height="80dp" app:loadImage="@{ anime.images.recommended_url }" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{ anime.title }"/> </LinearLayout> </com.google.android.material.card.MaterialCardView> </layout>
ViewHolderの実装
ViewHolderでは、先ほど作成したリスト表示するレイアウトのBindingを引数にとり、ViewHolderの引数に渡すViewでは、Bindingのrootを指定します。
DataBindingを使用しない場合は適宜読み替えて実装してください。
class AnimeViewHolder(val binding: ViewAnimeTitleBinding) : RecyclerView.ViewHolder(binding.root)
PagingAdapter本体の実装
PagingDataAdapterの実装は以下の3つです。順を追って見ていきます。
- DiffUtil.ItemCallback:入ってきたデータに変更があるかを確かめる処理を書きます。
- onCreateViewHolder:定義したViewHolderをインスタンス化する処理を書きます。
- onBindViewHolder:渡されたデータをレイアウトに紐づける処理を書きます。
DiffUtil.ItemCallbackの実装
今回は、AdapterにはAnimeというデータクラスを渡します。そこで、Adapter側が渡されたデータに変更があった際に表示の更新をする必要があります。その際に、これらのデータが同じデータかどうかを判別するための処理が必要となるため実装する必要がありますので実装していきます。
2つメソッドを実装する必要があり、それぞれの必要な処理は以下になります。
- areItemsTheSame:メソッド名の通りですが、渡されたデータが同じものかどうかを判別する処理です。そのため、一意のid等がある場合はそれが同じかどうかで判別を行ってください。今回はAnimeクラスが一意のidを持っているためそれで比較しています。
- areContentsTheSame:こちらのメソッドは、渡されたデータの中身が同じものかどうかを判別する処理を実装します。今回Animeはデータクラスのため==を用いて比較しています。
object : DiffUtil.ItemCallback<Anime>() { override fun areItemsTheSame(oldItem: Anime, newItem: Anime): Boolean { return oldItem.id == oldItem.id } override fun areContentsTheSame(oldItem: Anime, newItem: Anime): Boolean { return oldItem == newItem } }
onCreateViewHolder、onBindViewHolderの実装
上記の2つのメソッドについては同時に見ていきます。
onCreateViewHolderでは、「ViewHolderの実装」で実装したViewHolderのインスタンスを作成して返却します。今回はリスト表示するレイアウトのBindingを持たせるため、DataBindingUtilでレイアウトを作成して引数に渡しています。
onBindViewHolderでは、まず引数にわたってきたpositionから、該当するデータ(今回はAnimeクラス)を取得します。そして、同じく引数にわたってきたViewHolderが持っているレイアウトに対して取得したAnimeクラスのデータをBindしていきます。
実際のコードは以下のようになります。
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimeViewHolder = AnimeViewHolder( DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.view_anime_title, parent, false ) ) override fun onBindViewHolder(holder: AnimeViewHolder, position: Int) { val item = getItem(position) holder.binding.anime = item }
Fragmentの実装
Fragmentでは、レイアウトファイルで追加したRecyclerViewに対して、先ほど実装したPagingDataAdapterをセットします。
そして、ViewModelで提供された値をPagingDataAdapterに渡します。
ここで、FlowのcollectLatestやadapterへのsubmitDataは非同期処理となっているため、lifefcycleScopeで別スレッドで実行する必要がある点には注意してください。
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data></data> <androidx.recyclerview.widget.RecyclerView android:id="@+id/anime_recycler" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> </layout>
val adapter = AnimeAdapter() binding.animeRecycler.adapter = adapter // Flowの場合 lifecycleScope.launch { viewModel.getAnimesAsFlow().collectLatest { adapter.submitData(it) } } // LiveDataの場合 viewModel.getAnimesAsLiveData().observe(viewLifecycleOwner) { lifecycleScope.launch { adapter.submitData(it) } }
以上、共通部分について説明していきました。
以降は、要件ごとにPagerインスタンスに対して渡すPagerのインスタンスを作るときに渡すコンストラクタによって、「要件を確認する」の実装ができます。
ここからは、先に示した2つの要件別に、実装方法を紹介します。