1. Antes de comenzar
En este codelab, usarás el código de la solución del codelab Introducción al estado en Compose para compilar una calculadora interactiva de propinas que puede calcular y redondear automáticamente un importe de propina cuando ingresas el importe de la factura y el porcentaje de propina. Puedes ver la app final en esta imagen:
Requisitos previos
- Haber completado el codelab Estado de uso en Jetpack Compose
- Poder agregar elementos de componibilidad
Text
yTextField
a una app - Conocer la función
remember
, el estado, la elevación de estado y la diferencia entre las funciones de componibilidad con y sin estado
Qué aprenderás
- Cómo agregar un botón de acción a un teclado virtual
- Cómo configurar las acciones del teclado
- Qué es un elemento componible
Switch
y cómo usarlo - Qué es el Inspector de diseño
Qué compilarás
- Una app de Tip Time que calcula los importes de las propinas según el costo del servicio y el porcentaje de propina ingresados por el usuario
Requisitos
- Android Studio
- El código de solución del codelab Estado de uso en Jetpack Compose
2. Descripción general de la app de partida
Este codelab comienza con la app de Tip Time del codelab anterior, que proporciona la interfaz de usuario necesaria para calcular una propina con un porcentaje fijo de propinas. El cuadro de texto Cost of Service (costo de servicio) le permite al usuario ingresar el costo del servicio. La app calcula y muestra el importe de la propina en un elemento Text
componible.
Obtén el código de inicio
Para comenzar, descarga el código de partida:
Como alternativa, puedes clonar el repositorio de GitHub para el código:
$ 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
Puedes explorar el código en el repositorio de GitHub Tip Calculator
.
Ejecuta la app de Tip Time
- Abre el proyecto Tip Time en Android Studio y ejecuta la app en un emulador o dispositivo.
- Ingresa un costo de servicio. La app calculará y mostrará el importe de la propina automáticamente.
En la implementación actual, el porcentaje de la propina se codifica al 15%. En este codelab, extenderás esta función con un campo de texto que le permite a la app calcular un porcentaje de propina personalizado y redondear el importe.
Agrega los recursos de strings necesarios
- En la pestaña Project, haz clic en res > values > strings.xml.
- Entre las etiquetas
<resources>
del archivostrings.xml
, agrega estos recursos de strings:
<string name="how_was_the_service">Tip (%)</string>
<string name="round_up_tip">Round up tip?</string>
El archivo strings.xml
debería verse como este fragmento de código, que incluye las cadenas del codelab anterior:
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>
- Cambia la cadena
Cost Of Service
por unaBill Amount
. En algunos países, servicio significa propina, por lo que este cambio evita confusiones. - En la cadena
Cost of Service
, haz clic con el botón derecho en elname
cost_of_service
del atributo y selecciona Refactor > Rename. Se abrirá el diálogo Rename.
- En el cuadro de diálogo Rename, reemplaza
cost_of _service
porbill_amount
y haz clic en Refactor. Esto actualiza todos los casos del recurso de stringscost_of_service
en el proyecto, por lo que no necesitas cambiar el código de Compose de forma manual.
- En el archivo
strings.xml
, cambia el valor de string aBill Amount
desdeCost of Service
:
<string name="bill_amount">Bill Amount</string>
- Navega al archivo
MainActivity.kt
y ejecuta la app. La etiqueta se actualizará en el cuadro de texto como se muestra en esta imagen:
3. Agrega un campo de texto de porcentaje de propina
Un cliente podría querer una propina mayor o menor según la calidad del servicio proporcionado y otros motivos. Para adecuarse a esto, la app debe permitir que el usuario calcule una propina personalizada. En esta sección, agregarás un campo de texto para que el usuario ingrese un porcentaje de propina personalizado, como se muestra en esta imagen:
Ya tienes un componible de campo de texto para el Importe de facturación en tu app, que es la función de componibilidad sin estado EditNumberField()
. En el codelab anterior, elevaste el estado amountInput
del elemento de componibilidad EditNumberField()
a la función TipTimeScreen()
, lo que hizo que el elemento EditNumberField()
no tuviera estado.
Para agregar un campo de texto, puedes volver a usar el mismo elemento EditNumberField()
componible, pero con una etiqueta diferente. Para realizar este cambio, debes pasar la etiqueta como parámetro, en lugar de codificarla dentro de la función de componibilidad EditNumberField()
.
Haz que la función de componibilidad EditNumberField()
sea reutilizable:
- En el archivo
MainActivity.kt
, en los parámetros de la función de componibilidadEditNumberField()
, agrega un recurso de stringslabel
de tipoInt
:
@Composable
fun EditNumberField(
label: Int,
value: String,
onValueChange: (String) -> Unit
)
- Agrega un argumento
modifier
de tipoModifier
a la función de componibilidadEditNumberField()
:
@Composable
fun EditNumberField(
label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
)
- En el cuerpo de la función, reemplaza el ID de recurso de cadenas codificado con el parámetro
label
:
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
label = { Text(stringResource(label)) },
//...
)
}
- Para indicar que se espera que el parámetro
label
sea una referencia de recursos de strings, anota el parámetro de la función con la anotación@StringRes
:
@Composable
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
)
- Importa lo siguiente:
import androidx.annotation.StringRes
- En el elemento
TextField
de la función de componibilidadEditNumberField()
, pasa el parámetrolabel
a la funciónstringResource()
.
@Composable
fun EditNumberField(
@StringRes label: Int,
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
TextField(
//...
label = { Text(stringResource(label)) },
//...
)
}
- En la llamada a la función
EditNumberField()
de la funciónTipTimeScreen()
, establece el parámetrolabel
en el recurso de stringsR.string.bill_amount
:
EditNumberField(
label = R.string.bill_amount,
value = amountInput,
onValueChange = { amountInput = it }
)
- En el panel Design, haz clic en Build & Refresh. La IU de la app debería verse de la siguiente manera:
- En la función
TipTimeScreen()
, después de la llamada a funciónEditNumberField()
, agrega otro campo de texto para el porcentaje de propina personalizada. Realiza una llamada a la función de componibilidadEditNumberField()
con estos parámetros:
EditNumberField(
label = R.string.how_was_the_service,
value = "",
onValueChange = { }
)
Esto agrega otro cuadro de texto para el porcentaje de propina personalizada.
- En el panel Design, haz clic en Build & Refresh. La vista previa de la app ahora muestra un campo de texto Tip (%) (propina) como se puede ver en esta imagen:
- En la parte superior de la función
TipTimeScreen()
, agrega una propiedadvar
llamadatipInput
para la variable de estado del campo de texto agregado. UsamutableStateOf("")
para inicializar la variable y rodea la llamada con la funciónremember
:
var tipInput by remember { mutableStateOf("") }
- En la nueva llamada a función
EditNumberField
()
, configura el parámetro con nombrevalue
para la variabletipInput
y, luego, actualiza la variabletipInput
en la expresión lambdaonValueChange
:
EditNumberField(
label = R.string.how_was_the_service,
value = tipInput,
onValueChange = { tipInput = it }
)
- En la función
TipTimeScreen()
, después de la definición de la variabletipInput
, define una variableval
llamadatipPercent
que convierta la variabletipInput
en un tipoDouble
, usa un operador Elvis y muestra0.0
si el valor esnull
:
val tipPercent = tipInput.toDoubleOrNull() ?: 0.0
- En la función
TipTimeScreen()
, actualiza la llamada a la funcióncalculateTip()
y pasa la variabletipPercent
como segundo parámetro:
val tip = calculateTip(amount, tipPercent)
El código para la función TipTimeScreen()
debería verse de la siguiente manera:
@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
)
}
}
- Ejecuta la app en un emulador o dispositivo y, luego, ingresa un importe para la factura y el porcentaje de propina. ¿La app calcula el importe correcto para la propina?
4. Establece un botón de acción
En el codelab anterior, exploraste cómo usar la clase KeyboardOptions
para establecer el tipo de teclado. En esta sección, aprenderás a configurar el botón de acción del teclado con el mismo KeyboardOptions
. El botón de acción de teclado es el que se encuentra al final del teclado. Puedes ver algunos ejemplos en esta tabla:
Propiedad | Botón de acción del teclado |
| |
| |
|
En esta tarea, establecerás dos botones de acción diferentes para los cuadros de texto:
- Un botón de acción Next (siguiente) para el cuadro de texto Bill Amount (importe de la factura), que indica que el usuario ya completó la entrada actual y quiere pasar al siguiente cuadro de texto.
- Un botón de acción Done (listo) para el cuadro de texto Tip % (porcentaje de propina), que indica que el usuario terminó de proporcionar la entrada.
Puedes ver ejemplos de teclados con estos botones de acción en las siguientes imágenes:
Agrega opciones del teclado:
- En la llamada a la función
TextField()
de la funciónEditNumberField()
, pasa el constructorKeyboardOptions
, un argumento con nombreimeAction
establecido en un valorImeAction.Next
. Usa la funciónKeyboardOptions.Default.copy
para usar las otras opciones predeterminadas, como el uso de mayúsculas y la corrección automática.
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
)
)
}
- Ejecuta la app en un emulador o dispositivo. El teclado ahora muestra el botón de acción Next, como se puede ver en esta imagen:
Sin embargo, te conviene usar dos botones de acción diferentes para los campos de texto. En breve, solucionarás este problema.
- Examina la función
EditNumberField()
. El parámetrokeyboardOptions
de la funciónTextField()
está codificado. Si deseas crear botones de acción diferentes para los campos de texto, debes pasar el objetoKeyboardOptions
como argumento, y lo harás en el paso siguiente.
// 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
)
)
}
- En la definición de la función
EditNumberField()
, agrega un parámetrokeyboardOptions
de tipoKeyboardOptions
. En el cuerpo de la función, asigna el parámetro con nombrekeyboardOptions
de la funciónTextField()
:
@Composable
fun EditNumberField(
@StringRes label: Int,
keyboardOptions: KeyboardOptions,
value: String,
onValueChange: (String) -> Unit
){
TextField(
//...
keyboardOptions = keyboardOptions
)
}
- En la función
TipTimeScreen()
, actualiza la primera llamada a la funciónEditNumberField()
y pasa el parámetro con nombrekeyboardOptions
para el campo de texto Bill amount.
EditNumberField(
label = R.string.bill_amount,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
value = amountInput,
onValueChange = { amountInput = it }
)
- En la segunda llamada a la función
EditNumberField()
, cambia elimeAction
del campo de texto Tip % aImeAction.Done
. Tu función debería verse como este fragmento de código:
EditNumberField(
label = R.string.how_was_the_service,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
value = tipInput,
onValueChange = { tipInput = it }
)
- Ejecuta la app. Se mostrarán los botones de acción Next y Done, como se puede ver en estas imágenes:
- Ingresa un importe de factura y haz clic en el botón de acción Next. Luego, ingresa un porcentaje de propina y haz clic en el botón de acción Done. No ocurre nada porque aún no agregaste ninguna funcionalidad a los botones. Puedes hacerlo en la próxima sección.
5. Establece acciones del teclado
En esta sección, implementarás la funcionalidad para mover el enfoque al siguiente campo de texto y cerrar el teclado a fin de mejorar la experiencia del usuario con la clase KeyboardActions
, que permite a los desarrolladores especificar acciones que se activan en respuesta a la acción de IME (editor de método de entrada) en el teclado en pantalla. Un ejemplo de una acción de IME es cuando el usuario hace clic en el botón de acción Next o Done.
Implementarás lo siguiente:
- En la acción Next, mueve el enfoque al siguiente campo de texto (el cuadro de texto Tip %).
- En la acción Done, cierra el teclado virtual.
- En la función
EditNumberField()
, agrega una variableval
llamadafocusManager
y asígnale un valor de la propiedadLocalFocusManager.current
:
val focusManager = LocalFocusManager.current
La interfaz LocalFocusManager
se usa para controlar el enfoque en Compose. Usarás esta variable para mover el enfoque a los cuadros de texto y fuera de ellos.
- Importa el elemento
import androidx.compose.ui.platform.LocalFocusManager
. - En la firma de la función
EditNumberField()
, agrega otro parámetrokeyboardActions
de tipoKeyboardActions
:
@Composable
fun EditNumberField(
@StringRes label: Int,
keyboardOptions: KeyboardOptions,
keyboardActions: KeyboardActions,
value: String,
onValueChange: (String) -> Unit
) {
//...
}
- En el cuerpo de la función
EditNumberField()
, actualiza la llamada a la funciónTextField
()
y establece el parámetrokeyboardActions
en el parámetro que se pasókeyboardActions
.
@Composable
fun EditNumberField(
//...
) {
TextField(
//...
keyboardActions = keyboardActions
)
}
Ahora puedes personalizar los campos de texto con diferentes funciones para cada botón de acción.
- En la llamada a función
TipTimeScreen()
, actualiza la primera llamada a funciónEditNumberField()
para que incluya un parámetro con nombrekeyboardActions
como argumento nuevo. Asígnale un valor,KeyboardActions( onNext =
{ }
)
:
// Bill amount text field
EditNumberField(
//...
keyboardActions = KeyboardActions(
onNext = { }
),
//...
)
La expresión lambda del parámetro onNext
nombrado se ejecuta cuando el usuario presiona el botón de acción Next en el teclado.
- Define la lambda, solicita
FocusManager
para mover el enfoque hacia abajo al siguiente elemento que admite composición, Tip %. En la expresión lambda, llama a la funciónmoveFocus()
en el objetofocusManager
y pasa el argumentoFocusDirection.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 }
)
La función moveFocus()
mueve el enfoque en la dirección especificada, que se encuentra hacia abajo hasta el campo de texto (en este caso, Tip %).
- Importa lo siguiente:
import androidx.compose.ui.focus.FocusDirection
- Agrega una implementación similar al campo de texto Tip %. La diferencia es que debes definir un parámetro llamado
onDone
, en lugar deonNext
.
// Tip% text field
EditNumberField(
//...
keyboardActions = KeyboardActions(
onDone = { }
),
//...
)
- Una vez que el usuario ingresa la propina personalizada, la acción Done en el teclado debe borrar el enfoque, lo que, a su vez, cierra el teclado. Define la lambda y solicita
FocusManager
para borrar el enfoque. En la expresión lambda, llama a la funciónclearFocus()
en el objetofocusManager
:
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 }
)
La función clearFocus()
borra el enfoque del componente enfocado.
- Ejecuta la app. Las acciones del teclado ahora cambian el enfoque del componente, como se puede ver en este GIF:
6. Agrega un interruptor
Un interruptor activa o desactiva el estado de un solo elemento. Existen dos estados en un interruptor que permiten al usuario seleccionar entre dos opciones. Un interruptor consiste en un círculo y una barra, como se puede ver en estas imágenes:
1. Círculo |
El interruptor es un control de selección que se puede usar para ingresar decisiones o declarar preferencias, como los parámetros que se pueden ver en estas imágenes:
El usuario puede arrastrar el círculo hacia adelante y hacia atrás para elegir la opción seleccionada, o simplemente presionar el botón de interruptor. Puedes ver otro ejemplo de un interruptor en este GIF, en el que la configuración de Opciones visuales está activada (Modo oscuro):
Para obtener más información, consulta la documentación sobre interruptores.
Usas el elemento componible Switch
para que el usuario pueda elegir si redondea la propina al número entero más cercano como se muestra en la siguiente imagen:
Agrega una fila para los elementos que admiten composición Text
y Switch
:
- Después de la función
EditNumberField()
, agrega una función de componibilidadRoundTheTipRow()
y pasa unModifier
predeterminado, por ejemplo, argumentos similares a la funciónEditNumberField()
:
@Composable
fun RoundTheTipRow(modifier: Modifier = Modifier) {
}
- Implementa la función
RoundTheTipRow()
, agrega un elemento de diseño que admite composiciónRow
con el siguientemodifier
para establecer el ancho de los elementos secundarios al máximo en la pantalla, centrar la alineación y garantizar un tamaño de48
dp
:
Row(
modifier = Modifier
.fillMaxWidth()
.size(48.dp),
verticalAlignment = Alignment.CenterVertically
) {
}
- Importa lo siguiente:
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Size
- En el bloque de lambda del elemento de componibilidad de diseño
Row
, agrega un elemento de componibilidadText
y usa el recurso de cadenasR.string.round_up_tip
para mostrar una cadenaRound up tip?
:
Text(text = stringResource(R.string.round_up_tip))
- Después del elemento
Text
componible, agrega un elementoSwitch
también componible, pasa un parámetro con nombrechecked
establecido enroundUp
y un parámetro con nombreonCheckedChange
establecido enonRoundUpChanged
.
Switch(
checked = roundUp,
onCheckedChange = onRoundUpChanged,
)
Esta tabla contiene información sobre estos parámetros, que son los mismos que definiste para la función RoundTheTipRow()
:
Parámetro | Descripción |
| Indica si el interruptor está marcado. Este es el estado del elemento |
| Es la devolución de llamada a la que se llamará cuando se haga clic en el interruptor. |
- Importa lo siguiente:
import androidx.compose.material.Switch
- En la función
RoundTipRow()
, agrega un parámetroroundUp
de tipoBoolean
y una función lambdaonRoundUpChanged
que tome unBoolean
, pero no muestre nada:
@Composable
fun RoundTheTipRow(
roundUp: Boolean,
onRoundUpChanged: (Boolean) -> Unit,
modifier: Modifier = Modifier
)
Con esta acción, se eleva el estado del interruptor.
- En el elemento de componibilidad
Switch
, agrega estemodifier
para alinear el elemento de componibilidadSwitch
al final de la pantalla:
Switch(
modifier = modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.End),
//...
)
- Importa lo siguiente:
import androidx.compose.foundation.layout.wrapContentWidth
- En la función
TipTimeScreen()
, agrega una variable var para el estado del elementoSwitch
componible. Crea una variablevar
llamadaroundUp
establecida enmutableStateOf()
, confalse
como argumento predeterminado. Rodea la llamada conremember { }
.
fun TipTimeScreen() {
//...
var roundUp by remember { mutableStateOf(false) }
//...
Column(
...
) {
//...
}
}
Esta es la variable del estado que admite composición Switch
y el valor falso será el predeterminado.
- En el bloque
Column
de la funciónTipTimeScreen()
, después del campo de texto Tip %, llama a la funciónRoundTheTipRow()
con los siguientes argumentos: un parámetro con nombreroundUp
establecido enroundUp
y un parámetro con nombreonRoundUpChanged
establecido en una devolución de llamada lambda que actualice el valorroundUp
:
@Composable
fun TipTimeScreen() {
//...
Column(
...
) {
Text(
...
)
Spacer(...)
EditNumberField(
...
)
EditNumberField(
...
)
RoundTheTipRow(roundUp = roundUp, onRoundUpChanged = { roundUp = it })
Spacer(...)
Text(
...
)
}
}
Se mostrará la fila Round up tip (Redondear propina).
- Ejecuta la app. Esta muestra el interruptor Round up tip? (¿Redondear propina?), pero el círculo es apenas visible, como se ve en esta imagen:
1. Círculo |
En los próximos pasos, mejorarás la visibilidad del círculo volviéndolo gris oscuro.
- En el elemento componible
Switch()
de la funciónRoundTheTipRow()
, agrega un parámetro llamadocolors
. - Configura ese parámetro
colors
en una funciónSwitchDefaults.colors()
que acepte un parámetro llamadouncheckedThumbColor
establecido en un argumentoColor.DarkGray
.
Switch(
//...
colors = SwitchDefaults.colors(
uncheckedThumbColor = Color.DarkGray
)
)
- Importa lo siguiente:
import androidx.compose.material.SwitchDefaults
import androidx.compose.ui.graphics.Color
La función de componibilidad RoundTheTipRow()
ahora debería verse como este fragmento de código:
@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
)
)
}
}
- Ejecuta la app. Como puedes ver en la imagen, el círculo del interruptor es de otro color.
- Ingresa un importe de la factura y un porcentaje de propina y selecciona el interruptor Round up tip?. El importe de la propina no se redondea porque todavía necesitas actualizar la función
calculateTip()
; lo haremos en la siguiente sección.
Actualiza la función calculateTip()
para redondear la propina.
Modifica la función calculateTip()
de modo que acepte una variable Boolean
para redondear la propina al número entero más cercano:
- Para redondear la propina, la función
calculateTip()
debe conocer el estado del interruptor, que es unBoolean
. En la funcióncalculateTip()
, agrega un parámetroroundUp
de tipoBoolean
:
private fun calculateTip(
amount: Double,
tipPercent: Double = 15.0,
roundUp: Boolean
): String {
//...
}
- En la función
calculateTip()
, antes de la sentenciareturn
, agrega una condiciónif()
que verifique el valorroundUp
. SiroundUp
estrue
, define una variabletip
, establécela enkotlin.math.
ceil
()
y pasa la funcióntip
como argumento:
if (roundUp)
tip = kotlin.math.ceil(tip)
La función calculateTip()
completa debería verse como este fragmento de código:
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)
}
- En la función
TipTimeScreen()
, actualiza la llamada a la funcióncalculateTip()
y pasa un parámetroroundUp
:
val tip = calculateTip(amount, tipPercent, roundUp)
- Ejecuta la app. Ahora, esta redondea el importe de la propina, como puedes ver en las siguientes imágenes:
7. Obtén el código de la solución
Para descargar el código del codelab terminado, puedes usar este comando de git:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-tip-calculator.git
También puedes descargar el repositorio como un archivo ZIP, descomprimirlo y abrirlo en Android Studio.
Si deseas ver el código de la solución, puedes hacerlo en GitHub.
8. Conclusión
¡Felicitaciones! Agregaste la funcionalidad de propina personalizada a tu app de Tip Time. Ahora, la app permite que los usuarios ingresen un porcentaje de propina personalizado y redondeen el importe correspondiente. Comparte tu trabajo en redes sociales con el hashtag #AndroidBasics.