revisi terbaru

This commit is contained in:
RafiFattan23 2025-11-07 12:59:51 +07:00
parent 0358bf15e4
commit 253cc3a6bb
8 changed files with 131 additions and 92 deletions

View File

@ -2,67 +2,61 @@
**Dibuat oleh:** **Dibuat oleh:**
👨‍💻 **Rafi Fattan Fitriardi** 👨‍💻 **Rafi Fattan Fitriardi**
🆔 **NPM: 202310715002** 🆔 **NPM:** 202310715002
🏫 **Pemrograman Perangkat Bergerak - F5A5** 🏫 **Kelas:** Pemrograman Perangkat Bergerak - F5A5
--- ---
## 📖 Deskripsi Aplikasi ## 📖 Deskripsi Aplikasi
Aplikasi **Kalkulator BMI (Body Mass Index)** ini dibuat sebagai proyek akhir mata kuliah **Pemrograman Perangkat Bergerak**. Aplikasi **Kalkulator BMI (Body Mass Index)** ini dibuat sebagai proyek akhir mata kuliah **Pemrograman Perangkat Bergerak**.
Tujuan utama aplikasi ini adalah membantu pengguna menghitung **Indeks Massa Tubuh (BMI)** berdasarkan **berat badan (kg)** dan **tinggi badan (cm)** untuk mengetahui apakah berat badan tergolong **kurang, ideal, berlebih, atau obesitas**. Tujuannya adalah membantu pengguna menghitung **Indeks Massa Tubuh (BMI)** berdasarkan **berat badan (kg/lbs)** dan **tinggi badan (cm/inci)** agar dapat mengetahui apakah berat badan tergolong **kurang, ideal, berlebih, atau obesitas**.
Aplikasi ini memiliki **dua halaman utama**: Aplikasi memiliki **dua halaman utama**:
1. **Halaman Biodata Pengembang** menampilkan informasi pembuat aplikasi (nama, NIM, kelas, dan foto), serta tombol **“MULAI”** untuk berpindah ke laman utama.
2. **Halaman Utama (Kalkulator BMI)** tempat pengguna menginput berat dan tinggi badan, menekan tombol **“Hitung BMI”**, lalu melihat hasil nilai BMI beserta kategori dan saran kesehatannya. 1. 🧑‍💻 **Halaman Biodata Pengembang**
Menampilkan informasi pengembang (nama, NIM, kelas, dan foto), serta tombol **“MULAI”** untuk berpindah ke halaman utama.
2. ⚖️ **Halaman Utama (Kalkulator BMI)**
Pengguna dapat menginput berat dan tinggi badan, menekan tombol **“Hitung BMI”**, dan melihat hasil nilai BMI beserta **kategori serta saran kesehatannya**.
--- ---
## ⚙️ Fitur Utama ## ⚙️ Fitur Utama
- Input berat dan tinggi badan secara interaktif (bisa satuan SI atau USC). - ✏️ Input berat dan tinggi badan secara interaktif (bisa satuan **SI** atau **USC**).
- Perhitungan otomatis nilai BMI. - 🧮 Perhitungan otomatis nilai BMI dengan opsi pembulatan hasil.
- Tampilan kategori hasil (Kurus, Normal, Gemuk, Obesitas). - 📊 Tampilan kategori hasil (Kurus, Normal, Gemuk, Obesitas).
- Antarmuka sederhana dan responsif. - 🎨 Antarmuka sederhana, bersih, dan responsif menggunakan **Jetpack Compose**.
- Navigasi antarhalaman menggunakan tombol **MULAI** dari halaman biodata. - 🔄 Navigasi antarhalaman dengan tombol **MULAI** dari halaman biodata.
--- ---
## 🧩 Teknologi yang Digunakan ## 🧩 Teknologi yang Digunakan
- **Android Studio (Kotlin)** - 💻 **Android Studio (Kotlin)**
- **XML Layouts** untuk desain antarmuka - 🧱 **Jetpack Compose** & **XML Layouts** untuk desain antarmuka
- **Intent** untuk navigasi antar activity - 🔗 **Intent** untuk navigasi antar activity
- **Drawable XML** untuk gradasi dan tema warna aplikasi - 🎨 **Drawable XML & colors.xml** untuk tema warna dan efek gradasi
- 🧪 **Unit Test (disarankan)** untuk menguji akurasi perhitungan BMI
---
## 💡 Struktur Proyek
```
app/
├── java/com/example/bmiapp/
│ ├── SplashActivity.kt // Halaman biodata pengembang
│ ├── MainActivity.kt // Halaman utama kalkulator BMI
├── res/
│ ├── layout/
│ │ ├── activity_splash.xml
│ │ ├── activity_main.xml
│ ├── drawable/
│ │ ├── splash_gradient.xml
│ ├── mipmap/
│ │ ├── ic_launcher.png // Ikon aplikasi
│ │ ├── ic_launcher_round.png
│ ├── values/
│ ├── colors.xml
│ ├── strings.xml
│ ├── themes.xml
└── AndroidManifest.xml
```
--- ---
## 🧠 Kontribusi & Kredit ## 🧠 Kontribusi & Kredit
Aplikasi ini dikembangkan dengan bantuan **ChatGPT (OpenAI)** dalam pembuatan kode, desain antarmuka, dan penyusunan dokumentasi. Aplikasi ini dikembangkan dengan bantuan **ChatGPT (OpenAI)** dalam pembuatan kode, desain antarmuka, dan dokumentasi.
Semua logika, pengujian, dan penyempurnaan dilakukan secara mandiri oleh pengembang. Semua logika perhitungan, pengujian, dan penyempurnaan dilakukan mandiri oleh pengembang.
--- ---
## 🕓 Change Log (Ringkas)
- ✅ **Migrasi kode dasar** dari kalkulator tip ke kalkulator BMI berbasis Kotlin Compose.
- ⚙️ **Penambahan mode satuan USC (Inci & Lbs)** dengan validasi tinggi minimal 4 inci.
- 🧮 **Perbaikan rumus perhitungan BMI** agar sesuai standar WHO.
- 🎨 **Desain ulang Splash Screen** dengan tombol “MULAI” berwarna hijau dan latar gradasi biru-hijau.
- 🚀 **Optimalisasi UX** — hasil BMI hanya muncul setelah tombol **“Hitung BMI”** ditekan.
- 🧰 **Penambahan file `colors.xml` dan drawable gradient** untuk tema.
---
## 📜 Lisensi
Proyek ini dibuat untuk tujuan pembelajaran dalam mata kuliah **Pemrograman Perangkat Bergerak** dan tidak untuk tujuan komersial.
Lisensi mengikuti [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
---

View File

@ -45,14 +45,13 @@ fun BmiCalculatorLayout() {
var weightInput by remember { mutableStateOf("") } var weightInput by remember { mutableStateOf("") }
var useMetricSystem by remember { mutableStateOf(true) } // ✅ toggle sistem satuan var useMetricSystem by remember { mutableStateOf(true) } // ✅ toggle sistem satuan
var errorMessage by remember { mutableStateOf("") } var errorMessage by remember { mutableStateOf("") }
var showResult by remember { mutableStateOf(false) } // ✅ hasil hanya tampil setelah tombol ditekan var showResult by remember { mutableStateOf(false) }
val height = heightInput.toFloatOrNull() ?: 0f val height = heightInput.toFloatOrNull() ?: 0f
val weight = weightInput.toFloatOrNull() ?: 0f val weight = weightInput.toFloatOrNull() ?: 0f
val isValid = validateInput(height, weight, useMetricSystem) val isValid = validateInput(height, weight, useMetricSystem)
// Konversi tinggi & berat ke sistem metrik (meter & kg)
val heightInMeters = if (useMetricSystem) height / 100f else height * 0.0254f val heightInMeters = if (useMetricSystem) height / 100f else height * 0.0254f
val weightInKg = if (useMetricSystem) weight else weight * 0.453592f val weightInKg = if (useMetricSystem) weight else weight * 0.453592f
@ -176,7 +175,6 @@ fun BmiCalculatorLayout() {
) )
} }
// Pesan error
if (errorMessage.isNotEmpty()) { if (errorMessage.isNotEmpty()) {
Text( Text(
text = errorMessage, text = errorMessage,
@ -186,7 +184,7 @@ fun BmiCalculatorLayout() {
) )
} }
// Hasil BMI hanya tampil setelah ditekan tombol // Hasil BMI
if (showResult && isValid) { if (showResult && isValid) {
val categoryColor = when (category) { val categoryColor = when (category) {
"Kurus" -> MaterialTheme.colorScheme.tertiaryContainer "Kurus" -> MaterialTheme.colorScheme.tertiaryContainer
@ -217,10 +215,34 @@ fun BmiCalculatorLayout() {
} }
} }
} }
// 📘 Panduan Kategori BMI
Spacer(modifier = Modifier.height(24.dp))
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Panduan Kategori BMI",
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.height(8.dp))
Divider()
Spacer(modifier = Modifier.height(8.dp))
Text("• Kurus : BMI < 18.5")
Text("• Normal : 18.5 ≤ BMI < 25")
Text("• Kelebihan Berat : 25 ≤ BMI < 30")
Text("• Obesitas : BMI ≥ 30")
}
}
} }
} }
} }
@Composable @Composable
fun EditNumberField( fun EditNumberField(
@StringRes label: Int, @StringRes label: Int,
@ -257,6 +279,16 @@ fun validateInput(height: Float, weight: Float, useMetric: Boolean): Boolean {
height >= 4f && height <= 100f && weight in 5f..550f height >= 4f && height <= 100f && weight in 5f..550f
} }
fun calculateBMI(weight: Float, height: Float, useMetric: Boolean = true): Float {
if (height <= 0) return 0f
val heightInMeters = if (useMetric) height / 100f else height * 0.0254f
val weightInKg = if (useMetric) weight else weight * 0.453592f
return weightInKg / heightInMeters.pow(2)
}
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
fun BmiCalculatorPreview() { fun BmiCalculatorPreview() {

View File

@ -1,9 +1,9 @@
package com.example.tiptime package com.example.tiptime
import android.content.Intent import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.widget.Button import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
class SplashActivity : AppCompatActivity() { class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -11,11 +11,9 @@ class SplashActivity : AppCompatActivity() {
setContentView(R.layout.activity_splash) setContentView(R.layout.activity_splash)
val btnMulai = findViewById<Button>(R.id.btnMulai) val btnMulai = findViewById<Button>(R.id.btnMulai)
btnMulai.setOnClickListener { btnMulai.setOnClickListener {
val intent = Intent(this, MainActivity::class.java) startActivity(Intent(this, MainActivity::class.java))
startActivity(intent) //finish()
//finish() // supaya tidak bisa kembali ke splash dengan tombol back
} }
} }
} }

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#1FFFFFFF" />
<stroke android:color="#40FFFFFF" android:width="2dp"/>
</shape>

View File

@ -1,13 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"> android:shape="rectangle">
<gradient <gradient
android:type="linear" android:startColor="@color/purple_200"
android:angle="270" android:centerColor="@color/teal_200"
android:startColor="#4A90E2" android:endColor="@color/purple_700"
android:centerColor="#6A5ACD" android:angle="270" />
android:endColor="#8A2BE2"
android:useLevel="false" />
</shape> </shape>

View File

@ -1,18 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center"
android:padding="24dp" android:padding="24dp"
android:background="@drawable/splash_gradient"> android:background="@drawable/splash_gradient"
tools:context=".SplashActivity">
<ImageView <ImageView
android:layout_width="150dp" android:layout_width="160dp"
android:layout_height="150dp" android:layout_height="160dp"
android:src="@mipmap/ic_launcher_round" android:src="@mipmap/ic_launcher_round"
android:contentDescription="Foto Profil" android:contentDescription="Foto Profil"
android:layout_marginBottom="16dp" /> android:layout_marginBottom="20dp"
android:padding="16dp" />
<TextView <TextView
android:id="@+id/tvName" android:id="@+id/tvName"
@ -20,12 +24,12 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Rafi Fattan Fitriardi" android:text="Rafi Fattan Fitriardi"
android:textStyle="bold" android:textStyle="bold"
android:textSize="20sp" android:textSize="22sp"
android:textColor="#FFFFFF" android:textColor="@android:color/white"
android:shadowColor="#80000000" android:shadowColor="#80000000"
android:shadowDx="1" android:shadowDx="2"
android:shadowDy="1" android:shadowDy="2"
android:shadowRadius="2" android:shadowRadius="4"
android:layout_marginBottom="8dp" /> android:layout_marginBottom="8dp" />
<TextView <TextView
@ -33,12 +37,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="202310715002" android:text="202310715002"
android:textSize="16sp" android:textSize="18sp"
android:textColor="#E0E0E0" android:textColor="#F0F0F0"
android:shadowColor="#80000000"
android:shadowDx="1"
android:shadowDy="1"
android:shadowRadius="2"
android:layout_marginBottom="8dp" /> android:layout_marginBottom="8dp" />
<TextView <TextView
@ -47,20 +47,18 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Pemrograman Perangkat Bergerak - F5A5" android:text="Pemrograman Perangkat Bergerak - F5A5"
android:textSize="16sp" android:textSize="16sp"
android:textColor="#E0E0E0" android:textColor="#EDEDED"
android:shadowColor="#80000000" android:layout_marginBottom="32dp" />
android:shadowDx="1"
android:shadowDy="1"
android:shadowRadius="2"
android:layout_marginBottom="24dp" />
<Button <Button
android:id="@+id/btnMulai" android:id="@+id/btnMulai"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="MULAI" android:text="MULAI"
android:backgroundTint="#4CAF50"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:paddingHorizontal="32dp" android:backgroundTint="@android:color/holo_green_dark"
android:paddingVertical="12dp" /> android:elevation="6dp"
android:paddingHorizontal="36dp"
android:paddingVertical="14dp"
android:fontFamily="sans-serif-medium" />
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Warna tema dasar -->
<color name="purple_200">#BB86FC</color>
<color name="purple_500">#6200EE</color>
<color name="purple_700">#3700B3</color>
<color name="teal_200">#03DAC5</color>
<color name="teal_700">#018786</color>
<color name="black">#000000</color>
<color name="white">#FFFFFF</color>
<!-- Gradasi untuk splash screen -->
<color name="gradient_start">#4CAF50</color> <!-- hijau terang -->
<color name="gradient_center">#81C784</color> <!-- hijau lembut -->
<color name="gradient_end">#C8E6C9</color> <!-- hijau muda -->
</resources>

View File

@ -4,8 +4,7 @@ import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
/** /**
* Unit test untuk memastikan fungsi perhitungan dan kategori BMI * Unit test untuk memastikan fungsi perhitungan dan kategori BMI berjalan dengan benar.
* berjalan dengan benar.
* *
* Lokasi file: app/src/test/java/com/example/tiptime/BmiUnitTest.kt * Lokasi file: app/src/test/java/com/example/tiptime/BmiUnitTest.kt
*/ */
@ -13,15 +12,15 @@ class BmiUnitTest {
@Test @Test
fun calculateBMI_isAccurate() { fun calculateBMI_isAccurate() {
// Data: berat 70 kg, tinggi 170 cm // Data: berat 70 kg, tinggi 170 cm (sistem metrik)
val result = calculateBMI(70f, 170f) val result = calculateBMI(70f, 170f, true)
// Hasil ekspektasi ≈ 24.22 // Hasil ekspektasi ≈ 24.22
assertEquals(24.22f, result, 0.01f) assertEquals(24.22f, result, 0.01f)
} }
@Test @Test
fun calculateBMI_zeroHeight_returnsZero() { fun calculateBMI_zeroHeight_returnsZero() {
val result = calculateBMI(70f, 0f) val result = calculateBMI(70f, 0f, true)
assertEquals(0f, result, 0.0f) assertEquals(0f, result, 0.0f)
} }
@ -51,13 +50,13 @@ class BmiUnitTest {
@Test @Test
fun validateInput_validValues_returnsTrue() { fun validateInput_validValues_returnsTrue() {
val isValid = validateInput(170f, 65f) val isValid = validateInput(170f, 65f, true)
assertEquals(true, isValid) assertEquals(true, isValid)
} }
@Test @Test
fun validateInput_invalidValues_returnsFalse() { fun validateInput_invalidValues_returnsFalse() {
val isValid = validateInput(5f, 500f) val isValid = validateInput(5f, 500f, true)
assertEquals(false, isValid) assertEquals(false, isValid)
} }
} }