Session 2 - Activity Lifecycle
Sesi 2: Activity Lifecycle
Mata Kuliah: Mobile Technology Solution (COSC6103001) Learning Outcome:
- LO2 — Describe the main features of Android Programming and Android Software Development
- LO3 — Produce simple Mobile Application using the main features of Android
Daftar Isi
- Understanding an Activity
- Intent — Cara Activity Saling Memanggil
- Activity Lifecycle
- Tujuh Method Lifecycle
- Studi Kasus: Music Player App
- Menangani Rotasi Layar
- Activity Monitoring: Tiga Lifetime
- Ejection from Memory
- Android Components (Material Design 3)
- Rangkuman Cepat
1. Understanding an Activity
Definisi
Activity merepresentasikan satu window atau satu halaman dalam aplikasi, berisi satu hierarki View.
Empat karakteristik utama:
| Karakteristik | Penjelasan |
|---|---|
| Satu window/page + satu hierarki view | Satu Activity = satu layar. Hierarki View-nya didefinisikan di file XML layout |
| Bisa fullscreen atau floating | Tidak selalu menutupi seluruh layar — dialog izin dan Picture-in-Picture adalah contoh Activity floating |
| Menangani interaksi user | Jembatan antara aksi user (klik, ketik) dengan respons yang ditampilkan |
| Bisa memanggil Activity lain | Baik dalam app yang sama maupun app lain (lewat Intent) |
Kaitan dengan project HelloBinus:
MainActivity.kt= satu Activityactivity_main.xml= hierarki View-nyabinding.buttonSapa.setOnClickListener { }= menangani interaksi userbinding.textViewGreeting.text = "..."= menampilkan respons
Konsep: Non-Deterministic User Journey
Non-deterministic = alurnya tidak bisa ditebak, bisa berbeda-beda tiap kali.
Contoh konkret:
Membuka app Email dari Home Screen → menampilkan inbox. Membuka app Email yang sama dari app Galeri (lewat tombol "Share") → langsung menampilkan halaman compose email baru, bukan inbox.
App yang sama bisa "dimasuki" dari pintu berbeda tergantung konteks.
Alur Logika Desain Android
MASALAH: User journey di mobile TIDAK bisa ditebak (non-deterministic)
↓
SOLUSI: Android memecah app menjadi Activity-Activity yang INDEPENDEN
↓
EFEK: Setiap Activity berpotensi menjadi entry point
↓
HASIL: Activity bisa dipanggil dari Activity/app manapun (lewat Intent)
2. Intent — Cara Activity Saling Memanggil
Intent adalah "pesan permintaan" yang dikirim ke sistem Android untuk meminta suatu aksi dilakukan.
Dua Jenis Intent
Explicit Intent — tahu persis tujuannya
Dipakai untuk berpindah antar Activity dalam app yang sama.
val intent = Intent(this, HomeActivity::class.java)
startActivity(intent)
Analogi: mengirim surat dengan alamat lengkap — sistem tidak perlu mencari.
Implicit Intent — hanya menyebut "aksi apa"
Dipakai untuk meminta aksi tanpa tahu app spesifik mana yang akan menjalankannya.
// Buka kamera — sistem yang cari app kamera mana yang tersedia
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivity(intent)
// Buka halaman web
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://binus.ac.id"))
startActivity(intent)
// Bagikan teks ke app lain (WhatsApp, Email, dll)
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, "Halo dari app saya!")
}
startActivity(intent)
Analogi: "Tolong carikan siapa saja yang bisa fotokopi dokumen ini" — tidak peduli siapa, yang penting bisa mengerjakan.
Mengirim Data lewat Intent
// Activity pengirim
val intent = Intent(this, HomeActivity::class.java)
intent.putExtra("nama_user", "Budi")
startActivity(intent)
// Activity penerima
val nama = intent.getStringExtra("nama_user")
Topik ini akan dibahas mendalam di Topik 3 — Multiple Activities.
Membatasi Urutan Pemanggilan Activity
Pertanyaan umum: bisakah Activity A dibatasi hanya boleh dibuka setelah Activity B?
Jawaban: Android tidak menyediakan mekanisme bawaan untuk ini — sesuai filosofi non-deterministic. Developer harus menegakkannya sendiri lewat kode.
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val prefs = getSharedPreferences("app_prefs", MODE_PRIVATE)
val isLoggedIn = prefs.getBoolean("is_logged_in", false)
if (!isLoggedIn) {
startActivity(Intent(this, LoginActivity::class.java))
finish() // tutup Activity ini agar tidak bisa di-back
return
}
setContentView(R.layout.activity_home)
// ...lanjut kode normal...
}
}
Jangan Tertukar: android:exported
| Mekanisme | Mencegah apa |
|---|---|
android:exported="false" di Manifest |
App lain memanggil Activity ini dari luar |
Pengecekan kondisi di onCreate() |
Activity dipanggil tanpa melalui alur yang benar (dari dalam maupun luar) |
Sejak Android 12,
android:exportedwajib dideklarasikan eksplisit untuk Activity yang punya<intent-filter>— kalau tidak, app gagal di-compile.
3. Activity Lifecycle
Activity Stack (Back Stack)
Activity dikelola sistem sebagai stack dengan prinsip LIFO (Last In, First Out) — seperti tumpukan piring.
Buka MainActivity → Stack: [MainActivity]
Buka DetailActivity → Stack: [MainActivity, DetailActivity] ← terlihat
Buka SettingsActivity → Stack: [MainActivity, DetailActivity, SettingsActivity] ← terlihat
Tekan BACK → SettingsActivity di-pop
→ Stack: [MainActivity, DetailActivity] ← DetailActivity terlihat lagi
Setiap startActivity(intent) = push Activity baru ke atas stack.
Bisa ada lebih dari satu stack di layar sekaligus — contohnya saat split-screen (fitur resmi sejak Android 7.0), di mana dua app berdampingan masing-masing punya stack independen.
Empat State Activity
| Istilah di slide | Istilah resmi Android | Kondisi |
|---|---|---|
| Active / Running | Resumed | Di foreground, menerima interaksi user |
| Paused | Paused | Masih terlihat, tapi kehilangan fokus |
| Stopped | Stopped | Tidak terlihat sama sekali |
| — | Destroyed | Sudah dihancurkan |
Perbedaan krusial Paused vs Stopped:
- Paused = masih terlihat sebagian. Contoh: dialog muncul di atas Activity, atau split-screen dengan fokus di app sebelah
- Stopped = tertutup total. Contoh: tekan tombol Home, atau Activity lain menutupi penuh
Penting: Baik Paused maupun Stopped, sistem berhak membunuh proses kapan saja tanpa pemberitahuan kalau butuh RAM.
Diagram Lifecycle Resmi
┌─────────────┐
┌──────►│ Resumed │◄──────┐
│ │ (visible) │ │ onResume()
onResume() │ └──────┬──────┘ │
│ │ onPause() │
┌──────┴──────┐ ▼ ┌──────┴────────────┐
│ Started │ │ Paused │
│ (visible) │ │ (partially visible) │
└──────▲──────┘ └──────┬───────────────┘
│ onStart() │ onStop()
│ ▼
┌──────┴──────┐ onRestart() ┌──────────────┐
│ Created │◄─────────────│ Stopped │
└──────▲──────┘ │ (hidden) │
│ onCreate() └──────┬───────┘
│ │ onDestroy()
[App diluncurkan] ▼
┌──────────────┐
│ Destroyed │ ← tidak ada jalan kembali
└──────────────┘
Pelajaran Utama dari Diagram
Jumlah method yang dipanggil saat "kembali aktif" berbeda drastis tergantung seberapa parah gangguannya:
| Skenario | Jalur | Method yang dipanggil |
|---|---|---|
| Kembali dari Paused (gangguan ringan) | Paused → Resumed | 1 method: onResume() |
| Kembali dari Stopped (gangguan berat) | Stopped → Created → Started → Resumed | 3 method: onRestart() → onStart() → onResume() |
Catatan tentang onRestart()
onRestart() bukan pengganti onCreate(). Dia hanya "jembatan" yang dipanggil sebelum onStart(), khusus untuk Activity yang sebelumnya sempat Stopped.
Analogi:
onCreate()= menyalakan TV dari kondisi mati total (kabel dicabut)onRestart()= menyalakan TV dari mode standby
4. Tujuh Method Lifecycle
Tabel Lengkap
| Method | Kapan dipanggil | Contoh penggunaan |
|---|---|---|
onCreate() |
Activity dibuat | Inflate layout, setup View Binding, pasang listener |
onStart() |
Activity mulai terlihat | Mulai animasi, daftarkan BroadcastReceiver |
onResume() |
Activity mulai berinteraksi dengan user | Mulai GPS, buka kamera, putar musik |
onPause() |
Activity kehilangan fokus | Hentikan GPS, jeda musik, simpan data |
onStop() |
Activity tidak terlihat lagi | Simpan ke database, tutup koneksi |
onDestroy() |
Activity dihancurkan | Lepas resource berat (release()) |
onRestart() |
Activity bangkit dari Stopped | Jarang diisi kode khusus |
Pola Pasangan "Buka–Tutup"
Ini cara paling efektif memahami lifecycle — jangan hafal 7 item terpisah, tapi ingat sebagai 3 pasang + 1 jembatan:
onCreate() ←────────────────→ onDestroy() (Entire Lifetime)
onStart() ←────────────→ onStop() (Visible Lifetime)
onResume() ←──────→ onPause() (Foreground Lifetime)
onRestart() (jembatan, berdiri sendiri)
Prinsip: apapun yang "dinyalakan" di satu method, harus "dimatikan" di method pasangannya.
Contoh Implementasi Lengkap
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
Log.d("LifecycleDemo", "onCreate() dipanggil")
}
override fun onStart() {
super.onStart()
Log.d("LifecycleDemo", "onStart() dipanggil")
}
override fun onResume() {
super.onResume()
Log.d("LifecycleDemo", "onResume() dipanggil")
}
override fun onPause() {
super.onPause()
Log.d("LifecycleDemo", "onPause() dipanggil")
}
override fun onStop() {
super.onStop()
Log.d("LifecycleDemo", "onStop() dipanggil")
}
override fun onRestart() {
super.onRestart()
Log.d("LifecycleDemo", "onRestart() dipanggil")
}
override fun onDestroy() {
super.onDestroy()
Log.d("LifecycleDemo", "onDestroy() dipanggil")
}
}
Jangan lupa import:
import android.util.Log
Aturan Penting per Method
onPause() harus CEPAT. Activity berikutnya (yang baru dibuka) menunggu onPause() selesai sebelum bisa masuk onResume(). Kalau isinya proses berat, transisi akan terasa lag.
onDestroy() TIDAK terjamin dipanggil. Kalau sistem membunuh proses paksa karena kekurangan RAM, onDestroy() bisa dilewati sepenuhnya.
Tabel Keandalan
| Method | Seberapa terjamin dipanggil? |
|---|---|
onCreate(), onStart(), onResume() |
Selalu, dalam alur normal |
onPause() |
Sangat terjamin |
onStop() |
Cukup terjamin |
onDestroy() |
TIDAK terjamin — bisa dilewati saat kill paksa |
Aturan emas: simpan data penting di
onPause()atauonStop(), jangan menungguonDestroy().
5. Studi Kasus: Music Player App
Versi Awal (Bermasalah)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val musicUrl = "https://...../music.mp3"
mediaPlayer = MediaPlayer().apply {
setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build()
)
setDataSource(musicUrl)
prepare()
start()
}
}
Penjelasan kode:
| Bagian | Fungsi |
|---|---|
MediaPlayer() |
Class bawaan Android untuk memutar audio/video |
.apply { } |
Scope function Kotlin — this merujuk ke objek MediaPlayer, jadi tidak perlu mengulang nama variabel |
AudioAttributes.Builder() |
Pola Builder untuk konfigurasi: CONTENT_TYPE_MUSIC (ini musik, bukan notifikasi), USAGE_MEDIA (volume media) |
setDataSource → prepare → start |
Alur baku MediaPlayer: tentukan sumber, siapkan, putar |
Empat Masalah pada Versi Awal
1. prepare() untuk sumber internet — berisiko ANR
prepare() bersifat blocking — menahan UI thread sampai buffering selesai. Untuk URL internet dengan koneksi lambat, ini bisa memicu ANR (Application Not Responding).
Solusi:
mediaPlayer.prepareAsync() // non-blocking
mediaPlayer.setOnPreparedListener { it.start() } // start setelah siap
2. Tidak ada release() — resource leak
MediaPlayer memakai buffer native dan koneksi jaringan. Tanpa release(), resource tetap menggantung di memori.
3. Musik restart setiap onCreate() dipanggil ulang
Karena start() ada di dalam onCreate(), setiap Activity dibuat ulang (misal karena rotasi), musik mengulang dari awal.
4. Tidak menangani perpindahan app
Tidak ada onPause()/onResume() — musik terus main (atau berhenti mendadak kalau proses di-kill) tanpa kontrol.
Versi Perbaikan
override fun onResume() {
super.onResume()
mediaPlayer?.start()
}
override fun onPause() {
super.onPause()
mediaPlayer?.pause()
}
override fun onDestroy() {
super.onDestroy()
mediaPlayer?.release()
}
Mengapa desain ini tepat:
| Keputusan | Alasan |
|---|---|
Pakai onResume()/onPause(), bukan onStart()/onStop() |
Musik wajar dijeda begitu user kehilangan fokus sedikit saja |
Pakai pause(), bukan stop() atau release() |
pause() cepat (sesuai aturan onPause()) dan posisi lagu tersimpan |
Pakai release() di onDestroy() |
Pasangan dari pembuatan objek di onCreate() |
Operator ?. (safe call) |
Mencegah crash kalau method lifecycle terpanggil sebelum mediaPlayer diinisialisasi |
Alur Cerita Lengkap App Ini
1. User buka app
→ onCreate() : layout disiapkan, musik disiapkan dan mulai main
→ onStart() → onResume() : mediaPlayer.start() (musik sudah main, ini memastikan saja)
2. User dengarkan musik
→ State: Resumed. Musik main normal.
3. User tekan Home / pindah app
→ onPause() : mediaPlayer.pause() → musik berhenti, posisi TERSIMPAN (misal detik 45)
→ onStop() : Activity tidak terlihat
4A. User kembali ke app (proses masih hidup)
→ onRestart() → onStart() → onResume() : mediaPlayer.start()
→ Musik LANJUT dari detik 45
4B. Sistem membunuh proses (butuh RAM) sebelum user kembali
→ Semua objek di memori HILANG, termasuk mediaPlayer dan posisi lagunya
→ Saat app dibuka lagi: onCreate() jalan dari nol
→ Musik MULAI ULANG dari detik 0
5. User menutup app sepenuhnya
→ onPause() → onStop() → onDestroy() : mediaPlayer.release()
→ Semua resource dilepas bersih
Keterbatasan yang Masih Ada
Perbaikan di atas belum menyelesaikan masalah rotasi layar — karena mediaPlayer masih dibuat dari nol di onCreate().
Untuk musik yang benar-benar independen dari lifecycle Activity (seperti Spotify yang tetap main saat app ditutup), solusi sesungguhnya adalah Service — dibahas di Topik 9.
6. Menangani Rotasi Layar
Mengapa Musik Restart Saat Rotasi?
Rotasi layar memicu Activity benar-benar di-destroy lalu dibuat ulang (onDestroy() → onCreate()), karena rotasi dianggap perubahan konfigurasi. Objek mediaPlayer yang lama ikut hilang.
Solusi 1: Cegah Recreate (Sederhana)
AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:exported="true">
...
</activity>
Dengan ini, onCreate() tidak dipanggil ulang saat rotasi — musik lanjut mulus tanpa putus.
Kalau perlu tahu kapan rotasi terjadi:
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// sesuaikan tampilan manual di sini kalau perlu
}
Trade-off: Google tidak merekomendasikan ini sebagai solusi umum, karena kamu jadi harus urus sendiri penyesuaian layout tiap orientasi. Tapi untuk media player, ini solusi yang wajar dan umum dipakai di industri.
Solusi 2: Simpan Posisi (Lebih Formal)
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("posisi_musik", mediaPlayer?.currentPosition ?: 0)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mediaPlayer = MediaPlayer().apply {
setDataSource(musicUrl)
prepare()
val posisiTersimpan = savedInstanceState?.getInt("posisi_musik") ?: 0
seekTo(posisiTersimpan)
start()
}
}
Cara kerja: onSaveInstanceState() dipanggil sistem sebelum Activity dihancurkan. Kita "titipkan" posisi lagu ke Bundle. Saat onCreate() jalan lagi, savedInstanceState berisi titipan itu.
Kelemahan: musik tetap putus sepersekian detik karena Activity memang dibuat ulang.
Perbandingan Dua Solusi
Solusi 1 (configChanges) |
Solusi 2 (onSaveInstanceState) |
|
|---|---|---|
| Kompleksitas | Rendah | Sedang (perlu paham Bundle) |
| Musik putus? | Tidak | Ya, sedikit |
| Sesuai rekomendasi Google? | Kurang, untuk kasus umum | Ya, pola resmi |
| Cocok untuk sesi pengantar? | ✅ | Materi pengayaan |
Layout Berbeda untuk Portrait dan Landscape
Kalau app perlu tampilan berbeda tiap orientasi, cukup buat folder terpisah:
res/
├── layout/ ← PORTRAIT (default)
│ └── activity_main.xml
└── layout-land/ ← LANDSCAPE
└── activity_main.xml
Cara membuat di Android Studio: klik kanan res → New → Android Resource Directory → Resource type: layout → qualifier: Orientation → Landscape.
Aturan penting: nama file dan nama id harus sama persis di kedua folder.
<!-- res/layout/activity_main.xml -->
<Button android:id="@+id/buttonSapa" ... />
<!-- res/layout-land/activity_main.xml -->
<Button android:id="@+id/buttonSapa" ... /> ← id SAMA, posisi/susunan boleh beda
Kode Kotlin tidak perlu diubah — setContentView(R.layout.activity_main) otomatis memilih file yang sesuai orientasi.
Konflik: configChanges vs Layout Otomatis
| Kebutuhan | Solusi | Efek samping |
|---|---|---|
| Musik tidak putus saat rotasi | android:configChanges |
Layout tidak otomatis berganti |
| Layout otomatis berganti | Biarkan default (destroy-recreate) | Musik restart dari 0 |
Solusi gabungan — pakai configChanges tapi ganti layout manual:
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
// Android tetap otomatis ambil dari layout-land kalau orientasi landscape
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// PENTING: pasang ulang listener, karena View lama sudah diganti
binding.buttonSapa.setOnClickListener {
mediaPlayer?.let { if (it.isPlaying) it.pause() else it.start() }
}
}
mediaPlayer tetap aman karena dia objek terpisah, bukan bagian dari View.
7. Activity Monitoring: Tiga Lifetime
Hubungan Bersarang (Nested)
Entire Lifetime [onCreate() ................................ onDestroy()]
Visible Lifetime [onStart()...onStop()] [onStart()...onStop()]
Foreground Lifetime [onResume()..onPause()] [onResume()..onPause()]
Foreground selalu di dalam Visible, dan Visible selalu di dalam Entire.
Tabel Perbandingan
| Lifetime | Rentang | Frekuensi | Aturan isi kode |
|---|---|---|---|
| Entire | onCreate() – onDestroy() |
1 kali per generasi Activity | Setup/bongkar resource besar (boleh agak berat) |
| Visible | onStart() – onStop() |
Beberapa kali | Resource yang perlu aktif selama terlihat |
| Foreground | onResume() – onPause() |
Paling sering, bisa berkali-kali dalam detik | HARUS ringan dan cepat |
Prinsip Umum
Semakin ke dalam (Entire → Visible → Foreground), semakin sering method itu dipanggil, jadi semakin ringan kode di dalamnya harus dibuat.
Panduan Memilih Lifetime
| Kalau resource ini... | Setup di | Bersihkan di |
|---|---|---|
| Perlu ada sekali selama Activity hidup (koneksi database, View Binding) | onCreate() |
onDestroy() |
| Perlu aktif selama terlihat, walau tidak fokus (animasi background) | onStart() |
onStop() |
| Hanya boleh aktif saat benar-benar dipakai (musik, GPS, kamera) | onResume() |
onPause() |
Contoh Perbandingan Nyata
Animasi awan di app cuaca → cocok di Visible Lifetime:
override fun onStart() {
super.onStart()
animasiAwan.mulai() // boleh jalan walau tidak fokus penuh
}
override fun onStop() {
super.onStop()
animasiAwan.berhenti() // berhenti saat tidak terlihat sama sekali
}
Musik → cocok di Foreground Lifetime (seperti studi kasus kita), karena wajar berhenti begitu user teralihkan sedikit saja.
Contoh Kode Ringan vs Berat
✅ RINGAN — cocok untuk onResume()/onPause():
override fun onResume() {
super.onResume()
mediaPlayer?.start() // operasi cepat
}
❌ BERAT — hindari di onResume()/onPause():
override fun onResume() {
super.onResume()
val semuaData = database.query("SELECT * FROM riwayat_besar") // lambat
mediaPlayer = MediaPlayer().apply { setDataSource(url); prepare(); start() } // bikin objek baru terus
}
8. Ejection from Memory
Rantai Sebab-Akibat
Sistem butuh RAM
↓
Sistem harus MEMBUNUH beberapa proses
↓
Kemungkinan proses X dibunuh, tergantung STATE PROSES itu
↓
State proses itu sendiri, tergantung STATE ACTIVITY di dalamnya
Tabel Prioritas Kill
| Likelihood of being killed | Process state | Final activity state |
|---|---|---|
| Lowest | Foreground (focus) | Resumed |
| Low | Visible (no focus) | Started / Paused |
| Higher | Background (invisible) | Stopped |
| Highest | Empty | Destroyed |
Catatan Penting
"Lowest" ≠ "Never". Google memakai kata likelihood (kemungkinan), bukan jaminan mutlak. Bahkan Activity Resumed tetap bisa hilang dalam skenario:
| Skenario | Penjelasan |
|---|---|
| Tekanan memori ekstrem | HP RAM terbatas + banyak app berat berjalan |
| User force-stop manual | Settings → Apps → Force Stop |
| App crash | Unhandled exception (misal NullPointerException) |
| HP restart / baterai habis | Semua proses mati bersamaan |
| Perintah developer | adb shell am force-stop |
Satu proses bisa punya banyak Activity. State proses mengikuti Activity paling penting di dalamnya — kalau ada satu saja Activity yang Resumed, seluruh proses dianggap foreground (aman).
Method vs State
Perlu dibedakan:
onCreate(),onStart(),onResume()= method, titik transisi sesaat- Created, Started, Resumed = state, kondisi yang bertahan
onCreate() dipanggil → sesaat di state "Created" → langsung lanjut ke onStart()
onStart() dipanggil → sesaat di state "Started" → langsung lanjut ke onResume()
onResume() dipanggil → masuk state "Resumed" → BERTAHAN sampai ada gangguan
Tabel prioritas kill mengacu ke state yang sedang bertahan, bukan method-nya.
9. Android Components (Material Design 3)
Enam Kategori
Components adalah building block interaktif untuk membuat user interface, dikelompokkan berdasarkan tujuan:
| Kategori | Definisi | Contoh komponen |
|---|---|---|
| Action | Membantu user mencapai suatu tujuan | Button, FAB, Icon Button, Segmented Button |
| Containment | Menampung informasi dan aksi lain | Card, Dialog, List |
| Communication | Menyediakan informasi berguna | Snackbar, Badge, Progress indicator |
| Navigation | Membantu user berpindah di dalam UI | Bottom Nav, Tabs, Navigation Drawer |
| Selection | Membiarkan user menentukan pilihan | Checkbox, Radio Button, Switch, Chip |
| Text Input | User memasukkan teks | Text Field |
Status di Project HelloBinus
| Kategori | Sudah dipraktikkan? | Komponen |
|---|---|---|
| Action | ✅ | MaterialButton |
| Containment | ✅ | MaterialCardView |
| Communication | ⚠️ Sebagian | error message di TextInputLayout |
| Navigation | ❌ Belum | — |
| Selection | ❌ Belum | — |
| Text Input | ✅ | TextInputLayout |
Action Components — Empat Varian
1. Common Button — sudah dipakai di HelloBinus
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonSapa"
android:text="@string/btn_sapa" />
Tiga level penekanan visual:
| Level | Tampilan | Kapan dipakai |
|---|---|---|
| Filled | Solid berwarna penuh | Aksi paling penting (contoh: "Sapa Saya") |
| Outlined | Hanya garis tepi | Aksi sekunder (contoh: "Reset") |
| Text | Tanpa background/border | Aksi paling ringan ("Batal") |
<!-- Outlined -->
<com.google.android.material.button.MaterialButton
style="?attr/materialButtonOutlinedStyle"
android:id="@+id/buttonReset"
android:text="@string/btn_reset" />
2. Floating Action Button (FAB) — tombol bulat mengambang untuk satu aksi paling penting di layar
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_menu_edit"
app:layout_gravity="bottom|end" />
Contoh nyata: FAB "+" di Gmail untuk compose email baru.
3. Icon Button — kecil, hanya ikon, untuk aksi yang simbolnya universal (gear = settings)
4. Segmented Button — beberapa tombol menyatu, untuk switch tampilan
<com.google.android.material.button.MaterialButtonToggleGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:singleSelection="true">
<com.google.android.material.button.MaterialButton android:text="Songs" />
<com.google.android.material.button.MaterialButton android:text="Albums" />
<com.google.android.material.button.MaterialButton android:text="Podcasts" />
</com.google.android.material.button.MaterialButtonToggleGroup>
Catatan klasifikasi: Segmented Button masuk Action, bukan Selection — karena mengkliknya memicu aksi (mengganti konten yang ditampilkan), bukan sekadar menyimpan pilihan.
Containment Components — Tiga Contoh
1. Card — sudah dipakai di HelloBinus
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="6dp"
app:cardBackgroundColor="@color/white">
<!-- TextView, EditText, Button ditampung di sini -->
</com.google.android.material.card.MaterialCardView>
2. Dialog — contoh nyata Activity/komponen yang floating (tidak fullscreen)
binding.buttonReset.setOnClickListener {
MaterialAlertDialogBuilder(this)
.setTitle("Konfirmasi Reset")
.setMessage("Yakin mau reset sapaan?")
.setPositiveButton("Ya") { _, _ ->
// logic reset
}
.setNegativeButton("Batal", null)
.show()
}
Dialog bisa diimplementasikan dua cara:
| Cara | Kapan dipakai |
|---|---|
AlertDialog (komponen biasa) |
Paling umum — konfirmasi sederhana |
| Activity dengan tema Dialog | Kasus kompleks yang butuh lifecycle sendiri |
3. List — item berulang dengan struktur sama tapi data berbeda
List membutuhkan
RecyclerView+Adapterkarena datanya dinamis — dibahas mendalam di Topik 5 — RecyclerView and Adapter.
10. Rangkuman
Tujuh Method
onCreate() → Activity dibuat (sekali saja)
onStart() → mulai terlihat (bisa berkali-kali)
onResume() → mulai berinteraksi (paling sering)
onPause() → kehilangan fokus (harus CEPAT)
onStop() → tidak terlihat lagi
onRestart() → bangkit dari Stopped (jembatan)
onDestroy() → dihancurkan (TIDAK terjamin dipanggil)
Empat State
Resumed → terlihat + fokus → paling AMAN dari kill
Paused → terlihat, tanpa fokus → agak berisiko
Stopped → tidak terlihat → berisiko tinggi
Destroyed → sudah hancur → tidak ada jalan kembali
Tiga Lifetime
Entire [onCreate ──────────────────── onDestroy] 1x, kode boleh berat
Visible [onStart ───── onStop] beberapa kali
Foreground [onResume ─ onPause] paling sering, HARUS ringan
Referensi
- Forrester, A., Boudjnah, E., & Dumbravan, A. (2025). How to Build Android Apps with Kotlin: A hands-on guide to developing, testing, and publishing production-grade Android 16 apps. Packt Publishing. Chapter 1–2.
- https://developer.android.com/guide/components/activities/activity-lifecycle
- https://developer.android.com/guide/components/activities/intro-activities
- https://developer.android.com/codelabs/basic-android-kotlin-compose-activity-lifecycle
- https://m3.material.io/components/text-fields/overview