カスタムのチップ金額を計算する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

1. 始める前に

この Codelab では、Compose の状態の概要 Codelab の解答コードを使用して、インタラクティブなチップ計算ツールを作成します。このアプリでは、請求額とチップ率を入力するとチップ金額が自動的に計算され、四捨五入されます。最終的なアプリの外観は次のとおりです。

24370de6d667a700.png

前提条件

  • 「Jetpack Compose で状態を使用する」Codelabを修了している
  • Text コンポーザブルと TextField コンポーザブルをアプリに追加できる
  • remember 関数、状態、状態ホイスティング、コンポーズ可能な関数のステートフル / ステートレスの違いに関する知識

学習内容

  • 仮想キーボードにアクション ボタンを追加する方法
  • キーボード アクションの設定方法
  • Switch コンポーザブルの概要と使用方法
  • Layout Inspector の概要

作成するアプリの概要

  • ユーザーが入力したサービス料金とチップ率に基づいてチップ金額を計算する Tip Time アプリ

必要なもの

  • Android Studio
  • 「Jetpack Compose で状態を使用する」Codelab の解答コード

2. Code-Along 動画を見る(省略可)

コースの講師が Codelab を完了する様子を視聴する場合は、以下の動画を再生してください。

このステップは省略可能です。

3.スターター アプリの概要

この 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 アプリを実行する

  1. Android Studio で Tip Time プロジェクトを開き、エミュレータまたはデバイスでアプリを実行します。
  2. サービス料金を入力します。アプリが自動的にチップ金額を計算して表示します。

761df483de663721.png

現在の実装では、チップ率は 15% にハードコードされています。この Codelab では、テキスト フィールドを使用してこの機能を拡張し、アプリでカスタムのチップ率を計算してチップ金額を四捨五入できるようにします。

必要な文字列リソースを追加する

  1. [Project] タブで、[res] > [values] > [strings.xml] をクリックします。
  2. 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>
  1. Cost Of Service 文字列を Bill Amount 文字列に変更します。国によっては「サービス」が「チップ」を意味するため、このように変更して混乱を防ぎます。
  2. Cost of Service 文字列で、属性の name cost_of_service を右クリックし、[Refactor] > [Rename] を選択します。[Rename] ダイアログが開きます。

a2f301b95a8c0e3f.png

  1. [Rename] ダイアログ ボックスで、cost_of _servicebill_amount に置き換えて [Refactor] をクリックします。これでプロジェクト内の cost_of_service 文字列リソースがすべて更新されるため、Compose コードを手動で変更する必要はありません。

f525a371c2851d08.png

  1. strings.xml ファイルで、文字列値を Cost of Service から Bill Amount に変更します。
<string name="bill_amount">Bill Amount</string>
  1. MainActivity.kt ファイルに移動してアプリを実行します。次の画像のように、テキスト ボックスのラベルが更新されます。

テキスト フィールドに Cost of Service ではなく Bill Amount と表示

4. チップ率のテキスト フィールドを追加する

提供されたサービスの質やその他のさまざまな理由によって、チップを増減したい場合があります。これに対応するために、アプリでユーザーがカスタムのチップ金額を計算できるようにする必要があります。このセクションでは、次の画像のように、ユーザーがカスタムのチップ率を入力するテキスト フィールドを追加します。

47b5e8543e5eb754.png

アプリにはすでに [Bill Amount] テキスト フィールド コンポーザブルがあります。これはステートレスな、EditNumberField() というコンポーズ可能な関数です。前の Codelab では、amountInput の状態を EditNumberField() コンポーザブルから TipTimeScreen() 関数にホイスティングし、EditNumberField() コンポーザブルをステートレスにしました。

テキスト フィールドを追加するには、同じ EditNumberField() コンポーザブルを再利用しますが、別のラベルを使用します。この変更を行うには、ラベルをコンポーズ可能な関数 EditNumberField() の中でハードコードするのではなく、パラメータとして渡す必要があります。

コンポーズ可能な関数 EditNumberField() を再利用可能にします。

  1. MainActivity.kt ファイルで、コンポーズ可能な関数 EditNumberField() のパラメータに Int 型の label 文字列リソースを追加します。
