SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

アプリケーション開発の最新トレンド

Androidのページング処理のライブラリ「Paging 3」の移行と実装のポイント

  • X ポスト
  • このエントリーをはてなブックマークに追加

共通部分の実装

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を使用しない場合は適宜読み替えて実装してください。

AnimeViewHolder.kt
    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はデータクラスのため==を用いて比較しています。
AnimeAdapter.kt
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していきます。

 実際のコードは以下のようになります。

AnimeAdapter.kt
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で別スレッドで実行する必要がある点には注意してください。

fragment_paging_source.xml
<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>
PagingSourceFragment.kt
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つの要件別に、実装方法を紹介します。

次のページ
APIから取得したデータを表示するだけの場合

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
アプリケーション開発の最新トレンド連載記事一覧

もっと読む

この記事の著者

長濱 伶(ヤフー株式会社)(ナガハマ レイ)

 2000年沖縄県生まれ。 学生時代に計算機シミュレーションを用いた文化の安定性に関する研究に従事。 2021年3月に沖縄工業高等専門学校のメディア情報工学科卒業後、4月にヤフー株式会社に入社。 2021年7月からPayPayフリマのAndroidアプリ開発に携わる。 Twitter: @Fel1Tech

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/15314 2022/02/17 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング