29 - Canvas - drawArc (gráfico de tarta)

Problema

Confeccionar una aplicación que permita ingresar los gastos de tres meses y luego al presionar un botón mostrar un gráfico de tarta que represente los gastos de cada mes.

Crearemos un proyecto llamado: 'Compose31'

La interfaz visual a implementar debe ser similar a:

drawArc Canvas Jetpack Compose

El código a implementar en Kotlin para obtener dicha funcionalidad es:

package com.tutorialesprogramacionya.compose31

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PantallaPrincipal()
        }
    }
}

@Composable
fun PantallaPrincipal() {
    var mes1 by remember { mutableStateOf("") }
    var mes2 by remember { mutableStateOf("") }
    var mes3 by remember { mutableStateOf("") }
    var visible by remember { mutableStateOf(false)}
    Column() {
        OutlinedTextField(
            value = mes1,
            onValueChange = { mes1 = it },
            label = {
                Text("Gastos del mes 1")
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            singleLine = true,
            colors = TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color.Red,
                unfocusedBorderColor = Color.Red),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
        )
        OutlinedTextField(
            value = mes2,
            onValueChange = { mes2 = it },
            label = {
                Text("Gastos del mes 2")
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            singleLine = true,
            colors = TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color.Green,
                unfocusedBorderColor = Color.Green),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
        )
        OutlinedTextField(
            value = mes3,
            onValueChange = { mes3 = it },
            label = {
                Text("Gastos del mes 3")
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            singleLine = true,
            colors = TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color.Blue,
                unfocusedBorderColor = Color.Blue),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
        )
        Button(onClick = { visible=true },modifier = Modifier
            .fillMaxWidth()
            .padding(10.dp),) {
            Text(text = "Mostrar gráfico de tarta")
        }
        if (visible && mes1!=""  && mes2!=""  && mes3!="")
            GraficoTarta(mes1 = mes1.toInt(), mes2 = mes2.toInt(), mes3 = mes3.toInt())
    }
}

@Composable
fun GraficoTarta(mes1:Int,mes2:Int,mes3:Int) {
    val suma =mes1+mes2+mes3
    val grados1=mes1.toFloat()/suma.toFloat()*360
    val grados2=mes2.toFloat()/suma.toFloat()*360
    val grados3=mes3.toFloat()/suma.toFloat()*360
    Canvas(modifier = Modifier.fillMaxSize()) {
        val diametro=if(size.width>size.height) size.height else size.width
        drawArc(startAngle = 0f,sweepAngle = grados1,useCenter = true,color= Color.Red,size = Size(diametro,diametro))
        drawArc(startAngle = grados1,sweepAngle = grados2,useCenter = true,color= Color.Green,size = Size(diametro,diametro))
        drawArc(startAngle = grados1+grados2,sweepAngle = grados3,useCenter = true,color= Color.Blue,size = Size(diametro,diametro))
    }

}

Guardamos el estado de los 3 valores ingresados por teclado y una bandera que nos indica si se ha presionado el botón para mostrar el gráfico de tarta:

    var mes1 by remember { mutableStateOf("") }
    var mes2 by remember { mutableStateOf("") }
    var mes3 by remember { mutableStateOf("") }
    var visible by remember { mutableStateOf(false)}

En una columna componemos tres OutlineTextField:

    Column() {
        OutlinedTextField(
            value = mes1,
            onValueChange = { mes1 = it },
            label = {
                Text("Gastos del mes 1")
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(10.dp),
            singleLine = true,
            colors = TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color.Red,
                unfocusedBorderColor = Color.Red),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
        )

Cuando la bandera visiblle se encuentra en verdadero y los tres datos se encuentran cargados se procede a llamar a la función componible GraficoTarta:

Obtenemos la cantidad de grados que le corresponde a cada gasto y mediante la llamada a la función drawArc dibujamos cada trozo de tarta:

@Composable
fun GraficoTarta(mes1:Int,mes2:Int,mes3:Int) {
    val suma =mes1+mes2+mes3
    val grados1=mes1.toFloat()/suma.toFloat()*360
    val grados2=mes2.toFloat()/suma.toFloat()*360
    val grados3=mes3.toFloat()/suma.toFloat()*360
    Canvas(modifier = Modifier.fillMaxSize()) {
        val diametro=if(size.width>size.height) size.height else size.width
        drawArc(startAngle = 0f,sweepAngle = grados1,useCenter = true,color= Color.Red,size = Size(diametro,diametro))
        drawArc(startAngle = grados1,sweepAngle = grados2,useCenter = true,color= Color.Green,size = Size(diametro,diametro))
        drawArc(startAngle = grados1+grados2,sweepAngle = grados3,useCenter = true,color= Color.Blue,size = Size(diametro,diametro))
    }
}

Tomamos como ancho y alto el mismo valor para que la tarta sea un círculo:

        val diametro=if(size.width>size.height) size.height else size.width

Este proyecto lo puede descargar en un zip desde este enlace: Compose31.zip