jetpack compose 中的动画瓶装水尺寸错误

问题描述 投票:0回答:1

此代码计算一个人每天应该喝的水量。当您单击按钮时,它会减少满瓶中的水。

实际上,程序中的所有内容都清晰可见,但在第一次单击时,水位不会因单击而改变。第一次点击后一切正常。我无法克服这个问题,你能帮助我吗?

 package com.example.diettrackingapp.home

import android.widget.Toast
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.animateIntAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.diettrackingapp.firebase.PatientViewModel

@Composable
fun WaterScreen() {
    val context = LocalContext.current
    var totalWaterAmount by remember { mutableStateOf(0) }
    var usedWaterAmount by remember { mutableStateOf(0) }


    val weight = 70

    totalWaterAmount = (weight * 35)


    val configuration = LocalConfiguration.current
    val screenWidth = configuration.screenWidthDp.dp

    Column(
        modifier = Modifier
            .width(screenWidth * 0.4f),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Top
    ) {
        Spacer(modifier = Modifier.height(25.dp))
        Column(
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            WaterBottle(
                modifier = Modifier
                    .width(screenWidth * 0.3f)
                    .height(screenWidth * 0.6f),
                totalWaterAmount = totalWaterAmount,
                unit = "ml",
                usedWaterAmount = usedWaterAmount
            )
            Spacer(modifier = Modifier.height(10.dp))
            Text(text = "Total:$totalWaterAmount ml")
            Button(onClick = {
                if (usedWaterAmount < totalWaterAmount) usedWaterAmount += 200 else Toast.makeText(
                    context, "Bottle is Empty", Toast.LENGTH_LONG
                ).show()
            }) {
                Text(text = "Drink")
            }
        }
    }
}

@Composable
fun WaterBottle(
    modifier: Modifier,
    totalWaterAmount: Int,
    unit: String,
    usedWaterAmount: Int,
    waterColor: Color = Color(0xFF1F97F8),
    bottleColor: Color = Color.White,
    capColor: Color = Color(0xFF01355F)
) {
    val waterPercentage by animateFloatAsState(
        targetValue = if (totalWaterAmount > 0) usedWaterAmount.toFloat() / totalWaterAmount.toFloat() else 0f,
        animationSpec = tween(1000), label = ""
    )

    val usedWaterAnimation by animateIntAsState(
        targetValue = usedWaterAmount, animationSpec = tween(1000), label = ""
    )

    Box(
        modifier = modifier
            .clip(RoundedCornerShape(10.dp))
            .background(Color(0xFFFDF7BA))
            .padding(10.dp)
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            val width = size.width
            val height = size.height

            val capWidth = size.width * 0.55f
            val capHeight = size.height * 0.08f

            val bottleBodyPath = Path().apply {
                moveTo(width * 0.3f, height * 0.1f)
                lineTo(
                    width * 0.3f, height * 0.2f
                )
                quadraticBezierTo(
                    0f, height * 0.3f, 0f, height * 0.4f
                )
                lineTo(
                    0f, height * 0.95f
                )
                quadraticBezierTo(
                    0f, height, width * 0.05f, height
                )
                lineTo(
                    width * 0.95f, height
                )
                quadraticBezierTo(
                    width, height, width, height * 0.95f
                )
                lineTo(
                    width, height * 0.4f
                )
                quadraticBezierTo(
                    width, height * 0.3f, width * 0.7f, height * 0.2f
                )
                lineTo(
                    width * 0.7f, height * 0.1f
                )
                close()
            }
            clipPath(
                bottleBodyPath
            ) {
                drawRect(
                    color = bottleColor, size = size
                )
                val waterWavesYPosition = waterPercentage * size.height
                val waterPath = Path().apply {
                    moveTo(0f, waterWavesYPosition)
                    lineTo(
                        size.width, waterWavesYPosition
                    )
                    lineTo(
                        size.width, size.height
                    )
                    lineTo(
                        0f, size.height
                    )
                    close()
                }
                drawPath(
                    waterPath, waterColor
                )
            }
            drawRoundRect(
                color = capColor,
                size = Size(capWidth, capHeight),
                topLeft = Offset(size.width / 2 - capWidth / 2f, 0f),
                cornerRadius = CornerRadius(45f, 45f)
            )
        }

        val fontSize = with(LocalConfiguration.current) { screenWidthDp * 0.03 }.sp

        val text = buildAnnotatedString {
            withStyle(
                SpanStyle(
                    color = Color.Black,
                    fontSize = fontSize
                )
            ) {
                append("Drunk Water:\n")
            }
            withStyle(
                style = SpanStyle(
                    color = Color.Black,
                    fontSize = fontSize * 2
                )
            ) {
                append(usedWaterAnimation.toString())
            }
            withStyle(
                style = SpanStyle(
                    color = Color.Black,
                    fontSize = fontSize
                )
            ) {
                append(" ")
                append(unit)
            }
        }
        Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            Text(text = text)
        }
    }
}

 this is the bottle

