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

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

参考[URL](https://developer.android.com/courses/pathways/android-basics-compose-unit-2-pathway-3?hl=ja#codelab-https://developer.android.com/codelabs/basic-android-kotlin-compose-calculate-tip)

前回の続きからです。

最初に今回使うStringを登録します。

```kotlin:string.xml
<resources>
    <string name="app_name">Tip Time</string>
    <string name="calculate_tip">Calculate Tip</string>
    <string name="bill_amount">Bill Amount</string>
    <string name="tip_amount">Tip Amount: %s</string>
    <string name="how_was_the_service">Tip Percentage</string>
    <string name="round_up_tip">Round up tip?</string>
</resources>

```


チップ率を変更するためのテキストフィールドを作ります。
そのために前回使用したテキストフィールドコンポーザブルを使いますが、区別をつけるためにlabelを追加します。

```kotlin:MainActivity.kt
@Composable
fun EditNumberField(
    label: Int,
    value: String,
    onValueChanged: (String) -> Unit,
    modifier: Modifier = Modifier
)

```

関数本体の方もハードコードされた文字列をlabelに変更します。


```kotlin:MainActivity.kt
@Composable
fun EditNumberField(
    //...
) {
     TextField(
         //...
         label = { Text(stringResource(label)) },
         //...
     )
}

```

このままでは参照の型がおかしいと表示されるのでアノテーションを付け加えます。

```kotlin:MainActivity.kt
import androidx.annotation.StringRes

@Composable
fun EditNumberField(
    @StringRes label: Int,
    value: String,
    onValueChanged: (String) -> Unit,
    modifier: Modifier = Modifier
)

```

そして関数本体にあるEditNumberField()のlabelを変更します。

```kotlin:MainActivity.kt
EditNumberField(
    label = R.string.bill_amount,
    value = amountInput,
    onValueChanged = { amountInput = it },
    modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth()
)
```

今回の目的のチップのパーセンテージをカスタムするテキストフィールドを作ります



```kotlin:MainActivity.kt
EditNumberField(
    label = R.string.how_was_the_service,
    value = "",
    onValueChanged = { },
    modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth()
)
```

このフィールドへの入力を格納するための変数をTipTimeLayoutに追加します。

```kotlin:MainActivity.kt
fun TipTimeLayout() {
    var amountInput by remember { mutableStateOf("") }
    var tipInput by remember { mutableStateOf("") }
    val amount = amountInput.toDoubleOrNull() ?: 0.0
```

追加した変数をテキストフィールドにも適応させます
```kotlin:MainActivity.kt
EditNumberField(
    label = R.string.how_was_the_service,
    value = tipInput,
    onValueChanged = { tipInput = it },
    modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth()
)

```


その後前回のamountInputに適応したdoubleへの変更処理とtipPercentをtipに適応させます。

```kotlin:MainActivity.kt
@Composable
fun TipTimeLayout() {
    var amountInput by remember { mutableStateOf("") }
    var tipInput by remember { mutableStateOf("") }
    val amount = amountInput.toDoubleOrNull() ?: 0.0
    val tipPercent = tipInput.toDoubleOrNull() ?: 0.0

    val tip = calculateTip(amount, tipPercent)
    Column(
        modifier = Modifier.padding(40.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = stringResource(R.string.calculate_tip),
            modifier = Modifier
                .padding(bottom = 16.dp)
                .align(alignment = Alignment.Start)
        )
        EditNumberField(
            label = R.string.bill_amount,
            value = amountInput,
            onValueChanged = { amountInput = it },
            modifier = Modifier
                .padding(bottom = 32.dp)
                .fillMaxWidth()
        )
        EditNumberField(
            label = R.string.how_was_the_service,
            value = tipInput,
            onValueChanged = { tipInput = it },
            modifier = Modifier
                .padding(bottom = 32.dp)
                .fillMaxWidth()
        )
        Text(
            text = stringResource(R.string.tip_amount, tip),
            style = MaterialTheme.typography.displaySmall
        )
        Spacer(modifier = Modifier.height(150.dp))
    }
}

```



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

キーボードオプションKeyboardOptionsを使って、キーボードをカスタムします。

今回は決定のボタンをNextとDoneに割り振ります。

```kotlin:MainActivity.kt

EditNumberField(value = amountInput,
            label = R.string.bill_amount,
            onValueChange = { amountInput = it },
            modifier = Modifier
                .padding(bottom = 36.dp)
                .fillMaxWidth(),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Number,
                imeAction = ImeAction.Next)
        )
        EditNumberField(value = tipInput,
            label = R.string.how_was_the_service,
            onValueChange = { tipInput = it },
            modifier = Modifier
                .padding(bottom = 36.dp)
                .fillMaxWidth(),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Number,
                imeAction = ImeAction.Done)
        )

//...

@Composable
fun EditNumberField(value: String,
                    @StringRes label:Int,
                    onValueChange: (String) -> Unit,
                    modifier: Modifier = Modifier,
                    keyboardOptions: KeyboardOptions = KeyboardOptions()) {

    TextField(
        value = value,
        onValueChange = onValueChange,
        modifier = modifier,
        singleLine = true,
        label = { Text(text = stringResource(label))},
        keyboardOptions = keyboardOptions,

    )
}

```


##  スイッチを追加する

チップを整数に切り上げるスイッチを付け加えます。

左に説明文、右にスイッチがあるコンポーザブルを作ります。

説明文はString.xmlから取得します。

新しいパラメータとしてスイッチの状態を示すroundUpとコールバック関数のonRoundUpChangedを追加します。
スイッチを右端に置きます。

```kotlin:MainActivity.kt
@Composable
fun RoundTheTipRow(roundUp:Boolean,
                   onRoundUpChanged:(Boolean) ->(Unit),
                   modifier: Modifier= Modifier){
    Row(modifier = modifier
        .fillMaxWidth()
        .size(48.dp),
        verticalAlignment = Alignment.CenterVertically) {
        Text(text = stringResource(id = R.string.round_up_tip))
        Switch(checked = roundUp, onCheckedChange = onRoundUpChanged,
            modifier = modifier.fillMaxWidth().wrapContentWidth(Alignment.End))
    }
}
```

関数本体も変更します。


```kotlin:MainActivity.kt
@Composable
fun TipTimeLayout() {
    //...
    var roundUp by remember { mutableStateOf(false) }
    //...

    Column(
        ...
    ) {
        Text(
            ...
        )
        Spacer(...)
        EditNumberField(
            ...
        )
        EditNumberField(
            ...
        )
        RoundTheTipRow(
             roundUp = roundUp,
             onRoundUpChanged = { roundUp = it },
             modifier = Modifier.padding(bottom = 32.dp)
         )
        Text(
            ...
        )
    }
}
```


今度はcalculateTipに修正を加えます。

```kotlin:MainActivity.kt
fun TipTimeLayout() {
//...
    val tipPercent = tipInput.toDoubleOrNull() ?: 0.0
    val tip = calculateTip(amount,tipPercent,roundUp)
    //...
}

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)
}
```

これでスイッチの完成です。


## 横向きのサポートを追加する

横向きだとしたの画面が表示されない可能性があるのでスクロール機能を付けます。

付け加えるのは修飾子のverticalScroll(rememberScrollState())です。

```kotlin:MainActivity.kt
fun TipTimeLayout() {
//...
    Column(
        modifier = Modifier
            .statusBarsPadding()
            .padding(horizontal = 40.dp)
            .safeDrawingPadding().verticalScroll(rememberScrollState()),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    )
//...
```

これで横向きにしても画面をスクロールして見れるようになりました。



## テキスト フィールドに先頭のアイコンを追加する（省略可）

テキストフィールドにアイコンを付け加えて視覚的に理解しやすくします。
leadingIconを付け加えます。

```kotlin:MainActivity.kt
@Composable
fun EditNumberField(value: String,
                    @StringRes label:Int,
                    @DrawableRes leadingIcon:Int,
                    onValueChange: (String) -> Unit,
                    modifier: Modifier = Modifier,
                    keyboardOptions: KeyboardOptions = KeyboardOptions()) {

    TextField(
        value = value,
        leadingIcon = { Icon(painter = painterResource(id = leadingIcon),
            contentDescription = null)},
        onValueChange = onValueChange,
        modifier = modifier,
        singleLine = true,
        label = { Text(text = stringResource(label))},
        keyboardOptions = keyboardOptions,

    )
}
//...
```