Kotlin/さらばRecyclerView
さて、RecyclerViewへの愚痴を始めましょうか。RecyclerViewは同じようなViewを大量に並べるのによく使ってました。俗にいう一覧画面なんかはWebでもアプリでもよく出てきそうなもんです。ただAndroidアプリでは、とにかく大量のデータを表示しなければならない(かもしれない)ので、メモリの消費やパフォーマンスの観点から、一覧表一つ作るのにも、細かく最適化できるような仕組み(= RecyclerView)が用意されていたんだと思います。最近、実装してなかったのでうろ覚えですが、まず、RecyclerViewと表示データをつなぐような役割のアダプターが必要でしたね。(RecyclerView.Adapter)それから一覧表示される各アイテムの状態を表すようなItemDetailsなんてのも作りましたね。各アイテムは画面によって違うのでそれぞれカスタマイズされたものを用意するわけです。表の中身が更新されたときなんかもいちいちnotifyして画面の更新タイミングを伝えなきゃいけないし、なんか似たようなちょっと違うものを何度も作り続けなきゃいけないのが地味に苦痛でした。きっともっと楽な方法があるんだろうなと思いつつ、むやみに一覧表を作り続けてきたのでした。
今回、それを全部Composableな関数に書き換えてやることにしました。
if (mainViewModel.itemSearching.value) {
Text(
text = "アイテムを探してる最中です。"
)
} else {
LazyColumn {
// ...
)
}
まず、一覧表の前に実装したのが、「表に表示されるアイテムは揃っているかどうか」という点についてですね。揃ってるかどうかを状態としてViewModelの中で持たせたので、それを参考に「揃ってなかった場合の表示」も出せるように処理を分岐させます。いや、アプリに載せる広告の掲載業者さんが「画面に何も表示されないタイミングがある!」ってことで広告の配信を停止してくることがあったので気を付けているのですが、そもそもユーザーにとっても不親切なので、大事なポイントなんでしょうね。表示件数が0件のときなんかも気を付けたいですね。
var uniqueKey = mainViewModel.uniqueKey
var selectedKey by remember { mutableStateOf(uniqueKey) }
LazyColumn {
items(mainViewModel.itemList.value.count()) { i ->
Card(
modifier = Modifier.fillMaxWidth().padding(8.dp),
colors = CardDefaults.cardColors(
containerColor = if (mainViewModel.itemList.value[i].uniqueKey == selectedKey) Color(0xFFE3F2FD) else Color.White
),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
onClick = {
selectedKey = mainViewModel.itemList.value[i].uniqueKey
uniqueKey = mainViewModel.itemList.value[i].uniqueKey
}
) {
Text(
text = mainViewModel.itemList.value[i].name,
fontWeight = if (mainViewModel.itemList.value[i].uniqueKey == selectedKey) FontWeight.Bold else FontWeight.Normal,
modifier = Modifier.padding(8.dp)
)
}
}
}
続いて、脱RecyclerViewの肝ともいえるLazyColumn を書いていきましょう。Columnと違って遅れて(Lazy)中身が決定される一覧構成要素という認識です。動的なダイナミックなColumnって感じでしょうか。中身の変更を受けて画面が更新されるところが、これまで学んできたComposableな関数によるレイアウトのいいところですね。配列の中身が変更する度に、画面と配列データの同期を自分でとっていたあの頃とはオサラバです。
今回の一覧表の中身はViewModelにitemListという名前でリストを用意しました。mutableStateOf(emptyList<A>()) みたいな感じで空のリストの状態という形で持っています。Aという型はuniqueKeyという一意のキーとnameという名前を表す文字列をプロパティに持たせてあります。名前は一覧表にTextで表示されます。Textの親はCardというComposableパーツで、まぁ、いろんなパーツをひとまとめに括るのに丁度良いやつです。型A一つにつき一枚のカードが増えるわけですね。物で例えられると理解が進みやすくていいですね。
LazyColumn はitemsというループを構成できるらしく、ここに先のリスト(itemList)を渡すと、その中身の数の分、ループが回ってCardが揃います。
CardにはonClick をつけてみました。クリック(タップ)されたときにアイテムのuniqueKeyを記録します。記録先は冒頭でrememberしたselectedKey です。これが記録(更新)されたことをトリガーに一覧表が更新され、selectedKey と各アイテムのuniqueKey が比較され、一致したら
- Cardの背景色を選択色に
- 名前のテキスト部分のフォントを太字に
と表示が変更されるようにしました。クリックで何が選択されているかは記録されているので、何かを選択するUIがこれだけで完成しました。表示項目もこのComposableな関数を変更して自由に拡張できますね。RecyclerViewのときはCardにあたる中身を表すレイアウトXMLをファイルツリーから探してこなくちゃでしたね~。あっちこっちのファイルをちょこちょこ直して回るの、性に合わなくて嫌いだったんですよね。Composableな関数はRecyclerViewを捨てられたことが一番うれしいかもしれません、今のところ。
とはいえ、今まで書き散らしてきたRecyclerViewがいっぱいあるし、それぞれ微妙に違うカスタマイズが入っていたりするので、しばらくは立ち止まりながら書き換えていくことになりそうです。
- Kotlin/リストをスワイプして消したい少しずつ、Jetpack Compose化計画を進めていて、もうそろそろXMLのレイアウトから脱却できそうな見込みです。 2025/10/5 要素を一覧するよくâ¦
- Kotlin/Composableで構成では、画面に必要な要件を挙げていって、実際にComposableな要素を詰め込んでいってみましょう。今回作るのは簡素なファイラーです。とりあえず、ファイルを一覧â¦
- Kotlin/ViewModelによる状態管理その2前回、データ(状態、ステート)を持っちゃっていたカスタムViewをComposable向けに書きなおすところまで進みました。いろんな話がでてきて頭の整理がおいつâ¦
- Kotlin/Composableに移行する2前回 、やっとのことでレイアウトXMLをsetContentViewで読み込む方式から脱却し、setContentで@Composableなレイアウト部品を読みâ¦
Kotlin/ViewModelによる状態管理既存のXMLによる画面レイアウトをComposableな関数による画面レイアウトに書き換えてみう試みは先日、うまくいって終わったんですが、微妙に消化不良なことがâ¦- Kotlin/NavControllerで遷移ドロワーを持つレイアウトを作ろうと思ったけど、現在のScaffoldだけではできなくなってて、ModalNavigationDrawerで囲ってやればよかったんâ¦
- 開発日記/2008年の日記GoogleAPIよりGDownloadUrlを使って、サイト内のドキュメントにアクセスしていた部分が、つい先日いきなりエラーを出すようになった。 GDownlâ¦
- Kotlin/ドロワーに再挑戦前回、ScaffoldからdrawerContentを指定してドロワーを実装しようとしたら、今はその手が通じないと言われたのでした。マテリアル3(M3)への過渡â¦
- Kotlin/ダイアログを出すもう、元からあったボタンを押したときの処理みたいなのを新しいComposableなボタンに関連付けていけば作業も終わりなのかな、って思ってましたが、まだまだやるâ¦
- Kotlin/状態を持つComposableなレイアウトを書き始めて、いくつかの部品をつなげて、画面の切り替えぐらいはできるようになりました。切り替わった先で、表示するものを変えれば何â¦