@Composable
fun EditNumberField(
   label: Int,
   value: String,
   onValueChange: (String) -> Unit
)
  1. Modifier 型の modifier 引数をコンポーズ可能な関数 EditNumberField() に追加します。
@Composable
fun EditNumberField(
   label: Int,
   value: String,
   onValueChange: (String) -> Unit,
   modifier: Modifier = Modifier
)
  1. 関数本体で、ハードコードされた文字列リソース ID を label パラメータに置き換えます。
@Composable
fun EditNumberField(
   //...
) {
   TextField(
       //...
       label = { Text(stringResource(label)) },
       //...
   )
}
  1. label パラメータが文字列リソース参照であることを示すために、関数パラメータに @StringRes アノテーションを付けます。
@Composable
fun EditNumberField(
   @StringRes label: Int,
   value: String,
   onValueChange: (String) -> Unit,
   modifier: Modifier = Modifier
)
  1. 以下をインポートします。
import androidx.annotation.StringRes
  1. EditNumberField() 関数の TextField コンポーザブルで、label パラメータを stringResource() 関数に渡します。
@Composable
fun EditNumberField(
   @StringRes label: Int,
   value: String,
   onValueChange: (String) -> Unit,
   modifier: Modifier = Modifier
) {
   TextField(
       //...
       label = { Text(stringResource(label)) },
       //...
   )
}
  1. TipTimeScreen() 関数の EditNumberField() 関数呼び出しで、label パラメータを R.string.bill_amount 文字列リソースに設定します。
EditNumberField(
   label = R.string.bill_amount,
   value = amountInput,
   onValueChange = { amountInput = it }
)
  1. [Design] ペインで 2d40b921003ab5eb.pngBuild & Refresh」をクリックします。アプリの UI は次の画像のようになります。

a84cd50c50235a9f.png

  1. TipTimeScreen() 関数で、EditNumberField() 関数呼び出しの後に、カスタムのチップ率用に別のテキスト フィールドを追加します。次のパラメータを使用して、コンポーズ可能な関数 EditNumberField() を呼び出します。
EditNumberField(
   label = R.string.how_was_the_service,
   value = "",
   onValueChange = { }
)

これで、カスタムのチップ率を入力する別のテキスト ボックスが追加されます。

  1. [Design] ペインで 2d40b921003ab5eb.pngBuild & Refresh」をクリックします。次の画像のように、アプリのプレビューに [Tip (%)] テキスト フィールドが表示されるようになりました。

9d2c01d577d077ae.png

  1. TipTimeScreen() 関数の先頭に、追加したテキスト フィールドの状態変数用に tipInput という var プロパティを追加します。mutableStateOf("") を使用して変数を初期化し、remember 関数で呼び出しを囲みます。
var tipInput by remember { mutableStateOf("") }
  1. 新しい EditNumberField() 関数呼び出しで、名前付きパラメータ valuetipInput 変数に設定し、ラムダ式 onValueChangetipInput 変数を更新します。
EditNumberField(
   label = R.string.how_was_the_service,
   value = tipInput,
   onValueChange = { tipInput = it }
)
  1. TipTimeScreen() 関数で、tipInput 変数の定義の後、tipInput 変数を Double 型に変換する tipPercent という val 変数を定義し、エルビス演算子を使用して、値が null の場合に 0.0 を返します。
val tipPercent = tipInput.toDoubleOrNull() ?: 0.0
  1. 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
       )
   }
}
  1. エミュレータまたはデバイスでアプリを実行し、請求額とチップ率を入力します。チップ金額は正しく計算されていますか。

bdc482b015472300.png

5. アクション ボタンを設定する

前の Codelab では、KeyboardOptions クラスを使用してキーボードのタイプを設定する方法について説明しました。このセクションでは、同じ KeyboardOptions を使用してキーボード アクション ボタンを設定する方法について説明します。キーボード アクション ボタンは、キーボードの末端にあるボタンです。次の表に例を示します。

プロパティ

キーボード アクション ボタン

ImeAction.Search: ユーザーが検索を行うときに使用します。

ImeAction.Send: ユーザーが入力フィールドでテキストを送信するときに使用します。

ImeAction.Go: ユーザーが入力テキストのターゲットに移動するときに使用します。

