- Visualisasi Botol Air:
Menampilkan botol air dengan bentuk yang dapat diubah dan diwarnai sesuai keinginan.
Warna botol default adalah kuning, namun dapat diubah sesuai preferensi pengguna.
- Animasi Pengisian Air:
Animasi yang menunjukkan persentase air yang telah diisi.
- Interaksi Pengguna:
Pengguna dapat menambahkan jumlah air dengan menekan tombol "Isi Air".
Tampilan informasi mengenai jumlah air yang telah diisi.
- Desain yang Responsif:
Antarmuka yang responsif dan mudah digunakan pada berbagai ukuran layar.
- Dependensi:
Aplikasi ini menggunakan beberapa dependensi utama dari Jetpack Compose untuk membangun antarmuka pengguna yang modern dan responsif:
Material3 untuk komponen UI yang mengikuti pedoman desain Material Design.
Compose UI untuk komposisi dan tata letak.
Compose Foundation untuk elemen dasar UI seperti Canvas.
Compose Animation untuk animasi halus.
Berikut adalah source codenya:
Berikut adalah source codenya:
WaterBottle.kt
package com.example.waterbottleapp
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.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.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.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun WaterBottle(
modifier: Modifier = Modifier,
totalWaterAmount: Int,
unit: String,
usedWaterAmount: Int,
waterWavesColor: Color = Color(0xFFF4CE14),
bottleColor: Color = Color.Gray,
capColor: Color = Color(0xFF379777)
) {
val waterPercentage = animateFloatAsState(
targetValue = (usedWaterAmount.toFloat() / totalWaterAmount.toFloat()),
label = "Water Waves animation",
animationSpec = tween(durationMillis = 1000)
).value
val usedWaterAmountAnimation = animateIntAsState(
targetValue = usedWaterAmount,
label = "Used water amount animation",
animationSpec = tween(durationMillis = 1000)
).value
Box(
modifier = modifier
.width(200.dp)
.height(600.dp)
) {
Canvas(modifier = Modifier.fillMaxSize()) {
val width = size.width
val height = size.height
val capWidth = size.width * 0.55f
val capHeight = size.height * 0.13f
// Draw the bottle body
val bodyPath = Path().apply {
// Start at the top-left corner of the bottle's body
moveTo(width * 0.25f, height * 0.05f)
// Top curve
cubicTo(width * 0.25f, height * 0.05f, width * 0.1f, height * 0.15f, width * 0.25f, height * 0.2f)
// Left side curve
cubicTo(width * 0.25f, height * 0.2f, width * 0.05f, height * 0.4f, width * 0.05f, height * 0.6f)
// Left bottom curve
cubicTo(width * 0.05f, height * 0.6f, width * 0.15f, height * 0.95f, width * 0.3f, height * 0.95f)
// Bottom straight line
lineTo(width * 0.7f, height * 0.95f)
// Right bottom curve
cubicTo(width * 0.85f, height * 0.95f, width * 0.95f, height * 0.6f, width * 0.95f, height * 0.6f)
// Right side curve
cubicTo(width * 0.95f, height * 0.4f, width * 0.75f, height * 0.2f, width * 0.75f, height * 0.2f)
// Top curve to close the path
cubicTo(width * 0.75f, height * 0.15f, width * 0.6f, height * 0.05f, width * 0.25f, height * 0.05f)
close()
}
clipPath(
path = bodyPath
) {
// Draw the color of the bottle
drawRect(
color = bottleColor,
// size = size,
// topLeft = Offset(0f, 0f)
size = Size(size.width * 0.55f, size.height * 0.1f),
topLeft = Offset(size.width / 2 - size.width * 0.55f / 2f, 0f),
// cornerRadius = CornerRadius(45f, 45f)
)
// Draw the water waves
val waterWavesYPosition = (1 - waterPercentage) * size.height
val wavesPath = Path().apply {
moveTo(
x = 0f,
y = waterWavesYPosition
)
lineTo(
x = size.width,
y = waterWavesYPosition
)
lineTo(
x = size.width,
y = size.height
)
lineTo(
x = 0f,
y = size.height
)
close()
}
drawPath(
path = wavesPath,
color = waterWavesColor,
)
}
// Draw the bottle cap
drawRoundRect(
color = capColor,
size = Size(capWidth, capHeight),
topLeft = Offset(size.width / 2 - capWidth / 2f, 0f),
cornerRadius = CornerRadius(45f, 45f)
)
}
val text = buildAnnotatedString {
withStyle(
style = SpanStyle(
color = if (waterPercentage > 0.5f) bottleColor else waterWavesColor,
fontSize = 44.sp
)
) {
append(usedWaterAmountAnimation.toString())
}
withStyle(
style = SpanStyle(
color = if (waterPercentage > 0.5f) bottleColor else waterWavesColor,
fontSize = 22.sp
)
) {
append(" ")
append(unit)
}
}
Box(
modifier = Modifier
.fillMaxSize()
.fillMaxHeight(),
contentAlignment = Alignment.Center
) {
Text(text = text)
}
}
}
@Preview
@Composable
fun WaterBottlePreview() {
WaterBottle(
totalWaterAmount = 2500,
unit = "ml",
usedWaterAmount = 120
)
}
package com.example.waterbottleapp
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.example.waterbottleapp.ui.theme.WaterBottleAppTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WaterBottleAppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
var usedWaterAmount by remember {
mutableStateOf(100)
}
val totalWaterAmount = remember {
2500
}
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
WaterBottle(
totalWaterAmount = totalWaterAmount,
unit = "ml",
usedWaterAmount = usedWaterAmount
)
Spacer(modifier = Modifier.height(24.dp))
Text(
text = "Total Amount: $totalWaterAmount ml",
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = { if (usedWaterAmount < totalWaterAmount) usedWaterAmount += 200 },
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF379777))
) {
Text(text = "Isi Air")
}
}
}
}
}
}
}
Tampilan: