Slide 1

Slide 1 text

RecyclerViewと GridLayoutManager について学んだ話 Shohei Nagai (@nagais) 2018/12/18 potatotips #57

Slide 2

Slide 2 text

自己紹介 ● 永井翔平(@nagais) ● Androidアプリ開発を初めて1年3ヶ月ほど ● 株式会社アイスタイルにて@cosmeのAndroidアプリ開発に 携わっています ● potatotipsには今回が初参加で、外部の勉強会で登壇するの も今回が初です 2

Slide 3

Slide 3 text

今日お話ししたいこと ● Viewをリサイクルしながらグリッドで表示する方法 ● ViewTypeを元にグリッドの分割数とレイアウトを変更する方 法 3

Slide 4

Slide 4 text

ある時… 4

Slide 5

Slide 5 text

こんな感じの実装依頼が ● タイトル1行とアイテムを複数をセッ トとして繰り返す画面 ● 1行のカラムを2つとする ● タイトルとなるViewはカラム2つ分の 幅を使用する 5 View View View View View View

Slide 6

Slide 6 text

何に困ったか ● グリッドで表示する画面がアプリ内 になく、触ったことがなかった ● GridLayoutManagerが使えそうだっ たが、1行のViewと2カラムの行数 が不規則なViewをセットで繰り返す 方法が不明だった 6 View View View View View View View

Slide 7

Slide 7 text

どうしたか ● グリッド表示のさせ方 ○ GridLayoutManagerをセットしたRecyclerViewを使用した ● 表示するviewの数が不規則 ○ ModelにViewTypeを持たせる ● 1行のViewと2カラムのViewの表示 ○ GridLayoutManagerのSpanSizeLookupを使用した 7

Slide 8

Slide 8 text

グリッド表示のさせ方 8

Slide 9

Slide 9 text

ActivityでのRecyclerViewセッティング 9 private fun initRecyclerView () { binding.recyclerView.adapter = viewModel.customAdapter // 2カラム指定 val layoutManager = GridLayoutManager (this, 2) binding.recyclerView.layoutManager = layoutManager } 1. RecyclerViewのAdapterにCustomAdapterをセット 2. 2カラム指定したGridLayoutManagerのインスタンス作成 3. RecyclerViewのLayoutManagerにセット

Slide 10

Slide 10 text

表示するview数が不規則 10

Slide 11

Slide 11 text

悩んだこと 11 ● 2カラムのデータが可変だったため、 セクション毎にデータをまとめて管理 したくなかった ● 特定のIndexで1行のViewが挿入さ れるわけでなかった Index 1 List Index 0 Index 4 Index 2 Index 3 Index 5 ここが可変

Slide 12

Slide 12 text

動的に表示位置を変更する 方法はないだろうか… 12

Slide 13

Slide 13 text

そうだ! ModelにViewTypeを持たせよう! 13

Slide 14

Slide 14 text

ModelにViewTypeを持たせる data class ABModel(val viewType: CustomAdapter.ViewType, val name: String, val imageUrl: String) 14 ViewType AとBで共通で使用できるModelにViewTypeを持たせ る ViewType A → 一行のView ViewType B → 二行のView enum class ViewType(val type: Int) { A(111), B(112) }

Slide 15

Slide 15 text

1行のViewと2カラムのViewの表示 15

Slide 16

Slide 16 text

Adapter側の処理 override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerViewHolder { return when (viewType) { ViewType.A.type -> else -> } } override fun getItemViewType(position: Int) = modelList[position].viewType.type override fun onBindViewHolder(holder: RecyclerViewHolder?, position: Int) { val model = modelList[position] when (model.viewType) { ViewType.A -> (holder?.binding as ALayoutBinding).viewModel.model = model ViewType.B -> (holder?.binding as BLayoutBinding).viewModel.model = model } } 16 1. 表示するpositionのModelが持つViewTypeを利用 2. ViewTypeに合わせて返却するHolderを変更 3. ViewTypeに合わせてModelをセットするLayout変更

Slide 17

Slide 17 text

カラム数を動的に変更する 17 GridLayoutManagerのSpanSizeLookup()を使用して、 ViewTypeに合わせてカラム数を変更する処理を追加 private fun initRecyclerView () { binding.recyclerView.adapter = viewModel.customAdapter // 2カラム指定 val layoutManager = GridLayoutManager(this, 2) layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { return when (viewModel.customAdapter.getItemViewType(position)) { CustomAdapter.ViewType.A.type -> 2 else -> 1 } } } binding.recyclerView.layoutManager = layoutManager }

Slide 18

Slide 18 text

おわりに ● RecyclerViewとGridLayoutManagerを使用して複数の ViewTypeを想定した場合の実装方法を紹介しました ● 普段使っているライブラリでも、知らないことはまだまだあるな と思いました ● 今後は普段使っているライブラリの中を覗いたり、まだ知らな い使い方があるのではないかを常に考えたい 18