From d1bd53eca33c9dabae95be951556979b2f63ff11 Mon Sep 17 00:00:00 2001 From: Raihan Ariq <202310715297@mhs.ubharajaya.ac.id> Date: Fri, 7 Nov 2025 02:00:11 +0700 Subject: [PATCH] Penyesuaian Tata Letak dan Code agar lebih clean --- app/build.gradle.kts | 1 + .../com/example/bmicalculator/MainActivity.kt | 139 +++++++++++++----- .../example/bmicalculator/ui/theme/Color.kt | 58 ++++---- app/src/main/res/values/strings.xml | 8 +- 4 files changed, 139 insertions(+), 67 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5aaa38d..5effd10 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -74,6 +74,7 @@ dependencies { implementation("androidx.core:core-ktx:1.15.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") implementation("junit:junit:4.12") + implementation("androidx.compose.foundation:foundation:1.9.4") // Unit Testing testImplementation("junit:junit:4.13.2") diff --git a/app/src/main/java/com/example/bmicalculator/MainActivity.kt b/app/src/main/java/com/example/bmicalculator/MainActivity.kt index f728342..b836674 100644 --- a/app/src/main/java/com/example/bmicalculator/MainActivity.kt +++ b/app/src/main/java/com/example/bmicalculator/MainActivity.kt @@ -29,6 +29,7 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.annotation.DrawableRes import androidx.annotation.StringRes +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -38,11 +39,14 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -59,8 +63,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.example.bmicalculator.ui.theme.BMICalculatorTheme @@ -90,16 +96,20 @@ fun BMICalculatorLayout() { var weightInput by remember { mutableStateOf("") } var unitUSC by remember { mutableStateOf(false) } - // Reset Nilai Input + // State baru untuk mengontrol kapan output ditampilkan + var showResult by remember { mutableStateOf(false) } + + // Reset nilai input ketika unit berubah LaunchedEffect(unitUSC) { heightInput = "" weightInput = "" + showResult = false } - val BmiHeight = heightInput.toDoubleOrNull() ?: 0.0 - val BmiWeight = weightInput.toDoubleOrNull() ?: 0.0 - val bmi = calculateBMI(BmiHeight, BmiWeight, unitUSC) - val category = calculateBMICategory(bmi) + val bmiHeight = heightInput.toDoubleOrNull() ?: 0.0 + val bmiWeight = weightInput.toDoubleOrNull() ?: 0.0 + val bmi = calculateBMI(bmiHeight, bmiWeight, unitUSC) + val category = calculateBMICategory(bmi) Column( modifier = Modifier @@ -107,17 +117,28 @@ fun BMICalculatorLayout() { .padding(horizontal = 40.dp) .verticalScroll(rememberScrollState()) .safeDrawingPadding(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + horizontalAlignment = Alignment.CenterHorizontally ) { + + Image( + painter = painterResource(R.drawable.health_report), + contentDescription = "BMI Icon", + modifier = Modifier + .padding(top = 24.dp, bottom = 16.dp) + .size(80.dp) + ) + Text( text = stringResource(R.string.calculate_bmi), + style = MaterialTheme.typography.headlineLarge.copy(fontWeight = FontWeight.Bold), + textAlign = TextAlign.Center, modifier = Modifier - .padding(bottom = 16.dp, top = 40.dp) - .align(alignment = Alignment.Start) + .fillMaxWidth() + .padding(bottom = 32.dp) ) + EditNumberField( - label = if (unitUSC == true) R.string.heightInch else R.string.heightCm, + label = if (unitUSC) R.string.heightInch else R.string.heightCm, leadingIcon = R.drawable.number, keyboardOptions = KeyboardOptions.Default.copy( keyboardType = KeyboardType.Number, @@ -125,8 +146,11 @@ fun BMICalculatorLayout() { ), value = heightInput, onValueChanged = { heightInput = it }, - modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), + modifier = Modifier + .padding(bottom = 32.dp) + .fillMaxWidth() ) + EditNumberField( label = if (unitUSC) R.string.weightPound else R.string.weightKg, leadingIcon = R.drawable.number, @@ -136,26 +160,72 @@ fun BMICalculatorLayout() { ), value = weightInput, onValueChanged = { weightInput = it }, - modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), + modifier = Modifier + .padding(bottom = 32.dp) + .fillMaxWidth() ) + UnitUSCFormulaRow( unitUSC = unitUSC, onUSCChanged = { unitUSC = it }, - modifier = Modifier.padding(bottom = 32.dp) - ) - Text( - text = stringResource(R.string.bmi_calculation, bmi), - style = MaterialTheme.typography.displaySmall - ) - Text( - text = stringResource(R.string.bmi_category, category), - style = MaterialTheme.typography.displaySmall + modifier = Modifier.padding(bottom = 24.dp) ) + + // Button Kalkulasi dan Clear Field Text + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { + showResult = true + }, + modifier = Modifier.weight(1f) + ) { + Text("Calculate") + } + + Spacer(modifier = Modifier.width(16.dp)) + + Button( + onClick = { + heightInput = "" + weightInput = "" + showResult = false + }, + modifier = Modifier.weight(1f) + ) { + Text("Clear") + } + } + + Spacer(modifier = Modifier.height(32.dp)) + + if (showResult) { + Text( + text = "BMI: $bmi", + style = MaterialTheme.typography.displaySmall, + textAlign = TextAlign.Center, + fontWeight = FontWeight.SemiBold, + modifier = Modifier.fillMaxWidth() + ) + + Text( + text = category, + style = MaterialTheme.typography.displayMedium, + textAlign = TextAlign.Center, + fontWeight = FontWeight.ExtraBold, + modifier = Modifier.fillMaxWidth() + ) + } Spacer(modifier = Modifier.height(150.dp)) } } + + // Fungsi media Input Tinggi Badan dan Berat Badan @Composable fun EditNumberField( @@ -187,7 +257,7 @@ fun UnitUSCFormulaRow( modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { - Text(text = stringResource(R.string.use_usc)) + Text(text = stringResource(R.string.use_usc), fontWeight = FontWeight.SemiBold) Switch( modifier = Modifier .fillMaxWidth() @@ -204,18 +274,18 @@ fun UnitUSCFormulaRow( * dan * dengan Rumus USC Units * - * Catatan: tambahkan unit test untuk kalkulasi BMI ini + * Catatan: Unit Testing Sudah ada di src/test/java/calculateBMITest.kt */ -fun calculateBMI(BmiHeight: Double, BmiWeight: Double, unitUSC: Boolean): String { - if (BmiHeight <= 0 || BmiWeight <= 0){ +fun calculateBMI(bmiHeight: Double, bmiWeight: Double, unitUSC: Boolean): String { + if (bmiHeight <= 0 || bmiWeight <= 0){ return "0.0" } - val heightInMeter = BmiHeight/100 // konversi centimeter ke meter + val heightInMeter = bmiHeight/100 // konversi centimeter ke meter - var bmi = BmiWeight / heightInMeter.pow(2) - if (unitUSC == true) { - bmi = 703 * (BmiWeight / BmiHeight.pow(2)) + var bmi = bmiWeight / heightInMeter.pow(2) + if (unitUSC) { + bmi = 703 * (bmiWeight / bmiHeight.pow(2)) } val df = DecimalFormat("#.#") @@ -229,17 +299,18 @@ fun calculateBMI(BmiHeight: Double, BmiWeight: Double, unitUSC: Boolean): String * 25 <= bmi <= 30 -> Normal * bmi > 30 -> Overweight * - * Catatan: tambahkan unit test untuk kalkulasi BMI ini + * Catatan: Unit Testing Sudah ada di src/test/java/calculateBMITest.kt + * */ fun calculateBMICategory(bmi: String): String { val bmiValue = bmi.replace(",", ".").toDoubleOrNull() ?: return "" return when { bmiValue == 0.0 -> "" - bmiValue < 18.5 -> "Underweight" - bmiValue < 25.0 -> "Normal" - bmiValue < 30.0 -> "Overweight" - else -> "Obesity" + bmiValue < 18.5 -> "⚠️ Underweight" + bmiValue < 25.0 -> "✅ Normal" + bmiValue < 30.0 -> "⚠️ Overweight" + else -> "‼️ Obesity" } } diff --git a/app/src/main/java/com/example/bmicalculator/ui/theme/Color.kt b/app/src/main/java/com/example/bmicalculator/ui/theme/Color.kt index 539c971..59c4af9 100644 --- a/app/src/main/java/com/example/bmicalculator/ui/theme/Color.kt +++ b/app/src/main/java/com/example/bmicalculator/ui/theme/Color.kt @@ -33,10 +33,10 @@ val md_theme_light_error = Color(0xFFBA1A1A) val md_theme_light_errorContainer = Color(0xFFFFDAD6) val md_theme_light_onError = Color(0xFFFFFFFF) val md_theme_light_onErrorContainer = Color(0xFF410002) -val md_theme_light_background = Color(0xFFFAFCFF) -val md_theme_light_onBackground = Color(0xFF001F2A) -val md_theme_light_surface = Color(0xFFFAFCFF) -val md_theme_light_onSurface = Color(0xFF001F2A) +val md_theme_light_background = Color(0xFFE3E2DE) +val md_theme_light_onBackground = Color(0xFF1351AA) +val md_theme_light_surface = Color(0xFFE3E2DE) +val md_theme_light_onSurface = Color(0xFF1351AA) val md_theme_light_surfaceVariant = Color(0xFFF2DDE2) val md_theme_light_onSurfaceVariant = Color(0xFF514347) val md_theme_light_outline = Color(0xFF837377) @@ -47,32 +47,32 @@ val md_theme_light_surfaceTint = Color(0xFF984061) val md_theme_light_outlineVariant = Color(0xFFD5C2C6) val md_theme_light_scrim = Color(0xFF000000) -val md_theme_dark_primary = Color(0xFFFFB0C8) -val md_theme_dark_onPrimary = Color(0xFF5E1133) -val md_theme_dark_primaryContainer = Color(0xFF7B2949) -val md_theme_dark_onPrimaryContainer = Color(0xFFFFD9E2) -val md_theme_dark_secondary = Color(0xFFDEB7FF) -val md_theme_dark_onSecondary = Color(0xFF44196A) -val md_theme_dark_secondaryContainer = Color(0xFF5C3382) -val md_theme_dark_onSecondaryContainer = Color(0xFFF1DBFF) -val md_theme_dark_tertiary = Color(0xFFFFB1C7) -val md_theme_dark_onTertiary = Color(0xFF5E1132) -val md_theme_dark_tertiaryContainer = Color(0xFF7B2948) -val md_theme_dark_onTertiaryContainer = Color(0xFFFFD9E2) +val md_theme_dark_primary = Color(0xFFE3E2DE) +val md_theme_dark_onPrimary = Color(0xFF1351AA) +val md_theme_dark_primaryContainer = Color(0xFFE3E2DE) +val md_theme_dark_onPrimaryContainer = Color(0xFF1351AA) +val md_theme_dark_secondary = Color(0xFFE3E2DE) +val md_theme_dark_onSecondary = Color(0xFF1351AA) +val md_theme_dark_secondaryContainer = Color(0xFFE3E2DE) +val md_theme_dark_onSecondaryContainer = Color(0xFF1351AA) +val md_theme_dark_tertiary = Color(0xFFE3E2DE) +val md_theme_dark_onTertiary = Color(0xFF1351AA) +val md_theme_dark_tertiaryContainer = Color(0xFFE3E2DE) +val md_theme_dark_onTertiaryContainer = Color(0xFFE3E2DE) val md_theme_dark_error = Color(0xFFFFB4AB) val md_theme_dark_errorContainer = Color(0xFF93000A) val md_theme_dark_onError = Color(0xFF690005) val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) -val md_theme_dark_background = Color(0xFF001F2A) -val md_theme_dark_onBackground = Color(0xFFBFE9FF) -val md_theme_dark_surface = Color(0xFF001F2A) -val md_theme_dark_onSurface = Color(0xFFBFE9FF) -val md_theme_dark_surfaceVariant = Color(0xFF514347) -val md_theme_dark_onSurfaceVariant = Color(0xFFD5C2C6) -val md_theme_dark_outline = Color(0xFF9E8C90) -val md_theme_dark_inverseOnSurface = Color(0xFF001F2A) -val md_theme_dark_inverseSurface = Color(0xFFBFE9FF) -val md_theme_dark_inversePrimary = Color(0xFF984061) -val md_theme_dark_surfaceTint = Color(0xFFFFB0C8) -val md_theme_dark_outlineVariant = Color(0xFF514347) -val md_theme_dark_scrim = Color(0xFF000000) +val md_theme_dark_background = Color(0xFF1351AA) +val md_theme_dark_onBackground = Color(0xFFE3E2DE) +val md_theme_dark_surface = Color(0xFF1351AA) // Warna Background depan +val md_theme_dark_onSurface = Color(0xFFE3E2DE) // Warna font Background depan +val md_theme_dark_surfaceVariant = Color(0xFFE3E2DE) +val md_theme_dark_onSurfaceVariant = Color(0xFFE3E2DE) +val md_theme_dark_outline = Color(0xFFE3E2DE) // Warna Opsi +val md_theme_dark_inverseOnSurface = Color(0xFF1351AA) +val md_theme_dark_inverseSurface = Color(0xFFE3E2DE) +val md_theme_dark_inversePrimary = Color(0xFFE3E2DE) +val md_theme_dark_surfaceTint = Color(0xFF1351AA) +val md_theme_dark_outlineVariant = Color(0xFFE3E2DE) +val md_theme_dark_scrim = Color(0xFFE3E2DE) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 469b90d..09eb4f3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,12 +16,12 @@ --> BMI Calculator - Calculate BMI + BMI Calculator Tinggi Badan (cm) Berat Badan (kg) Tinggi Badan (inch) Berat Badan (lbs) - Gunakan Unit USC (lbs/in)? - BMI Anda: %s - Kategori: %s + USC Units (lbs/in) + BMI -> %s + Kategori -> %s