このタスクでは、テキスト ボックスのアクション ボタンを 2 種類設定します。

  • [Bill Amount] テキスト ボックスの Next アクション ボタン。ユーザーが現在の入力を完了し、次のテキスト ボックスに移動することを示します。
  • [Tip %] テキスト ボックスの Done アクション ボタン。ユーザーが所定の入力を終了したことを示します。

これらのアクション ボタンを備えたキーボードの例を次の画像に示します。

キーボード オプションを追加します。

  1. EditNumberField() 関数の TextField() 関数呼び出しで、ImeAction.Next 値に設定した imeAction 名前付き引数を KeyboardOptions コンストラクタに渡します。KeyboardOptions.Default.copy 関数を使用して、大文字アルファベットの使用や自動修正などの、他のデフォルト オプションを使用します。
@Composable
fun EditNumberField(
   //...
) {
   TextField(
       //...
       keyboardOptions = KeyboardOptions.Default.copy(
           keyboardType = KeyboardType.Number,
           imeAction = ImeAction.Next
       )
   )
}
  1. エミュレータまたはデバイスでアプリを実行します。次の画像のように、キーボードに Next アクション ボタンが表示されるようになりました。

しかし、テキスト フィールドにはアクション ボタンが 2 種類必要です。この問題はすぐに修正できます。

  1. 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
       )
   )
}
  1. EditNumberField() 関数定義に、KeyboardOptions 型の keyboardOptions パラメータを追加します。関数本体で、これを TextField() 関数の keyboardOptions 名前付きパラメータに代入します。
@Composable
fun EditNumberField(
   @StringRes label: Int,
   keyboardOptions: KeyboardOptions,
   value: String,
   onValueChange: (String) -> Unit
){
   TextField(
       //...
       keyboardOptions = keyboardOptions
   )
}
  1. TipTimeScreen() 関数で、最初の EditNumberField() 関数呼び出しを更新し、[Bill Amount] テキスト フィールドの keyboardOptions 名前付きパラメータを渡します。
EditNumberField(
   label = R.string.bill_amount,
   keyboardOptions = KeyboardOptions(
       keyboardType = KeyboardType.Number,
       imeAction = ImeAction.Next
   ),
   value = amountInput,
   onValueChange = { amountInput = it }
)
  1. 2 番目の EditNumberField() 関数呼び出しで、[Tip %] テキスト フィールドの imeActionImeAction.Done に変更します。関数は次のコード スニペットのようになります。
EditNumberField(
   label = R.string.how_was_the_service,
   keyboardOptions = KeyboardOptions(
       keyboardType = KeyboardType.Number,
       imeAction = ImeAction.Done
   ),
   value = tipInput,
   onValueChange = { tipInput = it }
)
  1. アプリを実行します。次の画像のように、NextDone のアクション ボタンが表示されます。

  1. 請求額を入力して Next アクション ボタンをクリックします。次にチップ率を入力し、Done アクション ボタンをクリックします。まだボタンに機能を追加していないため、何も起こりません。これは次のセクションで行います。

6. キーボード アクションを設定する

このセクションでは、KeyboardActions クラスを使用して、フォーカスを次のテキスト フィールドに移動してキーボードを閉じる機能を実装し、ユーザー エクスペリエンスを改善します。これによりデベロッパーは、ソフトウェア キーボードでユーザー IME(インプット メソッド エディタ)アクションに応じてトリガーするアクションを指定できます。IME アクションの例としては、ユーザーが Next または Done アクション ボタンをクリックした場合が挙げられます。

以下を実装します。

  • Next アクション: フォーカスを次のテキスト フィールド([Tip %] テキスト ボックス)に移動する
  • Done アクション: 仮想キーボードを閉じる
  1. TipTimeScreen() 関数で、focusManager という val 変数を追加し、LocalFocusManager.current プロパティの値を代入します。
val focusManager = LocalFocusManager.current

Compose では、LocalFocusManager インターフェースを使用してフォーカスを制御します。この変数を使用して、フォーカスをテキスト ボックスに移動し、テキスト ボックスからフォーカスをクリアします。

  1. import androidx.compose.ui.platform.LocalFocusManager をインポートします。
  2. EditNumberField() 関数のシグネチャに、KeyboardActions 型の別の keyboardActions パラメータを追加します。
