1. 始める前に
この Codelab では、Compose の状態の概要 Codelab の解答コードを使用して、インタラクティブなチップ計算ツールを作成します。このアプリでは、請求額とチップ率を入力するとチップ金額が自動的に計算され、四捨五入されます。最終的なアプリの外観は次のとおりです。
前提条件
- 「Jetpack Compose で状態を使用する」Codelabを修了している
Text
コンポーザブルとTextField
コンポーザブルをアプリに追加できるremember
関数、状態、状態ホイスティング、コンポーズ可能な関数のステートフル / ステートレスの違いに関する知識
学習内容
- 仮想キーボードにアクション ボタンを追加する方法
- キーボード アクションの設定方法
Switch
コンポーザブルの概要と使用方法- Layout Inspector の概要
作成するアプリの概要
- ユーザーが入力したサービス料金とチップ率に基づいてチップ金額を計算する Tip Time アプリ
必要なもの
- Android Studio
- 「Jetpack Compose で状態を使用する」Codelab の解答コード
2. スターター アプリの概要
この Codelab は、前の Codelab の「Tip Time アプリ」を使って始めます。このアプリは、固定のチップ率でチップ金額を計算するために必要なユーザー インターフェースを提供するものです。[Cost of Service] テキスト ボックスに、ユーザーがサービス料金を入力します。アプリはチップ金額を計算して Text
コンポーザブルに表示します。
スターター コードを取得する
まず、スターター コードをダウンロードします。
または、コードの GitHub リポジトリのクローンを作成することもできます。
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator.git $ cd basic-android-kotlin-compose-training-tip-calculator $ git checkout state
コードは Tip Calculator
GitHub リポジトリで確認できます。
Tip Time アプリを実行する
- Android Studio で Tip Time プロジェクトを開き、エミュレータまたはデバイスでアプリを実行します。
- サービス料金を入力します。アプリが自動的にチップ金額を計算して表示します。
現在の実装では、チップ率は 15% にハードコードされています。この Codelab では、テキスト フィールドを使用してこの機能を拡張し、アプリでカスタムのチップ率を計算してチップ金額を四捨五入できるようにします。
必要な文字列リソースを追加する
- [Project] タブで、[res] > [values] > [strings.xml] をクリックします。
strings.xml
ファイルの<resources>
タグの間に、次の文字列リソースを追加します。
<string name="how_was_the_service">Tip (%)</string>
<string name="round_up_tip">Round up tip?</string>
strings.xml
ファイルは、前の Codelab の文字列を含む次のコード スニペットのようになります。
strings.xml
<resources>
<string name="app_name">TipTime</string>
<string name="calculate_tip">Calculate Tip</string>
<string name="cost_of_service">Cost of Service</string>
<string name="how_was_the_service">Tip (%)</string>
<string name="round_up_tip">Round up tip?</string>
<string name="tip_amount">Tip Amount: %s</string>
</resources>
Cost Of Service
文字列をBill Amount
文字列に変更します。国によっては「サービス」が「チップ」を意味するため、このように変更して混乱を防ぎます。Cost of Service
文字列で、属性のname
cost_of_service
を右クリックし、[Refactor] > [Rename] を選択します。[Rename] ダイアログが開きます。
- [Rename] ダイアログ ボックスで、
cost_of _service
をbill_amount
に置き換えて [Refactor] をクリックします。これでプロジェクト内のcost_of_service
文字列リソースがすべて更新されるため、Compose コードを手動で変更する必要はありません。
strings.xml
ファイルで、文字列値をCost of Service
からBill Amount
に変更します。
<string name="bill_amount">Bill Amount</string>
MainActivity.kt
ファイルに移動してアプリを実行します。次の画像のように、テキスト ボックスのラベルが更新されます。
3. チップ率のテキスト フィールドを追加する
提供されたサービスの質やその他のさまざまな理由によって、チップを増減したい場合があります。これに対応するために、アプリでユーザーがカスタムのチップ金額を計算できるようにする必要があります。このセクションでは、次の画像のように、ユーザーがカスタムのチップ率を入力するテキスト フィールドを追加します。
アプリにはすでに [Bill Amount] テキスト フィールド コンポーザブルがあります。これはステートレスな、EditNumberField()
というコンポーズ可能な関数です。前の Codelab では、amountInput
の状態を EditNumberField()
コンポーザブルから TipTimeScreen()
関数にホイスティングし、EditNumberField()
コンポーザブルをステートレスにしました。
テキスト フィールドを追加するには、同じ EditNumberField()
コンポーザブルを再利用しますが、別のラベルを使用します。この変更を行うには、ラベルをコンポーズ可能な関数 EditNumberField()
の中でハードコードするのではなく、パラメータとして渡す必要があります。
コンポーズ可能な関数 EditNumberField()
を再利用可能にします。
MainActivity.kt
ファイルで、コンポーズ可能な関数EditNumberField()
のパラメータにInt
型のlabel
文字列リソースを追加します。
@Composable
fun EditNumberField(
label: Int,
value: String,
onValueChange: (String) -> Unit
)
Modifier
型のmodifier
引数をコンポーズ可能な関数EditNumberField()
に追加します。
@Composable
fun EditNumberField(
label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
)
- 関数本体で、ハードコードされた文字列リソース ID を
label
パラメータに置き換えます。
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
label = { Text(stringResource(label)) },
//...
)
}
label
パラメータが文字列リソース参照であることを示すために、関数パラメータに@StringRes
アノテーションを付けます。
@Composable
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
)
- 以下をインポートします。
import androidx.annotation.StringRes
EditNumberField()
関数のTextField
コンポーザブルで、label
パラメータをstringResource()
関数に渡します。
@Composable
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
TextField(
//...
label = { Text(stringResource(label)) },
//...
)
}
TipTimeScreen()
関数のEditNumberField()
関数呼び出しで、label
パラメータをR.string.bill_amount
文字列リソースに設定します。
EditNumberField(
label = R.string.bill_amount,
value = amountInput,
onValueChange = { amountInput = it }
)
- [Design] ペインで 「Build & Refresh」をクリックします。アプリの UI は次の画像のようになります。
TipTimeScreen()
関数で、EditNumberField()
関数呼び出しの後に、カスタムのチップ率用に別のテキスト フィールドを追加します。次のパラメータを使用して、コンポーズ可能な関数EditNumberField()
を呼び出します。
EditNumberField(
label = R.string.how_was_the_service,
value = "",
onValueChange = { }
)
これで、カスタムのチップ率を入力する別のテキスト ボックスが追加されます。
- [Design] ペインで 「Build & Refresh」をクリックします。次の画像のように、アプリのプレビューに [Tip (%)] テキスト フィールドが表示されるようになりました。
TipTimeScreen()
関数の先頭に、追加したテキスト フィールドの状態変数用にtipInput
というvar
プロパティを追加します。mutableStateOf("")
を使用して変数を初期化し、remember
関数で呼び出しを囲みます。
var tipInput by remember { mutableStateOf("") }
- 新しい
EditNumberField
()
関数呼び出しで、名前付きパラメータvalue
をtipInput
変数に設定し、ラムダ式onValueChange
のtipInput
変数を更新します。
EditNumberField(
label = R.string.how_was_the_service,
value = tipInput,
onValueChange = { tipInput = it }
)
TipTimeScreen()
関数で、tipInput
変数の定義の後、tipInput
変数をDouble
型に変換するtipPercent
というval
変数を定義し、エルビス演算子を使用して、値がnull
の場合に0.0
を返します。
val tipPercent = tipInput.toDoubleOrNull() ?: 0.0
TipTimeScreen()
関数で、calculateTip()
関数呼び出しを更新し、2 番目のパラメータとしてtipPercent
変数を渡します。
val tip = calculateTip(amount, tipPercent)
TipTimeScreen()
関数のコードは次のコード スニペットのようになりました。
@Composable
fun TipTimeScreen() {
var amountInput by remember { mutableStateOf("") }
var tipInput by remember { mutableStateOf("") }
val tipPercent = tipInput.toDoubleOrNull() ?: 0.0
val amount = amountInput.toDoubleOrNull() ?: 0.0
val tip = calculateTip(amount, tipPercent)
Column(
modifier = Modifier.padding(32.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = stringResource(R.string.calculate_tip),
fontSize = 24.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(Modifier.height(16.dp))
EditNumberField(
label = R.string.bill_amount,
value = amountInput,
onValueChange = { amountInput = it }
)
EditNumberField(
label = R.string.how_was_the_service,
value = tipInput,
onValueChange = { tipInput = it }
)
Spacer(Modifier.height(24.dp))
Text(
text = stringResource(R.string.tip_amount, tip),
modifier = Modifier.align(Alignment.CenterHorizontally),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
}
}
- エミュレータまたはデバイスでアプリを実行し、請求額とチップ率を入力します。チップ金額は正しく計算されていますか。
4. アクション ボタンを設定する
前の Codelab では、KeyboardOptions
クラスを使用してキーボードのタイプを設定する方法について説明しました。このセクションでは、同じ KeyboardOptions
を使用してキーボード アクション ボタンを設定する方法について説明します。キーボード アクション ボタンは、キーボードの末端にあるボタンです。次の表に例を示します。
プロパティ | キーボード アクション ボタン |
| |
| |
|
このタスクでは、テキスト ボックスのアクション ボタンを 2 種類設定します。
- [Bill Amount] テキスト ボックスの Next アクション ボタン。ユーザーが現在の入力を完了し、次のテキスト ボックスに移動することを示します。
- [Tip %] テキスト ボックスの Done アクション ボタン。ユーザーが所定の入力を終了したことを示します。
これらのアクション ボタンを備えたキーボードの例を次の画像に示します。
キーボード オプションを追加します。
EditNumberField()
関数のTextField()
関数呼び出しで、ImeAction.Next
値に設定したimeAction
名前付き引数をKeyboardOptions
コンストラクタに渡します。KeyboardOptions.Default.copy
関数を使用して、大文字アルファベットの使用や自動修正などの、他のデフォルト オプションを使用します。
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
)
)
}
- エミュレータまたはデバイスでアプリを実行します。次の画像のように、キーボードに Next アクション ボタンが表示されるようになりました。
しかし、テキスト フィールドにはアクション ボタンが 2 種類必要です。この問題はすぐに修正できます。
EditNumberField()
関数を調べます。TextField()
関数のkeyboardOptions
パラメータはハードコードされています。テキスト フィールド用に種々のアクション ボタンを作成するには、KeyboardOptions
オブジェクトを引数として渡す必要があります。これは次のステップで行います。
// No need to copy, just examine the code.
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChange: (String) -> Unit
) {
TextField(
//...
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
)
)
}
EditNumberField()
関数定義に、KeyboardOptions
型のkeyboardOptions
パラメータを追加します。関数本体で、これをTextField()
関数のkeyboardOptions
名前付きパラメータに代入します。
@Composable
fun EditNumberField(
@StringRes label: Int,
keyboardOptions: KeyboardOptions,
value: String,
onValueChange: (String) -> Unit
){
TextField(
//...
keyboardOptions = keyboardOptions
)
}
TipTimeScreen()
関数で、最初のEditNumberField()
関数呼び出しを更新し、[Bill Amount] テキスト フィールドのkeyboardOptions
名前付きパラメータを渡します。
EditNumberField(
label = R.string.bill_amount,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
value = amountInput,
onValueChange = { amountInput = it }
)
- 2 番目の
EditNumberField()
関数呼び出しで、[Tip %] テキスト フィールドのimeAction
をImeAction.Done
に変更します。関数は次のコード スニペットのようになります。
EditNumberField(
label = R.string.how_was_the_service,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
value = tipInput,
onValueChange = { tipInput = it }
)
- アプリを実行します。次の画像のように、Next と Done のアクション ボタンが表示されます。
- 請求額を入力して Next アクション ボタンをクリックします。次にチップ率を入力し、Done アクション ボタンをクリックします。まだボタンに機能を追加していないため、何も起こりません。これは次のセクションで行います。
5. キーボード アクションを設定する
このセクションでは、KeyboardActions
クラスを使用して、フォーカスを次のテキスト フィールドに移動してキーボードを閉じる機能を実装し、ユーザー エクスペリエンスを改善します。これによりデベロッパーは、ソフトウェア キーボードでユーザー IME(インプット メソッド エディタ)アクションに応じてトリガーするアクションを指定できます。IME アクションの例としては、ユーザーが Next または Done アクション ボタンをクリックした場合が挙げられます。
以下を実装します。
- Next アクション: フォーカスを次のテキスト フィールド([Tip %] テキスト ボックス)に移動する
- Done アクション: 仮想キーボードを閉じる
EditNumberField()
関数で、focusManager
というval
変数を追加し、LocalFocusManager.current
プロパティの値を代入します。
val focusManager = LocalFocusManager.current
Compose では、LocalFocusManager
インターフェースを使用してフォーカスを制御します。この変数を使用して、フォーカスをテキスト ボックスに移動し、テキスト ボックスからフォーカスをクリアします。
import androidx.compose.ui.platform.LocalFocusManager
をインポートします。EditNumberField()
関数のシグネチャに、KeyboardActions
型の別のkeyboardActions
パラメータを追加します。
@Composable
fun EditNumberField(
@StringRes label: Int,
keyboardOptions: KeyboardOptions,
keyboardActions: KeyboardActions,
value: String,
onValueChange: (String) -> Unit
) {
//...
}
EditNumberField()
関数本体で、TextField
()
関数呼び出しを更新し、keyboardActions
パラメータを、渡されたkeyboardActions
パラメータに設定します。
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
keyboardActions = keyboardActions
)
}
テキスト フィールドをカスタマイズしてアクション ボタンごとに異なる機能を持たせられるようになりました。
TipTimeScreen()
関数呼び出しで、keyboardActions
名前付きパラメータを新しい引数として含むように最初のEditNumberField()
関数呼び出しを更新します。値KeyboardActions( onNext =
{ }
)
を代入します。
// Bill amount text field
EditNumberField(
//...
keyboardActions = KeyboardActions(
onNext = { }
),
//...
)
onNext
名前付きパラメータのラムダ式は、ユーザーがキーボードの Next アクション ボタンを押すと実行されます。
- ラムダを定義し、
FocusManager
をリクエストして、フォーカスを次のコンポーザブルである [Tip %] まで下に移動します。ラムダ式では、focusManager
オブジェクトに対してmoveFocus()
関数を呼び出し、FocusDirection.Down
引数を渡します。
// Bill amount text field
EditNumberField(
label = R.string.bill_amount,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }
),
value = amountInput,
onValueChange = { amountInput = it }
)
moveFocus()
関数は、指定された方向にフォーカスを移動します。この場合は、[Tip %] テキスト フィールドまで下に移動します。
- 以下をインポートします。
import androidx.compose.ui.focus.FocusDirection
- 同様の実装を [Tip %] テキスト フィールドに追加します。違いは、
onNext
ではなくonDone
名前付きパラメータを定義する必要があることです。
// Tip% text field
EditNumberField(
//...
keyboardActions = KeyboardActions(
onDone = { }
),
//...
)
- ユーザーがカスタムのチップを入力したら、キーボードの Done アクションでフォーカスをクリアし、キーボードを閉じる必要があります。ラムダを定義し、
FocusManager
をリクエストしてフォーカスをクリアします。ラムダ式では、focusManager
オブジェクトに対してclearFocus()
関数を呼び出します。
EditNumberField(
label = R.string.how_was_the_service,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = { focusManager.clearFocus() }),
value = tipInput,
onValueChange = { tipInput = it }
)
clearFocus()
関数は、フォーカスのあるコンポーネントからフォーカスをクリアします。
- アプリを実行します。次の GIF のように、フォーカスのあるコンポーネントがキーボード アクションで変更されるようになりました。
6. スイッチを追加する
スイッチは、1 つのアイテムの状態をオンまたはオフに切り替えます。切り替えボタンには 2 つの状態があり、ユーザーは 2 つのオプションのいずれかを選択できます。切り替えボタンは、次の画像のように、つまみとトラックから構成されています。
1. つまみ |
スイッチは、次の画像のように、決定内容の入力や設定の宣言に使用できる選択コントロールです。
ユーザーは、つまみを前後にドラッグしてオプションを選択するか、単にスイッチをタップして切り替えます。別の切り替え例として、次の GIF ではビジュアル オプションの設定をダークモードに切り替えています。
スイッチについて詳しくは、スイッチのドキュメントをご覧ください。
次の画像のように、Switch
コンポーザブルを使用して、チップを最も近い整数に切り上げるかどうかを選択できるようにします。
Text
コンポーザブルと Switch
コンポーザブルの行を追加します。
EditNumberField()
関数の後にコンポーズ可能な関数RoundTheTipRow()
を追加し、EditNumberField()
関数と同様の引数としてデフォルトのModifier
を渡します。
@Composable
fun RoundTheTipRow(modifier: Modifier = Modifier) {
}
RoundTheTipRow()
関数を実装し、次のmodifier
を使用してRow
レイアウト コンポーザブルを追加します。子要素の幅を画面上の最大値に設定し、中央に配置して、サイズを48
dp
にします。
Row(
modifier = Modifier
.fillMaxWidth()
.size(48.dp),
verticalAlignment = Alignment.CenterVertically
) {
}
- 以下をインポートします。
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Size
Row
レイアウト コンポーザブルのラムダブロックに、R.string.round_up_tip
文字列リソースを使用してRound up tip?
文字列を表示するText
コンポーザブルを追加します。
Text(text = stringResource(R.string.round_up_tip))
Text
コンポーザブルの後にSwitch
コンポーザブルを追加します。checked
名前付きパラメータをroundUp
に設定し、onCheckedChange
名前付きパラメータをonRoundUpChanged
に設定して渡します。
Switch(
checked = roundUp,
onCheckedChange = onRoundUpChanged,
)
次の表に、RoundTheTipRow()
関数で定義したものと同じパラメータに関する情報が含まれています。
パラメータ | 説明 |
| スイッチがオンになっているかどうか。これは |
| スイッチがクリックされたときに呼び出されるコールバック。 |
- 以下をインポートします。
import androidx.compose.material.Switch
RoundTipRow()
関数に、Boolean
型のroundUp
パラメータと、Boolean
を受け取って何も返さないonRoundUpChanged
ラムダ関数を追加します。
@Composable
fun RoundTheTipRow(
roundUp: Boolean,
onRoundUpChanged: (Boolean) -> Unit,
modifier: Modifier = Modifier
)
これにより、スイッチの状態がホイスティングされます。
Switch
コンポーザブルに、このmodifier
を追加して、Switch
コンポーザブルを画面の末端に配置します。
Switch(
modifier = modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
//...
)
- 以下をインポートします。
import androidx.compose.foundation.layout.wrapContentWidth
TipTimeScreen()
関数に、Switch
コンポーザブルの状態の var 変数を追加します。roundUp
というvar
変数を作成してmutableStateOf()
に設定し、デフォルトの引数としてfalse
を指定します。呼び出しをremember { }
で囲みます。
fun TipTimeScreen() {
//...
var roundUp by remember { mutableStateOf(false) }
//...
Column(
...
) {
//...
}
}
これは Switch
コンポーザブルの状態の変数であり、false がデフォルトの状態になります。
TipTimeScreen()
関数のColumn
ブロックで、[Tip %] テキスト フィールドの後、RoundTheTipRow()
関数を呼び出します。roundUp
名前付きパラメータをroundUp
に設定し、onRoundUpChanged
名前付きパラメータを、roundUp
値を更新するラムダ コールバックに設定するよう、引数を指定します。
@Composable
fun TipTimeScreen() {
//...
Column(
...
) {
Text(
...
)
Spacer(...)
EditNumberField(
...
)
EditNumberField(
...
)
RoundTheTipRow(roundUp = roundUp, onRoundUpChanged = { roundUp = it })
Spacer(...)
Text(
...
)
}
}
[Round up tip?] 行が表示されます。
- アプリを実行します。アプリに [Round up tip?] の切り替えが表示されますが、次の画像のように、切り替えのつまみがかなり見えにくい状態です。
1. つまみ |
次のステップとして、つまみをダークグレーに変更し、見やすくします。
RoundTheTipRow()
関数のSwitch()
コンポーザブルに、colors
名前付きパラメータを追加します。colors
名前付きパラメータを、Color.DarkGray
引数に設定されたuncheckedThumbColor
名前付きパラメータを受け入れるSwitchDefaults.colors()
関数に設定します。
Switch(
//...
colors = SwitchDefaults.colors(
uncheckedThumbColor = Color.DarkGray
)
)
- 以下をインポートします。
import androidx.compose.material.SwitchDefaults
import androidx.compose.ui.graphics.Color
コンポーズ可能な関数 RoundTheTipRow()
は、次のコード スニペットのようになります。
@Composable
fun RoundTheTipRow(roundUp: Boolean, onRoundUpChanged: (Boolean) -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.size(48.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(stringResource(R.string.round_up_tip))
Switch(
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
checked = roundUp,
onCheckedChange = onRoundUpChanged,
colors = SwitchDefaults.colors(
uncheckedThumbColor = Color.DarkGray
)
)
}
}
- アプリを実行します。次の画像のように、スイッチのつまみの色が変わります。
- 請求額とチップ率を入力し、[Round up tip?] の切り替えを選択します。チップの金額は四捨五入されませんが、これはまだ
calculateTip()
関数を更新する必要があるためです(次のセクションで行います)。
チップを四捨五入するように calculateTip()
関数を更新する
Boolean
変数を受け入れてチップを最も近い整数に切り上げるように、calculateTip()
関数を変更します。
- チップを切り上げるには、
calculateTip()
関数がスイッチの状態(Boolean
)を把握する必要があります。calculateTip()
関数に、Boolean
型のroundUp
パラメータを追加します。
private fun calculateTip(
amount: Double,
tipPercent: Double = 15.0,
roundUp: Boolean
): String {
//...
}
calculateTip()
関数のreturn
ステートメントの前に、roundUp
の値をチェックするif()
条件を追加します。roundUp
がtrue
であれば、tip
変数を定義してkotlin.math.
ceil
()
関数に設定し、引数として関数tip
を渡します。
if (roundUp)
tip = kotlin.math.ceil(tip)
完成した calculateTip()
関数は次のコード スニペットのようになります。
private fun calculateTip(amount: Double, tipPercent: Double = 15.0, roundUp: Boolean): String {
var tip = tipPercent / 100 * amount
if (roundUp)
tip = kotlin.math.ceil(tip)
return NumberFormat.getCurrencyInstance().format(tip)
}
TipTimeScreen()
関数で、calculateTip()
関数呼び出しを更新し、roundUp
パラメータを渡します。
val tip = calculateTip(amount, tipPercent, roundUp)
- アプリを実行します。次の画像のように、チップ金額が切り上げられるようになりました。
7. 解答コードを取得する
この Codelab の完成したコードをダウンロードするには、次の git コマンドを使用します。
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator.git
または、リポジトリを ZIP ファイルとしてダウンロードし、Android Studio で開くこともできます。
解答コードを確認する場合は、GitHub で表示します。
8. おわりに
これで、Tip Time アプリにカスタムのチップ機能を追加しました。このアプリでは、ユーザーがカスタムのチップ率を入力し、チップの金額を切り上げられるようになりました。#AndroidBasics を付けて、ソーシャル メディアで共有しましょう。