android kotlin android-jetpack-compose android-canvas
1个回答
0
投票

使用

capHeightRatio

调整瓶子路径和取水逻辑
@Composable
fun WaterScreen() {
    val context = LocalContext.current
    var usedWaterAmount by remember { mutableStateOf(0) }

    // Initialize totalWaterAmount once based on weight
    val weight = 70
    val totalWaterAmount = remember { weight * 35 }

    val configuration = LocalConfiguration.current
    val screenWidth = configuration.screenWidthDp.dp

    Column(
        modifier = Modifier
            .width(screenWidth * 0.4f),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Top
    ) {
        Spacer(modifier = Modifier.height(25.dp))
        Column(
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            WaterBottle(
                modifier = Modifier
                    .width(screenWidth * 0.3f)
                    .height(screenWidth * 0.6f),
                totalWaterAmount = totalWaterAmount,
                unit = "ml",
                usedWaterAmount = usedWaterAmount
            )
            Spacer(modifier = Modifier.height(10.dp))
            Text(text = "Total: $totalWaterAmount ml")
            Button(onClick = {
                if (usedWaterAmount < totalWaterAmount) usedWaterAmount += 200 else Toast.makeText(
                    context, "Bottle is Empty", Toast.LENGTH_LONG
                ).show()
            }) {
                Text(text = "Drink")
            }
        }
    }
}

@Composable
fun WaterBottle(
    modifier: Modifier,
    totalWaterAmount: Int,
    unit: String,
    usedWaterAmount: Int,
    waterColor: Color = Color(0xFF1F97F8),
    bottleColor: Color = Color.White,
    capColor: Color = Color(0xFF01355F)
) {
    val waterPercentage by animateFloatAsState(
        targetValue = if (totalWaterAmount > 0) usedWaterAmount.toFloat() / totalWaterAmount.toFloat() else 0f,
        animationSpec = tween(1000), label = ""
    )

    val usedWaterAnimation by animateIntAsState(
        targetValue = usedWaterAmount, animationSpec = tween(1000), label = ""
    )

    Box(
        modifier = modifier
            .clip(RoundedCornerShape(10.dp))
            .background(Color(0xFFFDF7BA))
            .padding(10.dp)
    ) {
        Canvas(modifier = Modifier.fillMaxSize()) {
            val width = size.width
            val height = size.height

            // Adjust the starting position below the cap
            val capHeightRatio = 0.1f // Adjust this value based on your cap height
            val bodyStartY = height * capHeightRatio

            // Draw bottle body
            val bottleBodyPath = Path().apply {
                moveTo(width * 0.3f, bodyStartY)
                lineTo(width * 0.3f, bodyStartY + height * 0.1f)
                quadraticBezierTo(0f, bodyStartY + height * 0.2f, 0f, bodyStartY + height * 0.3f)
                lineTo(0f, height * 0.95f)
                quadraticBezierTo(0f, height, width * 0.05f, height)
                lineTo(width * 0.95f, height)
                quadraticBezierTo(width, height, width, height * 0.95f)
                lineTo(width, bodyStartY + height * 0.3f)
                quadraticBezierTo(width, bodyStartY + height * 0.2f, width * 0.7f, bodyStartY + height * 0.1f)
                lineTo(width * 0.7f, bodyStartY)
                close()
            }
            clipPath(bottleBodyPath) {
                drawRect(color = bottleColor, size = size)

                // Draw water
                val waterWavesYPosition = bodyStartY + waterPercentage * (height - bodyStartY)
                val waterPath = Path().apply {
                    moveTo(0f, waterWavesYPosition)
                    lineTo(size.width, waterWavesYPosition)
                    lineTo(size.width, size.height)
                    lineTo(0f, size.height)
                    close()
                }
                drawPath(waterPath, waterColor)
            }

            // Draw cap outside the clipping
            val capWidth = size.width * 0.55f
            val capHeight = height * capHeightRatio
            drawRoundRect(
                color = capColor,
                size = Size(capWidth, capHeight),
                topLeft = Offset(size.width / 2 - capWidth / 2f, 0f),
                cornerRadius = CornerRadius(45f, 45f)
            )
        }

        // Draw text in the center
        val fontSize = with(LocalConfiguration.current) { screenWidthDp * 0.03 }.sp
        val text = buildAnnotatedString {
            withStyle(SpanStyle(color = Color.Black, fontSize = fontSize)) {
                append("Drunk Water:\n")
            }
            withStyle(SpanStyle(color = Color.Black, fontSize = fontSize * 2)) {
                append(usedWaterAnimation.toString())
            }
            withStyle(SpanStyle(color = Color.Black, fontSize = fontSize)) {
                append(" ")
                append(unit)
            }
        }
        Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            Text(text = text)
        }
    }
}

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.