@Composable
fun EditNumberField(
   @StringRes label: Int,
   keyboardOptions: KeyboardOptions,
   keyboardActions: KeyboardActions,
   value: String,
   onValueChange: (String) -> Unit
) {
   //...
}
  1. EditNumberField() 関数本体で、TextField() 関数呼び出しを更新し、keyboardActions パラメータを、渡された keyboardActions パラメータに設定します。
@Composable
fun EditNumberField(
   //...
) {
   TextField(
       //...
       keyboardActions = keyboardActions
   )
}

テキスト フィールドをカスタマイズしてアクション ボタンごとに異なる機能を持たせられるようになりました。

  1. TipTimeScreen() 関数呼び出しで、keyboardActions 名前付きパラメータを新しい引数として含むように最初の EditNumberField() 関数呼び出しを更新します。値 KeyboardActions( onNext = { } ) を代入します。
// Bill amount text field
EditNumberField(
   //...
   keyboardActions = KeyboardActions(
       onNext = { }
   ),
   //...
)

onNext 名前付きパラメータのラムダ式は、ユーザーがキーボードの Next アクション ボタンを押すと実行されます。

  1. ラムダを定義し、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 %] テキスト フィールドまで下に移動します。

  1. 以下をインポートします。
import androidx.compose.ui.focus.FocusDirection
  1. 同様の実装を [Tip %] テキスト フィールドに追加します。違いは、onNext ではなく onDone 名前付きパラメータを定義する必要があることです。
// Tip% text field
EditNumberField(
   //...
   keyboardActions = KeyboardActions(
       onDone = { }
   ),
   //...
)
  1. ユーザーがカスタムのチップを入力したら、キーボードの 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() 関数は、フォーカスのあるコンポーネントからフォーカスをクリアします。

  1. アプリを実行します。次の GIF のように、フォーカスのあるコンポーネントがキーボード アクションで変更されるようになりました。

3164e7a2f39a2d7b.gif

7. スイッチを追加する

スイッチは、1 つのアイテムの状態をオンまたはオフに切り替えます。切り替えボタンには 2 つの状態があり、ユーザーは 2 つのオプションのいずれかを選択できます。切り替えボタンは、次の画像のように、つまみとトラックから構成されています。

1. つまみ
2. トラック

スイッチは、次の画像のように、決定内容の入力や設定の宣言に使用できる選択コントロールです。

a90c4e22e48b30e0.png

ユーザーは、つまみを前後にドラッグしてオプションを選択するか、単にスイッチをタップして切り替えます。別の切り替え例として、次の GIF ではビジュアル オプションの設定をダークモードに切り替えています。

91b7bd7a6e02e5ff.gif

スイッチについて詳しくは、スイッチのドキュメントをご覧ください。

次の画像のように、Switch コンポーザブルを使用して、チップを最も近い整数に切り上げるかどうかを選択できるようにします。

cf89a61484296bab.png

Text コンポーザブルと Switch コンポーザブルの行を追加します。

  1. EditNumberField() 関数の後にコンポーズ可能な関数 RoundTheTipRow() を追加し、EditNumberField() 関数と同様の引数としてデフォルトの Modifier を渡します。
@Composable
fun RoundTheTipRow(modifier: Modifier = Modifier) {
}
  1. RoundTheTipRow() 関数を実装し、次の modifier を使用して Row レイアウト コンポーザブルを追加します。子要素の幅を画面上の最大値に設定し、中央に配置して、サイズを 48 dp にします。
Row(
   modifier = Modifier
       .fillMaxWidth()
       .size(48.dp),
   verticalAlignment = Alignment.CenterVertically
) {
}
  1. 以下をインポートします。
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Size
  1. Row レイアウト コンポーザブルのラムダブロックに、R.string.round_up_tip 文字列リソースを使用して Round up tip? 文字列を表示する Text コンポーザブルを追加します。
Text(text = stringResource(R.string.round_up_tip))
  1. Text コンポーザブルの後に Switch コンポーザブルを追加します。checked 名前付きパラメータを roundUp に設定し、onCheckedChange 名前付きパラメータを onRoundUpChanged に設定して渡します。
