LoginSignup
1
0

More than 3 years have passed since last update.

【Android】RecyclerViewでスマートにコレクション画面の実装をする

Posted at

コレクション画面(=なにかしらの一覧画面)を実装する際にRecyclerViewをよく使うと思います。
僕としては複雑なViewTypeを比較的簡単に作り分けるために、Epoxyのような便利なライブラリを使うことが多いのですが、弊社の新人研修の課題で久しぶりにAdapterを書く機会があったので、その時の実装をメモ程度に残しておきます。

差分を比較する

どの値を比較して、差分とみなすかを定義します。
下記では単に比較対象の equals に従って比較しています。

MyDiffable.kt
interface Diffable {
    // otherと同じIDを持つかどうか
    fun isTheSame(other: Diffable) = equals(other)

    // otherと完全一致するかどうか
    fun isContentsTheSame(other: Diffable) = equals(other)
}

private class Callback(
    val old: List<Diffable>,
    val new: List<Diffable>
) : DiffUtil.Callback() {

    override fun getOldListSize() = old.size
    override fun getNewListSize() = new.size

    override fun areItemsTheSame(
        oldItemPosition: Int,
        newItemPosition: Int
    ) = old[oldItemPosition].isTheSame(new[newItemPosition])

    override fun areContentsTheSame(
        oldItemPosition: Int,
        newItemPosition: Int
    ) = old[oldItemPosition].isContentsTheSame(new[newItemPosition])
}

fun calculateDiff(
    old: List<Diffable>,
    new: List<Diffable>,
    detectMoves: Boolean = false
): DiffUtil.DiffResult = DiffUtil.calculateDiff(Callback(old, new), detectMoves)

Entityの実装

Diffableを継承したEntityをつくります。
data classにはDiffableの差分計算に必要なequalsメソッドが既に定義されているので、それをそのまま使います。

HogeItem.kt
data class HogeItem(
    val id,
    val name
) : Diffable

Adapterの実装

Viewの余計な更新が行われるのを防ぐために、デリゲートで差分を計算してから更新を伝えるようにします。

HogeAdapter.kt
class HogeAdapter(
    lifecycleOwner: LifecycleOwner,
    private val viewModel: HogeViewModel
) : RecyclerView.Adapter<HogeAdapter.ViewHolder>() {

    private var items: List<HogeItem> by Delegates.observable(emptyList()) { _, o, n ->
        calculateDiff(o, n).dispatchUpdatesTo(this)
    }

    init {
        viewModel.hogeList.observe(lifecycleOwner) { items = it }
    }

    override fun getItemCount() : Int { /* ... */ }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { /* ... */ }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) { /* ... */ }

    class ViewHolder(val binding: HogeItemBinding) : RecyclerView.ViewHolder(binding.root)
}

利用側の実装

LiveDataの監視はAdapter側で行っているので、ViewではRecyclerViewにAdapterを設定するだけでよくなります。

HogeActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding.hogeRecyclerView.adapter = HogeAdapter(this, viewModel)
}
HogeFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    binding.hogeRecyclerView.adapter = HogeAdapter(viewLifecycleOwner, viewModel)
}

まとめ

APIで取得したデータを必要最低限のViewの更新で反映できるので、大規模なコレクションではパフォーマンスの向上やUXの改善に繋がります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0