# PGL: Proyecto Calculadora Vamos a crear una aplicación donde utilizaremos todo lo aprendido. Como vimos en el proyecto anterior lo primero vamos a plantear la estrctura de nuestro proyecto para ello pulsamos en el botón derecho sobre la carpeta principal del proyecto dentro de Java->New->Package y creamos varias carpetas: - components : donde crearemos todos los componentes reutilizables en el proyecto. Dentro varios files: - BodyComponent.kt - CardComponent.kt - views: colocaremos las distintas vistas del proyecto en este caso será sólo una: - HomeView.kt Así quedará la estructura del proyecto: ![](https://hackmd.io/_uploads/H1S04wuM6.png) Si en el MainActivity cambiamos el contenido para que muestre el HomeView dará un error. Arréglalo! Seguidamente vamos a ir al ui.theme para añadir nuevos colores y cambiar algo el tema principal. en color.kt añadimos: ``` val Primary = Color(0xFF6AC451) ``` Y en Theme.kt en el LightColorScheme vamos a modificar para que el color primario sea el indicado: ``` private val LightColorScheme = lightColorScheme( primary = Primary, secondary = PurpleGrey40, tertiary = Pink40 ) ``` Empezamos con el HomeView y el HomeviewContent, creando un scaffold: ``` @OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeView(){ Scaffold (topBar ={ CenterAlignedTopAppBar(title = { Text(text ="App Descuentos")}, colors = TopAppBarDefaults.centerAlignedTopAppBarColors( containerColor = MaterialTheme.colorScheme.primary ) ) } ){ ContentHomeView(it) } } @Composable fun ContentHomeView(paddingValues: PaddingValues) { Column (modifier= Modifier .padding(paddingValues) .padding(10.dp) .fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Hola") } } ``` ## BodyComponents: Spacer Genérico Vamos a crear por lo pronto dos componentes que nos permitan poner espacio tanto vertical como horizontal pasando dicho espacio como parámetro ``` @Composable fun SpaceH(size: Dp =5.dp){ Spacer(modifier = Modifier.height(size)) } @Composable fun SpaceW(size: Dp=5.dp){ Spacer(modifier = Modifier.width(size)) } ``` ## BodyComponents: OutlinedTextField Genérico Creamos un input de texto "outlined", va a cambiar en tiempo de ejecución por lo que incluimos el onValueChange y además fíjate en que añadimos el keyboardOptions para que por defecto salga en el móvil el teclado numérico: ``` @OptIn(ExperimentalMaterial3Api::class) @Composable fun MainTextField(value:String, onValueChange:(String)->Unit, label:String){ OutlinedTextField( value = value, onValueChange = onValueChange, label={ Text(text = label)}, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier .fillMaxWidth() .padding(horizontal = 30.dp ) ) } ``` Para ver cómo nos va quedando incluye el siguiente código en el ContentHomeView: ``` @Composable fun ContentHomeView(paddingValues: PaddingValues) { Column (modifier= Modifier .padding(paddingValues) .padding(10.dp) .fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { var precio by remember { mutableStateOf("") } var descuento by remember { mutableStateOf("") } MainTextField(value = precio, onValueChange ={precio=it} , label ="Precio" ) MainTextField(value = descuento, onValueChange ={descuento=it} , label ="Descuento" ) } } ``` ## BodyComponents: OutlinedButton Genérico Vamos a añadir un botón a nuestros componentes: ``` @Composable fun MainButton(text:String, color: Color = MaterialTheme.colorScheme.primary, onClick:()->Unit){ OutlinedButton(onClick =onClick, colors=ButtonDefaults.outlinedButtonColors( contentColor = color, containerColor = Color.Transparent ), modifier = Modifier .fillMaxWidth() .padding(horizontal = 30.dp) ) { Text(text = text) } } ``` Añadimos al ContentHomeView las llamadas a dos botones, por ahora sin hacer nada, a ver qué tal va quedando: ``` MainButton(text = "Generar Descuento" ){ } MainButton(text = "Limpiar", color=Color.Red) { } ``` Debería ser algo así ![](https://hackmd.io/_uploads/SydVHFuG6.png) ## CardComponents: MainCard Las cards son componentes normales en cualquier aplicación móvil: https://developer.android.com/jetpack/compose/components/card Vamos a crear dos cards a partir de una MainCard que van a contener al resto de elementos: ``` @Composable fun TwoCards(title1:String,number1:Double, title2:String, number2:Double){ Row(modifier=Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly ){ MainCard(title = title1, number =number1 , modifier = Modifier .padding(start=30.dp) .weight(1f) ) SpaceW() MainCard(title = title2, number =number2 , modifier = Modifier .padding(end=30.dp) .weight(1f) ) } } @Composable fun MainCard(title: String, number:Double, modifier: Modifier){ Card(modifier=modifier, colors=CardDefaults.cardColors( containerColor = Color.LightGray ) ) { Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(16.dp) ) { Text(text = title, color= Color.Black, fontSize = 20.sp) Text(text ="$number€", color= Color.Black, fontSize = 20.sp) } } } ``` Nos faltaría proveer de los parámetros necesarios y llamar a estas cards desde ContentHomeView ``` @Composable fun ContentHomeView(paddingValues: PaddingValues) { Column (modifier= Modifier .padding(paddingValues) .padding(10.dp) .fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { var precio by remember { mutableStateOf("") } var descuento by remember { mutableStateOf("") } var precioDescuento by remember { mutableStateOf(0.0) } var totalDescuento by remember { mutableStateOf(0.0) } TwoCards(title1 = "Total", number1 =totalDescuento , title2 ="Descuento" , number2 =precioDescuento ) MainTextField(value = precio, onValueChange ={precio=it} , label ="Precio" ) SpaceH() MainTextField(value = descuento, onValueChange ={descuento=it} , label ="Descuento" ) SpaceH() MainButton(text = "Generar Descuento" ){ } MainButton(text = "Limpiar", color=Color.Red) { } } } ``` Al ejecutarse ahora mismo debería verse así: ![](https://hackmd.io/_uploads/HyHOJc_fT.png) # Calcular el descuento Ahora sólo faltaría hacer funcionar los botones y que calcule el descuento Añadimos estas dos funciones en HomeView.kt ``` fun calcularPrecio(precio:Double, descuento:Double):Double{ var res=precio - calcularDescuento(precio, descuento) return kotlin.math.round (res*100)/100 } fun calcularDescuento(precio:Double, descuento:Double): Double{ var res=precio *(1- descuento/100) return kotlin.math.round (res*100)/100 } ``` Y ya sólo quedaría hacer estas operaciones o limpiar al pulsar los botones: ``` @Composable fun ContentHomeView(paddingValues: PaddingValues) { Column (modifier= Modifier .padding(paddingValues) .padding(10.dp) .fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { var precio by remember { mutableStateOf("") } var descuento by remember { mutableStateOf("") } var precioDescuento by remember { mutableStateOf(0.0) } var totalDescuento by remember { mutableStateOf(0.0) } TwoCards(title1 = "Total", number1 =totalDescuento , title2 ="Descuento" , number2 =precioDescuento ) MainTextField(value = precio, onValueChange ={precio=it} , label ="Precio" ) SpaceH() MainTextField(value = descuento, onValueChange ={descuento=it} , label ="Descuento" ) SpaceH() MainButton(text = "Generar Descuento" ){ precioDescuento= calcularPrecio(precio.toDouble(), descuento.toDouble()) totalDescuento= calcularDescuento(precio.toDouble(), descuento.toDouble()) } MainButton(text = "Limpiar", color=Color.Red) { precio="" descuento="" precioDescuento=0.0 totalDescuento=0.0 } } } ``` ## Alertas Qué pasa si pulsamos el botón de calcular descuento sin haber introducido ningún valor?? La aplicación falla porque el botón esperaba tener los datos necesarios. Vamos a crear una alerta para estos casos.