1. 始める前に
はじめに
この時点で、Compose を使用したアプリの作成に精通し、XML、ビュー、ビュー バインディング、Fragment を使ったアプリの作成についてある程度理解しているはずです。ビューでアプリを構築したことで、Compose のような宣言型 UI でアプリを構築することの便利さに気付いたでしょう。しかし、Compose ではなくビューを使用するほうが合理的な場合があります。この Codelab では、ビューの相互運用機能を使用して、ビュー コンポーネントをモダンな Compose アプリに追加する方法を学びます。
この Codelab の執筆時点では、作成する予定の UI コンポーネントは Compose ではまだ使用できません。これはビュー相互運用機能を活用する絶好の機会です。
前提条件:
- ビューで Android アプリを作成するの Codelab で Compose での Android の基礎コースワークを完了していること。
必要なもの
- Android Studio がインストールされた、インターネットに接続できるパソコン。
- デバイスまたはエミュレータ。
- Juice Tracker アプリのスターター コード。
作成するアプリの概要
この Codelab では、Compose UI に Spinner、RatingBar、AdView の 3 つのビューを統合して、Juice Tracker アプリの UI を完成させる必要があります。これらのコンポーネントを作成するには、ビュー相互運用機能(またはビュー相互運用)を使用します。ビュー相互運用機能を使用して、ビューをコンポーズ可能な関数でラップすることで、実際にアプリにビューを追加できます。
コードのチュートリアル
この Codelab では、ビューで Android アプリを作成する の Codelab と Compose をビューベースのアプリに追加するの Codelab と同じ JuiceTracker アプリを使用します。このバージョンとの違いは、提供されているスターター コードが Compose だけでできている点です。このアプリは現在、入力ダイアログ シートとリスト画面上部の広告バナーに、色と評価の入力がありません。
bottomsheet
ディレクトリには、入力ダイアログに関連するすべての UI コンポーネントが含まれています。このパッケージには、作成時に色と評価の入力用の UI コンポーネントが含まれている必要があります。
homescreen
には、ホーム画面でホストされている UI コンポーネントが含まれます。これには JuiceTracker リストが含まれます。最終的には、このパッケージには作成時に広告バナーが含まれるようにする必要があります。
ボトムシートやジュースリストなどのメインの UI コンポーネントは JuiceTrackerApp.kt
ファイルでホストされています。
2. スターター コードを取得する
まず、スターター コードをダウンロードします。
または、GitHub リポジトリのクローンを作成してコードを入手することもできます。
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout compose-starter
- Android Studio で
basic-android-kotlin-compose-training-juice-tracker
フォルダを開きます。 - Android Studio で Juice Tracker アプリコードを開きます。
3. Gradle 構成
Play 開発者サービスの広告の依存関係をアプリの build.gradle.kts
ファイルに追加します。
app/build.gradle.kts
android {
...
dependencies {
...
implementation("com.google.android.gms:play-services-ads:22.2.0")
}
}
4. セットアップ
Android マニフェストの activity
タグの上に次の値を追加して、テスト用の広告バナーを有効にします。
AndroidManifest.xml
...
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713" />
...
5. 入力ダイアログを完成させる
このセクションでは、カラースピナーと評価バーを作成し、入力ダイアログを完成させます。カラースピナーは色を選択するコンポーネントであり、評価バーではジュースの評価を選択できます。次のデザインを確認してください。
カラースピナーを作成する
Compose でスピナーを実装するには、Spinner
クラスを使用する必要があります。Spinner
はコンポーズ可能な関数ではなくビュー コンポーネントであるため、相互運用機能を使用して実装する必要があります。
bottomsheet
ディレクトリにColorSpinnerRow.kt
という名前の新しいファイルを作成します。- ファイル内に
SpinnerAdapter
という新しいクラスを作成します。 SpinnerAdapter
のコンストラクタで、Int
パラメータを受け取るonColorChange
というコールバック パラメータを定義します。SpinnerAdapter
は、Spinner
のコールバック関数を処理します。
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit){
}
AdapterView.OnItemSelectedListener
インターフェースを実装します。
このインターフェースを実装すると、スピナーのクリック動作を定義できます。このアダプタは、後ほどコンポーズ可能な関数で設定します。
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit): AdapterView.OnItemSelectedListener {
}
AdapterView.OnItemSelectedListener
のメンバー関数であるonItemSelected()
とonNothingSelected()
を実装します。
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit): AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
TODO("Not yet implemented")
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
- 色を選択するとアプリで UI の選択された値が更新されるように、
onItemSelected()
関数を変更してonColorChange()
コールバック関数を呼び出します。
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit): AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
onColorChange(position)
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
- 何も選択しないときは最初の色(赤)がデフォルトの色になるように、
onNothingSelected()
関数を変更して色を0
に設定します。
bottomsheet/ColorSpinnerRow.kt
class SpinnerAdapter(val onColorChange: (Int) -> Unit): AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
onColorChange(position)
}
override fun onNothingSelected(parent: AdapterView<*>?) {
onColorChange(0)
}
}
コールバック関数を通じてスピナーの動作を定義する SpinnerAdapter
は作成済みです。ここで、スピナーの内容を作成し、それにデータを設定する必要があります。
ColorSpinnerRow.kt
ファイル内、かつSpinnerAdapter
クラスの外部で、ColorSpinnerRow
という新しいコンポーズ可能な関数を作成します。ColorSpinnerRow()
のメソッド シグネチャに、スピナー位置用のInt
パラメータ、Int
パラメータを受け取るコールバック関数、修飾子を追加します。
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
colorSpinnerPosition: Int,
onColorChange: (Int) -> Unit,
modifier: Modifier = Modifier
) {
}
- この関数内で、
JuiceColor
列挙型を使用してジュースの色文字列リソースの配列を作成します。この配列がスピナーに入力される内容となります。
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
colorSpinnerPosition: Int,
onColorChange: (Int) -> Unit,
modifier: Modifier = Modifier
) {
val juiceColorArray =
JuiceColor.values().map { juiceColor -> stringResource(juiceColor.label) }
}
InputRow()
コンポーズ可能関数を追加し、入力ラベル用の色文字列リソースと、Spinner
が表示される入力行を定義する修飾子を渡します。
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
colorSpinnerPosition: Int,
onColorChange: (Int) -> Unit,
modifier: Modifier = Modifier
) {
val juiceColorArray =
JuiceColor.values().map { juiceColor -> stringResource(juiceColor.label) }
InputRow(inputLabel = stringResource(R.string.color), modifier = modifier) {
}
}
次に、Spinner
を作成します。Spinner
は View クラスであるため、Compose のビュー相互運用 API を利用してコンポーズ可能な関数にラップする必要があります。これを実現するには、AndroidView
コンポーズ可能関数を使用します。
- Compose で
Spinner
を使用するために、InputRow
ラムダ本体で、AndroidView()
コンポーズ可能関数を作成します。AndroidView()
コンポーズ可能関数は、コンポーズ可能な関数内でビューの要素または階層を作成します。
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
colorSpinnerPosition: Int,
onColorChange: (Int) -> Unit,
modifier: Modifier = Modifier
) {
val juiceColorArray =
JuiceColor.values().map { juiceColor -> stringResource(juiceColor.label) }
InputRow(inputLabel = stringResource(R.string.color), modifier = modifier) {
AndroidView()
}
}
AndroidView
コンポーズ可能関数は、次の 3 つのパラメータを取ります。
factory
ラムダ。ビューを作成する関数です。update
コールバック。factory
で作成されたビューがインフレートされるときに呼び出されます。- コンポーズ可能な関数の修飾子
modifier
。
AndroidView
を実装するために、まず修飾子を渡し、画面の最大幅を占有させます。factory
パラメータにラムダを渡します。factory
ラムダはパラメータとしてContext
を受け取ります。Spinner
クラスを作成してコンテキストを渡します。
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
colorSpinnerPosition: Int,
onColorChange: (Int) -> Unit,
modifier: Modifier = Modifier
) {
...
InputRow(...) {
AndroidView(
modifier = Modifier.fillMaxWidth(),
factory = { context ->
Spinner(context)
}
)
}
}
RecyclerView.Adapter
が RecyclerView
にデータを提供するのと同じように、ArrayAdapter
は Spinner
にデータを提供します。Spinner
には、色の配列を保持するアダプタが必要です。
ArrayAdapter
を使用してアダプタを設定します。ArrayAdapter
には、コンテキスト、XML レイアウト、配列が必要です。レイアウトにsimple_spinner_dropdown_item
を渡します。このレイアウトは、Android のデフォルトとして提供されています。
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
colorSpinnerPosition: Int,
onColorChange: (Int) -> Unit,
modifier: Modifier = Modifier
) {
...
InputRow(...) {
AndroidView(
modifier = Modifier.fillMaxWidth(),
factory = { context ->
Spinner(context).apply {
adapter =
ArrayAdapter(
context,
android.R.layout.simple_spinner_dropdown_item,
juiceColorArray
)
}
}
)
}
}
factory
コールバックは、内部で作成された View のインスタンスを返します。update
は、factory
コールバックから返されるのと同じ型のパラメータを受け取るコールバックです。このパラメータは、factory
によってインフレートされる View のインスタンスです。この例では、factory 内で Spinner
が作成されたため、その Spinner
のインスタンスは update
ラムダ本体でアクセスできます。
spinner
を渡すupdate
コールバックを追加します。update
で提供されるコールバックを使用してsetSelection()
メソッドを呼び出します。
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
colorSpinnerPosition: Int,
onColorChange: (Int) -> Unit,
modifier: Modifier = Modifier
) {
...
InputRow(...) {
//...
},
update = { spinner ->
spinner.setSelection(colorSpinnerPosition)
spinner.onItemSelectedListener = SpinnerAdapter(onColorChange)
}
)
}
}
- 前に作成した
SpinnerAdapter
を使用して、update
でonItemSelectedListener()
コールバックを設定します。
bottomsheet/ColorSpinnerRow.kt
...
@Composable
fun ColorSpinnerRow(
colorSpinnerPosition: Int,
onColorChange: (Int) -> Unit,
modifier: Modifier = Modifier
) {
...
InputRow(...) {
AndroidView(
// ...
},
update = { spinner ->
spinner.setSelection(colorSpinnerPosition)
spinner.onItemSelectedListener = SpinnerAdapter(onColorChange)
}
)
}
}
これでカラースピナー コンポーネントのコードが完成しました。
- 次のユーティリティ関数を追加して、
JuiceColor
の列挙型インデックスを取得します。これは次のステップで使用します。
private fun findColorIndex(color: String): Int {
val juiceColor = JuiceColor.valueOf(color)
return JuiceColor.values().indexOf(juiceColor)
}
EntryBottomSheet.kt
ファイルのSheetForm
コンポーズ可能関数にColorSpinnerRow
を実装します。カラースピナーは「説明」テキストの後、ボタンの上に配置します。
bottomsheet/EntryBottomSheet.kt
...
@Composable
fun SheetForm(
juice: Juice,
onUpdateJuice: (Juice) -> Unit,
onCancel: () -> Unit,
onSubmit: () -> Unit,
modifier: Modifier = Modifier,
) {
...
TextInputRow(
inputLabel = stringResource(R.string.juice_description),
fieldValue = juice.description,
onValueChange = { description -> onUpdateJuice(juice.copy(description = description)) },
modifier = Modifier.fillMaxWidth()
)
ColorSpinnerRow(
colorSpinnerPosition = findColorIndex(juice.color),
onColorChange = { color ->
onUpdateJuice(juice.copy(color = JuiceColor.values()[color].name))
}
)
ButtonRow(
modifier = Modifier
.align(Alignment.End)
.padding(bottom = dimensionResource(R.dimen.padding_medium)),
onCancel = onCancel,
onSubmit = onSubmit,
submitButtonEnabled = juice.name.isNotEmpty()
)
}
}
評価入力を作成する
bottomsheet
ディレクトリにRatingInputRow.kt
という新しいファイルを作成します。RatingInputRow.kt
ファイルで、RatingInputRow()
という新しいコンポーズ可能な関数を作成します。- メソッド シグネチャで、評価用の
Int
、選択変更を処理するInt
パラメータを含むコールバック、修飾子を渡します。
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
}
- 次のサンプルコードに示すように、
ColorSpinnerRow
と同様に、AndroidView
を含むコンポーズ可能な関数にInputRow
を追加します。
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
InputRow(inputLabel = stringResource(R.string.rating), modifier = modifier) {
AndroidView(
factory = {},
update = {}
)
}
}
factory
ラムダ本体で、RatingBar
クラスのインスタンスを作成します。これにより、このデザインに必要な評価バーのタイプを指定します。stepSize
を1f
に設定して、評価が整数のみになるようにします。
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
InputRow(inputLabel = stringResource(R.string.rating), modifier = modifier) {
AndroidView(
factory = { context ->
RatingBar(context).apply {
stepSize = 1f
}
},
update = {}
)
}
}
ビューがインフレートされると、評価が設定されます。前述の通り、factory
は RatingBar
のインスタンスを update コールバックに返します。
- コンポーズ可能な関数に渡される評価を使用して、
update
ラムダ本体でRatingBar
インスタンスに評価を設定します。 - 新しい評価を設定したら、
RatingBar
コールバックを使用してonRatingChange()
コールバック関数を呼び出し、UI で評価を更新します。
bottomsheet/RatingInputRow.kt
@Composable
fun RatingInputRow(rating:Int, onRatingChange: (Int) -> Unit, modifier: Modifier = Modifier){
InputRow(inputLabel = stringResource(R.string.rating), modifier = modifier) {
AndroidView(
factory = { context ->
RatingBar(context).apply {
stepSize = 1f
}
},
update = { ratingBar ->
ratingBar.rating = rating.toFloat()
ratingBar.setOnRatingBarChangeListener { _, _, _ ->
onRatingChange(ratingBar.rating.toInt())
}
}
)
}
}
これで、評価入力のコンポーズ可能な関数が完成しました。
EntryBottomSheet
でRatingInputRow()
コンポーズ可能関数を使用します。カラースピナーの後、ボタンの上に配置します。
bottomsheet/EntryBottomSheet.kt
@Composable
fun SheetForm(
juice: Juice,
onUpdateJuice: (Juice) -> Unit,
onCancel: () -> Unit,
onSubmit: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
...
ColorSpinnerRow(
colorSpinnerPosition = findColorIndex(juice.color),
onColorChange = { color ->
onUpdateJuice(juice.copy(color = JuiceColor.values()[color].name))
}
)
RatingInputRow(
rating = juice.rating,
onRatingChange = { rating -> onUpdateJuice(juice.copy(rating = rating)) }
)
ButtonRow(
modifier = Modifier.align(Alignment.CenterHorizontally),
onCancel = onCancel,
onSubmit = onSubmit,
submitButtonEnabled = juice.name.isNotEmpty()
)
}
}
広告バナーを作成する
homescreen
パッケージで、AdBanner.kt
という新しいファイルを作成します。AdBanner.kt
ファイルで、AdBanner()
という新しいコンポーズ可能な関数を作成します。
前に作成したコンポーズ可能な関数とは異なり、AdBanner
には入力は必要ありません。そのため、InputRow
コンポーズ可能関数でラップする必要はありません。ただし、AndroidView
は必要です。
AdView
クラスを使用して、自作のバナーを作成してみましょう。広告サイズをAdSize.BANNER
、広告ユニット ID を"ca-app-pub-3940256099942544/6300978111"
に設定してください。AdView
がインフレートされたら、AdRequest Builder
を使用して広告を読み込みます。
homescreen/AdBanner.kt
@Composable
fun AdBanner(modifier: Modifier = Modifier) {
AndroidView(
modifier = modifier,
factory = { context ->
AdView(context).apply {
setAdSize(AdSize.BANNER)
// Use test ad unit ID
adUnitId = "ca-app-pub-3940256099942544/6300978111"
}
},
update = { adView ->
adView.loadAd(AdRequest.Builder().build())
}
)
}
JuiceTrackerApp
で、JuiceTrackerList
の前にAdBanner
を配置します。JuiceTrackerList
は 83 行目で宣言されています。
ui/JuiceTrackerApp.kt
...
AdBanner(
Modifier
.fillMaxWidth()
.padding(
top = dimensionResource(R.dimen.padding_medium),
bottom = dimensionResource(R.dimen.padding_small)
)
)
JuiceTrackerList(
juices = trackerState,
onDelete = { juice -> juiceTrackerViewModel.deleteJuice(juice) },
onUpdate = { juice ->
juiceTrackerViewModel.updateCurrentJuice(juice)
scope.launch {
bottomSheetScaffoldState.bottomSheetState.expand()
}
},
)
6. 解答コードを取得する
この Codelab の完成したコードをダウンロードするには、以下の git コマンドを使用します。
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout compose-with-views
または、リポジトリを ZIP ファイルとしてダウンロードし、Android Studio で開くこともできます。
解答コードを確認する場合は、GitHub で表示します。
7. 関連リンク
8. 最後に
このコースはここまでですが、これは Android アプリの開発の始まりにすぎません。
このコースでは、ネイティブ Android アプリを作成するための最新の UI ツールキットである Jetpack Compose を使用してアプリを作成する方法を学習しました。このコースでは、リストを使ったアプリ(単一画面または複数画面)を作成し、それらの間を移動しました。インタラクティブなアプリを作成し、ユーザー入力への応答を実装して、UI を更新する方法を学びました。マテリアル デザインを適用し、色、シェイプ、タイポグラフィを使用してアプリにテーマを設定しました。また、Jetpack とその他のサードパーティ ライブラリを使用して、タスクのスケジュール設定、リモート サーバーからのデータの取得、ローカルでのデータの保持などを行いました。
このコースを完了することで、Jetpack Compose を使用して美しくレスポンシブなアプリを作成する方法をよく理解しただけでなく、効率的でメンテナンスしやすく、視覚的に魅力のある Android アプリを作成するために必要な知識とスキルを身に付けることができました。この基礎知識を身に付けておけば、最新の Android 開発や Compose のスキルを継続的に学習して身に付けることができます。
このコースにご参加いただき、ありがとうございました。今後も、Android デベロッパー向けドキュメント、Android デベロッパー向けの Jetpack Compose コース、最新の Android アプリ アーキテクチャ、Android デベロッパー ブログ、その他の Codelab、サンプル プロジェクトなどの資料も活用して、スキルを身に付け、その幅を広げることをおすすめします。
最後に、ソーシャル メディアで作成したコンテンツを共有し、その際にはハッシュタグ #AndroidBasics を付けて、Google と他の Android デベロッパー コミュニティがあなたの学習プロセスを追いかけることができるようにしましょう。
Compose を使ったコーディングをお楽しみください!