スクロール可能なリストを追加する

1. 始める前に

この Codelab では、Jetpack Compose を使用してアプリでスクロール可能なリストを作成する方法を学びます。

アファメーションのリストを日常をポジティブにする美しい画像とともに表示する Affirmations アプリを取り扱います。

データはすでに用意されているので、そのデータを取得して UI に表示するだけです。

前提条件

  • Kotlin のリストに精通していること
  • Jetpack Compose でレイアウトを作成した経験
  • デバイスまたはエミュレータでアプリを実行した経験

学習内容

  • Jetpack Compose を使用してマテリアル デザイン カードを作成する方法
  • Jetpack Compose を使用してスクロール可能なリストを作成する方法

作成するアプリの概要

  • 既存のアプリケーションをベースとして、UI にスクロール可能なリストを追加する

最終的なプロダクトは次のようになります。

286f5132aa155fa6.png

必要なもの

  • インターネットにアクセスできるパソコン、ウェブブラウザ、Android Studio
  • GitHub へのアクセス

スターター コードをダウンロードする

Android Studio で basic-android-kotlin-compose-training-affirmations フォルダを開きます。

アプリが starter ブランチコードからビルドされた場合、空白の画面を表示することが想定されています。

3beea0789e2eeaba.png

2. リストアイテムのデータクラスを作成する

アファメーション用のデータクラスを作成する

Android アプリでは、リストはリストアイテムで構成されます。単一のデータを持つリストアイテムの場合、これは文字列や整数などの単純なアイテムになります。画像とテキストなど、複数のデータを持つリストアイテムの場合、それらすべてのプロパティを格納するクラスが必要になります。データクラスはプロパティのみを格納するクラスの一種で、このようなプロパティを扱うユーティリティ メソッドを提供します。

  1. com.example.affirmations の下に新しいパッケージを作成します。

89c8d8485c685fac.png

新しいパッケージに model という名前を付けます。この model パッケージには、データクラスで表されるデータモデルが含まれます。このデータクラスは、「アファメーション」となるものに関連する情報を表すプロパティで構成されます。アファメーションは、文字列リソースと画像リソースを 1 つずつ含みます。パッケージとは、クラスと他のディレクトリを含むディレクトリのことです。

b54fb6bf57de44c8.png

  1. com.example.affirmations.model パッケージ内に新しいクラスを作成します。

58510a651bd49100.png

新しいクラスに Affirmation という名前を付け、これを Data Class にします。

7f94b65ee3d8407f.png

  1. Affirmation は、1 つの画像と 1 つの文字列で構成されます。Affirmation データクラスに 2 つの val プロパティを作成します。一つは stringResourceId、もう一つは imageResourceId という名前にします。どちらも整数にする必要があります。

Affirmation.kt

data class Affirmation(
    val stringResourceId: Int,
    val imageResourceId: Int
)
  1. stringResourceId プロパティに @StringRes アノテーションを付け、imageResourceId@DrawableRes アノテーションを付けます。stringResourceId は、文字列リソースに格納されているアファメーション テキストの ID を表します。imageResourceId は、ドローアブル リソースに格納されているアファメーション画像の ID を表します。

Affirmation.kt

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes

data class Affirmation(
    @StringRes val stringResourceId: Int,
    @DrawableRes val imageResourceId: Int
)
  1. com.example.affirmations.data パッケージで Datasource.kt ファイルを開き、2 つの import ステートメントと Datasource クラスのコンテンツのコメント化を解除します。

Datasource.kt

import com.example.affirmations.R
import com.example.affirmations.model.Affirmation

class Datasource() {
    fun loadAffirmations(): List<Affirmation> {
        return listOf<Affirmation>(
            Affirmation(R.string.affirmation1, R.drawable.image1),
            Affirmation(R.string.affirmation2, R.drawable.image2),
            Affirmation(R.string.affirmation3, R.drawable.image3),
            Affirmation(R.string.affirmation4, R.drawable.image4),
            Affirmation(R.string.affirmation5, R.drawable.image5),
            Affirmation(R.string.affirmation6, R.drawable.image6),
            Affirmation(R.string.affirmation7, R.drawable.image7),
            Affirmation(R.string.affirmation8, R.drawable.image8),
            Affirmation(R.string.affirmation9, R.drawable.image9),
            Affirmation(R.string.affirmation10, R.drawable.image10))
    }
}

3. アプリにリストを追加する

リストアイテム カードを作成する

このアプリはアファメーションのリストを表示することを想定しています。リストを表示するための UI を構成する最初のステップとして、リストアイテムを作成します。各アファメーション アイテムは 1 つの画像と 1 つの文字列で構成されます。これらの各アイテムのデータにはスターター コードが付属しています。このようなアイテムを表示するために、UI コンポーネントを作成します。

