#72 Jetpack ComposeでToDoアプリを作る - ToDoのリスト表示
データベースにToDoを追加するところまで作成できたので、次はリスト表示をやってみます。
ToDoの取得部分を作る
ToDoのリスト表示はメイン画面で行います。まずToDoの一覧を取得する部分を作ります。MainViewModel
にFlowを返すプロパティを追加します。
@HiltViewModel
class MainViewModel @Inject constructor(
private val repo: ToDoRepository
) : ViewModel() {
val todoList = repo.getAll()
}
リポジトリにgetAll()
がないので追加します。
class ToDoRepository {
suspend fun create(title: String, detail: String): ToDo
fun getAll(): Flow<List<ToDo>>
}
実装クラスではDAOのメソッドを呼ぶだけ。お手軽ですね。
class ToDoRepositoryImpl @Inject constructor(
private val dao: ToDoDAO
) : ToDoRepository {
...
override fun getAll(): Flow<List<ToDo>> {
return dao.getAll()
}
}
FlowをJetpack Composeの状態として扱う
ViewModelに追加したFlowはcollectAsState()
を使うことでJetpack
Composeの状態として扱うことができます。追加のライブラリは不要です。
@Composable
fun MainScreen(
navController: NavController,
viewModel: MainViewModel,
) {
val todoList = viewModel.todoList.collectAsState(emptyList())
...
}
StateなのでFlowが最初のデータを流してくるまでの間、初期値として使う値が必要になります。
リスト表示部分を作る
データベースの内容が状態として取得できるようになったので、次はリスト表示部分です。Scaffoldのボディ部分を作ります。
@Composable
fun MainScreen(
navController: NavController,
viewModel: MainViewModel,
) {
val todoList = viewModel.todoList.collectAsState(emptyList())
Scaffold(
topBar = { MainTopBar() },
floatingActionButton = { MainFAB(navController) }
) {
ToDoList(todoList)
}
}
ToDoList()
composableは次のようにしてみます。
@Composable
fun ToDoList(list: State<List<ToDo>>) {
LazyColumn {
items(list.value) { todo ->
ToDoListItem(todo)
}
}
}
LazyColumn()
は、上下方向のリスト表示をやってくれるComposableです。Listに入っているものをリスト表示したい場合はitems()
を使います。
ToDoListItem
はリスト1行分のレイアウトを作るComposableです。今回は次のように実装してみました。
@Composable
fun ToDoListItem(todo: ToDo) {
Column(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 48.dp)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Text(
todo.title,
style = MaterialTheme.typography.subtitle1
)
Text(
DateFormat.format("yyyy-MM-dd hh:mm:ss", todo.created).toString(),
style = MaterialTheme.typography.body2
)
}
}
タイトル表示用のテキストと、作成時刻表示用のテキストを縦に並べたものにしてみました。
行タップ時の処理を実装する
最後に行タップ時の処理を実装します。Modifier.clickable()
で、指定したComposableをタップ可能にすることができます。タップ時の処理は上位のComposableで記述できるよう、ラムダ式を受け取りそれを実行するだけにします。
@Composable
fun ToDoListItem(todo: ToDo, itemSelected: (todo: ToDo) -> Unit) {
Column(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 48.dp)
.clickable { itemSelected(todo) }
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
...
}
}
.padding()
の後に.clickable()
を記述すると、タップ可能な領域がパディングの内側のみになるので.padding()
の前に.clickable()
を記述するようにしましょう。
ToDoListItem
にパラメータを追加したので、ToDoList()
も修正します。
@Composable
fun ToDoList(list: State<List<ToDo>>, itemSelected: (todo: ToDo) -> Unit) {
LazyColumn {
items(list.value) { todo ->
ToDoListItem(todo, itemSelected)
}
}
}
さらにMainScreen()
も修正します。実際の処理は次回やります。
@Composable
fun MainScreen(
navController: NavController,
viewModel: MainViewModel,
) {
val todoList = viewModel.todoList.collectAsState(emptyList())
Scaffold(
topBar = { MainTopBar() },
floatingActionButton = { MainFAB(navController) }
) {
ToDoList(todoList) { todo ->
// 画面遷移は次回
println(it)
}
}
}
動作確認してみます。アダプターやViewHolderを作ることなく、リスト表示ができました。
ここまで作業したものはこちらにあります。