Switch(
    checked = roundUp,
    onCheckedChange = onRoundUpChanged,
)

次の表に、RoundTheTipRow() 関数で定義したものと同じパラメータに関する情報が含まれています。

パラメータ

説明

checked

スイッチがオンになっているかどうか。これは Switch コンポーザブルの状態です。

onCheckedChange

スイッチがクリックされたときに呼び出されるコールバック。

  1. 以下をインポートします。
import androidx.compose.material.Switch
  1. RoundTipRow() 関数に、Boolean 型の roundUp パラメータと、Boolean を受け取って何も返さない onRoundUpChanged ラムダ関数を追加します。
@Composable
fun RoundTheTipRow(
   roundUp: Boolean,
   onRoundUpChanged: (Boolean) -> Unit,
   modifier: Modifier = Modifier
)

これにより、スイッチの状態がホイスティングされます。

  1. Switch コンポーザブルに、この modifier を追加して、Switch コンポーザブルを画面の末端に配置します。
       Switch(
           modifier = modifier
               .fillMaxWidth()
               .wrapContentWidth(Alignment.End),
           //...
       )
  1. 以下をインポートします。
import androidx.compose.foundation.layout.wrapContentWidth
  1. TipTimeScreen() 関数に、Switch コンポーザブルの状態の var 変数を追加します。roundUp という var 変数を作成して mutableStateOf() に設定し、デフォルトの引数として false を指定します。呼び出しを remember { } で囲みます。
fun TipTimeScreen() {
   //...
   var roundUp by remember { mutableStateOf(false) }

   //...
   Column(
       ...
   ) {
     //...
  }
}

これは Switch コンポーザブルの状態の変数であり、false がデフォルトの状態になります。

  1. 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?] 行が表示されます。

  1. アプリを実行します。アプリに [Round up tip?] の切り替えが表示されますが、次の画像のように、切り替えのつまみがかなり見えにくい状態です。

選択解除されたスイッチと選択されたスイッチ。数字を付して 2 つの要素と状態を示しています。1. つまみ
2. トラック

次のステップとして、つまみをダークグレーに変更し、見やすくします。

  1. RoundTheTipRow() 関数の Switch() コンポーザブルに、colors 名前付きパラメータを追加します。
  2. colors 名前付きパラメータを、Color.DarkGray 引数に設定された uncheckedThumbColor 名前付きパラメータを受け入れる SwitchDefaults.colors() 関数に設定します。
Switch(
   //...
   colors = SwitchDefaults.colors(
       uncheckedThumbColor = Color.DarkGray
   )
)
  1. 以下をインポートします。
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
           )
       )
   }
}
  1. アプリを実行します。次の画像のように、スイッチのつまみの色が変わります。

24370de6d667a700.png

  1. 請求額とチップ率を入力し、[Round up tip?] の切り替えを選択します。チップの金額は四捨五入されませんが、これはまだ calculateTip() 関数を更新する必要があるためです(次のセクションで行います)。

チップを四捨五入するように calculateTip() 関数を更新する

Boolean 変数を受け入れてチップを最も近い整数に切り上げるように、calculateTip() 関数を変更します。

  1. チップを切り上げるには、calculateTip() 関数がスイッチの状態(Boolean)を把握する必要があります。calculateTip() 関数に、Boolean 型の roundUp パラメータを追加します。
private fun calculateTip(
   amount: Double,
   tipPercent: Double = 15.0,
   roundUp: Boolean
): String {
   //...
}
  1. calculateTip() 関数の return ステートメントの前に、roundUp の値をチェックする if() 条件を追加します。roundUptrue であれば、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)
}
  1. TipTimeScreen() 関数で、calculateTip() 関数呼び出しを更新し、roundUp パラメータを渡します。
val tip = calculateTip(amount, tipPercent, roundUp)
  1. アプリを実行します。次の画像のように、チップ金額が切り上げられるようになりました。

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

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

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator.git

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

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

9. まとめ

これで、Tip Time アプリにカスタムのチップ機能を追加しました。このアプリでは、ユーザーがカスタムのチップ率を入力し、チップの金額を切り上げられるようになりました。#AndroidBasics を付けて、ソーシャル メディアで共有しましょう。

さらに詳しく学習するには