アイテムは、Image コンポーザブルと Text コンポーザブルを含む Card コンポーザブルで構成されます。Compose における Card は、コンテンツとアクションを単一のコンテナで表示するサーフェスです。プレビューで、アファメーション カードは次のようになります。

4f657540712a069f.png

カードには、画像と、その下にテキストが表示されます。この縦型のレイアウトは、Card コンポーザブルでラップされた Column コンポーザブルを使用することで実現できます。自分で試してみるか、以下の手順に沿って作成してください。

  1. MainActivity.kt ファイルを開きます。
  2. AffirmationsApp() メソッドの下に AffirmationCard() という新しいメソッドを作成し、@Composable アノテーションを付けます。

MainActivity.kt

@Composable
fun AffirmationsApp() {
}

@Composable
fun AffirmationCard() {

}
  1. メソッド シグネチャを編集して、Affirmation オブジェクトをパラメータとして取得します。Affirmation オブジェクトは model パッケージのものです。

MainActivity.kt

import com.example.affirmations.model.Affirmation

@Composable
fun AffirmationCard(affirmation: Affirmation) {

}
  1. 署名に modifier パラメータを追加します。パラメータのデフォルト値 Modifier を設定します。

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {

}
  1. AffirmationCard メソッド内で、Card コンポーザブルを呼び出します。modifier パラメータを渡します。

MainActivity.kt

import androidx.compose.material3.Card

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {

    }
}
  1. Card コンポーザブル内に Column コンポーザブルを追加します。Column コンポーザブル内のアイテムは、UI 上で縦方向に配置されます。これで、関連するテキストの上に画像を配置できます。逆に、Row コンポーザブル内のアイテムは横方向に配置されます。

MainActivity.kt

import androidx.compose.foundation.layout.Column

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {

        }
    }

}
  1. Image コンポーザブルを Column コンポーザブルのラムダ本文内に追加します。Image コンポーザブルには、表示するリソースと contentDescription が常に必要です。リソースは、painter パラメータに渡される painterResource である必要があります。painterResource メソッドは、ベクター型ドローアブルか、PNG などのラスター化されたアセット形式を読み込みます。また、contentDescription パラメータとして stringResource を渡します。

MainActivity.kt

import androidx.compose.foundation.Image
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
            )
        }
    }
}
  1. painter パラメータと contentDescription パラメータに加えて、modifiercontentScale を渡します。contentScale は、画像の拡大方法と表示方法を決定します。Modifier オブジェクトの fillMaxWidth 属性を設定して、高さを 194.dp にする必要があります。contentScale は、ContentScale.Crop である必要があります。

MainActivity.kt

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.unit.dp
import androidx.compose.ui.layout.ContentScale

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(194.dp),
                contentScale = ContentScale.Crop
            )
        }
    }
}
  1. Column 内で、Image コンポーザブルの後に Text コンポーザブルを作成します。affirmation.stringResourceIdstringResourcetext パラメータに渡し、padding 属性を 16.dp に設定した Modifier オブジェクトを渡します。また、MaterialTheme.typography.headlineSmallstyle パラメータに渡してテキストテーマを設定します。

MainActivity.kt

import androidx.compose.material3.Text
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.platform.LocalContext

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(194.dp),
                contentScale = ContentScale.Crop
            )
            Text(
                text = LocalContext.current.getString(affirmation.stringResourceId),
                modifier = Modifier.padding(16.dp),
                style = MaterialTheme.typography.headlineSmall
            )
        }
    }
}

AffirmationCard コンポーザブルのプレビュー

Affirmations アプリの UI の中核となるカードを作成することができました。カードが正しく表示されることを確認するために、アプリ全体を起動せずにプレビューできるコンポーザブルを作成します。

  1. AffirmationCardPreview() という名前のプライベート メソッドを作成します。メソッドに @Preview@Composable のアノテーションを付けます。

MainActivity.kt

import androidx.compose.ui.tooling.preview.Preview

@Preview
@Composable
private fun AffirmationCardPreview() {

}
  1. メソッド内で AffirmationCard コンポーザブルを呼び出します。そのコンポーザブルに、コンストラクタに渡された R.string.affirmation1 文字列リソースと R.drawable.image1 ドローアブル リソースを使用する新しい Affirmation オブジェクトを渡します。

MainActivity.kt

@Preview
@Composable
private fun AffirmationCardPreview() {
    AffirmationCard(Affirmation(R.string.affirmation1, R.drawable.image1))
}
  1. [Split] タブを開くと、AffirmationCard のプレビューが表示されます。必要に応じて、[Design] ペインで [Build & Refresh] をクリックしてプレビューを表示します。

