26 - Canvas - funciones reutilizables

Podemos implementar funciones que dibujen con la función composable 'Canvas' y llamarlas en forma sucesiva.

Problema

Confeccionar una aplicación que muestre tres dados, un botón y un texto. Cuando se presione un botón proceder a generar tres valores aleatorios, a partir de dichos valores llamar a una función componsable que reciba el valor aleatorio y pase a dibujar el dado.

La función que llama a la función que dibuja el dado debe almacenar el estado (en nuestro caso los tres valores aleatorios)

Si los tres valores son iguales mostrar un mensaje que "ganó", sino que "perdió".

Crearemos el proyecto 'Compose28'

La interfaz visual debe ser similar a:

Canvas juego de dados Jetpack Compose

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

package com.tutorialesprogramacionya.compose28

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlin.random.Random

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

@Composable
fun JuegoDeDados() {
    var dado1 by remember { mutableStateOf(1) }
    var dado2 by remember { mutableStateOf(1) }
    var dado3 by remember { mutableStateOf(1) }
    var resultado by remember { mutableStateOf("")}
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Si los 3 son iguales GANA",
            fontSize = 30.sp,
            fontFamily = FontFamily.Cursive
        )
        Dado(dado1)
        Dado(dado2)
        Dado(dado3)
        Button(onClick = {
            dado1 = Random.nextInt(1, 6)
            dado2 = Random.nextInt(1, 6)
            dado3 = Random.nextInt(1, 6)
            resultado=if (dado1==dado2 && dado1==dado3) "ganó" else "perdió"

        }) {
            Text("Tirar")
        }
        Text(text = "Resultado: $resultado")
    }
}


@Composable
fun Dado(valor: Int) {
    Canvas(
        modifier = Modifier
            .width(150.dp)
            .height(150.dp)
            .padding(10.dp)
    ) {
        val todo = size
        val radio = size.width / 12
        drawRoundRect(size = todo, color = Color.Black, cornerRadius = CornerRadius(10f, 10f))
        if (valor == 1 || valor == 3 || valor == 5)
            drawCircle(
                center = Offset(x = size.width * 0.5f, y = size.height * 0.5f),
                color = Color.White,
                radius = radio
            )
        if (valor == 2 || valor == 3 || valor == 4 || valor == 5 || valor == 6) {
            drawCircle(
                center = Offset(x = size.width * 0.3f, y = size.height * 0.3f),
                color = Color.White,
                radius = radio
            )
            drawCircle(
                center = Offset(x = size.width * 0.7f, y = size.height * 0.7f),
                color = Color.White,
                radius = radio
            )
        }
        if (valor == 4 || valor == 5 || valor == 6) {
            drawCircle(
                center = Offset(x = size.width * 0.7f, y = size.height * 0.3f),
                color = Color.White,
                radius = radio
            )
            drawCircle(
                center = Offset(x = size.width * 0.3f, y = size.height * 0.7f),
                color = Color.White,
                radius = radio
            )
        }
        if (valor == 6) {
            drawCircle(
                center = Offset(x = size.width * 0.3f, y = size.height * 0.5f),
                color = Color.White,
                radius = radio
            )
            drawCircle(
                center = Offset(x = size.width * 0.7f, y = size.height * 0.5f),
                color = Color.White,
                radius = radio
            )
        }
    }
}

La función composable 'JuegoDeDados' almacena el estado de los tres valores generados con el objeto de poder verificar si ganó o perdió:

@Composable
fun JuegoDeDados() {
    var dado1 by remember { mutableStateOf(1) }
    var dado2 by remember { mutableStateOf(1) }
    var dado3 by remember { mutableStateOf(1) }
    var resultado by remember { mutableStateOf("")}

Aquí seencuentra lo interesante donde llamamos a la otra función composable Dado, y le pasamos el número que debe mostrar el mismo, además llamamos a la función 3 veces (3 dados):

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Si los 3 son iguales GANA",
            fontSize = 30.sp,
            fontFamily = FontFamily.Cursive
        )
        Dado(dado1)
        Dado(dado2)
        Dado(dado3)
        Button(onClick = {
            dado1 = Random.nextInt(1, 6)
            dado2 = Random.nextInt(1, 6)
            dado3 = Random.nextInt(1, 6)
            resultado=if (dado1==dado2 && dado1==dado3) "ganó" else "perdió"

        }) {
            Text("Tirar")
        }
        Text(text = "Resultado: $resultado")
    }

Es importante notar que cuando se presiona el botón, generamos tres valores aleatorios y procedemos a actualizar las tres variables de estado, esto hace que se actualicen los gráficos de cada dado.

Finalmente la función composable "Dado" recibe como parámetro el número que debe mostrar.
Definimos el ancho del Canvas de 150*150 píxeles dp.

Con una serie de if graficamos los círculos que corresponden según la cara del dado a mostrar:

@Composable
fun Dado(valor: Int) {
    Canvas(
        modifier = Modifier
            .width(150.dp)
            .height(150.dp)
            .padding(10.dp)
    ) {
        val todo = size
        val radio = size.width / 12
        drawRoundRect(size = todo, color = Color.Black, cornerRadius = CornerRadius(10f, 10f))
        if (valor == 1 || valor == 3 || valor == 5)
            drawCircle(
                center = Offset(x = size.width * 0.5f, y = size.height * 0.5f),
                color = Color.White,
                radius = radio
            )
        if (valor == 2 || valor == 3 || valor == 4 || valor == 5 || valor == 6) {
            drawCircle(
                center = Offset(x = size.width * 0.3f, y = size.height * 0.3f),
                color = Color.White,
                radius = radio
            )
            drawCircle(
                center = Offset(x = size.width * 0.7f, y = size.height * 0.7f),
                color = Color.White,
                radius = radio
            )
        }
        if (valor == 4 || valor == 5 || valor == 6) {
            drawCircle(
                center = Offset(x = size.width * 0.7f, y = size.height * 0.3f),
                color = Color.White,
                radius = radio
            )
            drawCircle(
                center = Offset(x = size.width * 0.3f, y = size.height * 0.7f),
                color = Color.White,
                radius = radio
            )
        }
        if (valor == 6) {
            drawCircle(
                center = Offset(x = size.width * 0.3f, y = size.height * 0.5f),
                color = Color.White,
                radius = radio
            )
            drawCircle(
                center = Offset(x = size.width * 0.7f, y = size.height * 0.5f),
                color = Color.White,
                radius = radio
            )
        }
    }
}

Es importante notar que la función 'Dado' no tiene estado, solo tiene el objetivo de dibujar el dado cuando Compose lo requiera, que será cuando la variable de estado de la otra función cambie.

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