カスタムのチップ金額を計算する
参考URL
前回の続きからです。
最初に今回使うStringを登録します。
<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を追加します。
@Composable
fun EditNumberField(
label: Int,
value: String,
onValueChanged: (String) -> Unit,
modifier: Modifier = Modifier
)
関数本体の方もハードコードされた文字列をlabelに変更します。
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
label = { Text(stringResource(label)) },
//...
)
}
このままでは参照の型がおかしいと表示されるのでアノテーションを付け加えます。
import androidx.annotation.StringRes
@Composable
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChanged: (String) -> Unit,
modifier: Modifier = Modifier
)
そして関数本体にあるEditNumberField()のlabelを変更します。
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 = "",
onValueChanged = { },
modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth()
)
このフィールドへの入力を格納するための変数をTipTimeLayoutに追加します。
fun TipTimeLayout() {
var amountInput by remember { mutableStateOf("") }
var tipInput by remember { mutableStateOf("") }
val amount = amountInput.toDoubleOrNull() ?: 0.0
追加した変数をテキストフィールドにも適応させます
EditNumberField(
label = R.string.how_was_the_service,
value = tipInput,
onValueChanged = { tipInput = it },
modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth()
)
その後前回のamountInputに適応したdoubleへの変更処理とtipPercentをtipに適応させます。
@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に割り振ります。
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を追加します。
スイッチを右端に置きます。
@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))
}
}
関数本体も変更します。
@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に修正を加えます。
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())です。
fun TipTimeLayout() {
//...
Column(
modifier = Modifier
.statusBarsPadding()
.padding(horizontal = 40.dp)
.safeDrawingPadding().verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
)
//...
これで横向きにしても画面をスクロールして見れるようになりました。
テキスト フィールドに先頭のアイコンを追加する(省略可)
テキストフィールドにアイコンを付け加えて視覚的に理解しやすくします。
leadingIconを付け加えます。
@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,
)
}
//...