924a4df2c1db236c.png

リストを作成する

リストアイテムのコンポーネントがリストの唯一のビルディング ブロックです。リストアイテムを作成したら、それを利用してリスト コンポーネントそのものを作成できます。

  1. AffirmationList() という関数を作成し、@Composable アノテーションを付けて、Affirmation オブジェクトの List をメソッド シグネチャのパラメータとして宣言します。

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>) {

}
  1. デフォルト値を Modifier にして、メソッド シグネチャのパラメータとして modifier オブジェクトを宣言します。

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {

}
  1. Jetpack Compose では、LazyColumn コンポーザブルを使用してスクロール可能なリストを作成できます。LazyColumnColumn の違いは、表示するアイテム数が少ない場合は Column を使用する必要がある点です(Compose は一度にすべてのアイテムを読み込むため)。Column には、事前に定義した(つまり一定の)数のコンポーザブルのみを格納できます。LazyColumn はオンデマンドでコンテンツを追加できるため、長いリスト、特にリストの長さが不明な場合に適しています。LazyColumn もデフォルトでスクロール機能を提供します。追加のコードは必要ありません。AffirmationList() 関数内で LazyColumn コンポーザブルを宣言します。modifier オブジェクトを引数として LazyColumn に渡します。

MainActivity.kt

import androidx.compose.foundation.lazy.LazyColumn

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {

    }
}
  1. LazyColumn のラムダ本体で、items() メソッドを呼び出して affirmationList を渡します。items() メソッドは、アイテムを LazyColumn に追加するためのものです。このメソッドは、このコンポーザブルに固有のもので、他のコンポーザブルでは一般的ではありません。

MainActivity.kt

import androidx.compose.foundation.lazy.items

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) {

        }
    }
}
  1. items() メソッドの呼び出しにはラムダ関数が必要です。この関数で、affirmationList の 1 つのアファメーション アイテムを表す affirmation のパラメータを指定します。

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) { affirmation ->

        }
    }
}
  1. リスト内のアファメーションごとに AffirmationCard() コンポーザブルを呼び出します。affirmation と、padding 属性を 8.dp に設定した Modifier オブジェクトを渡します。

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) { affirmation ->
            AffirmationCard(
                affirmation = affirmation,
                modifier = Modifier.padding(8.dp)
            )
        }
    }
}

リストを表示する

  1. AffirmationsApp コンポーザブルで、現在のレイアウトの向きを取得し、変数に保存します。これらは、後でパディングを設定するために使用されます。

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
}
  1. 次に、Surface コンポーザブルを作成します。このコンポーザブルは、AffirmationsList コンポーザブルのパディングを設定します。

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface() {
    }
}
  1. 親の最大幅と最大高さを埋め、ステータスバーのパディングを設定し、左右のパディングを layoutDirection に設定する ModifierSurface コンポーザブルに渡します。たとえば、LayoutDirection オブジェクトをパディングに変換する場合は、WindowInsets.safeDrawing.asPaddingValues().calculateStartPadding(layoutDirection) のようにします。

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        Modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .padding(
            start = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateStartPadding(layoutDirection),
            end = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateEndPadding(layoutDirection),
        ),
    ) {
    }
}
  1. Surface コンポーザブルのラムダで、AffirmationList コンポーザブルを呼び出し、DataSource().loadAffirmations()affirmationList パラメータに渡します。

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        Modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .padding(
            start = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateStartPadding(layoutDirection),
            end = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateEndPadding(layoutDirection),
        ),
    ) {
        AffirmationsList(
            affirmationList = Datasource().loadAffirmations(),
        )
    }
}

デバイスまたはエミュレータで Affirmations アプリを実行すると、完成したプロダクトが表示されます。

286f5132aa155fa6.png

4. 解答コードを取得する

この Codelab の完成したコードをダウンロードするには、以下の git コマンドを使用します。

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-affirmations.git
$ cd basic-android-kotlin-compose-training-affirmations
$ git checkout intermediate

または、リポジトリを ZIP ファイルとしてダウンロードし、Android Studio で開くこともできます。

解答コードを確認する場合は、GitHub で表示します

5. おわりに

ここでは、Jetpack Compose でカード、リストアイテム、スクロール可能なリストを作成する方法を学習しました。これらはあくまでリスト作成の基本ツールであり、リストアイテムは、創造性を発揮して自由にカスタマイズできます。

まとめ

  • Card コンポーザブルを使用してリストアイテムを作成します。
  • Card コンポーザブル内の UI を変更します。
  • LazyColumn コンポーザブルを使用してスクロール可能なリストを作成します。
  • カスタムのリストアイテムを使用してリストを作成します。