Pertemuan 11

Fragment, Tablet UI &
Bottom Navigation 📱

Pernah sadar kalau buka Instagram, menu bawahnya tetap diam walau layarnya berganti? Itulah keajaiban Fragment. Mari kita bedah tuntas arsitektur ini dari cara Manual hingga cara Modern (Jetpack)!

1. Activity vs Fragment

Di modul sebelumnya, setiap kali pindah layar kita selalu berpindah ke Activity yang baru. Jika aplikasimu punya 10 halaman, berarti punya 10 Activity. Cara kuno ini sangat memakan Memori RAM dan membuat transisi layar kaku (ada jeda hitam sesaat).

Activity (Sang Bingkai)

Ibarat Bingkai Foto. Sifatnya kaku, memakan banyak ruang, dan menjadi fondasi utama. Fragment tidak akan bisa hidup jika tidak menempel pada Activity.

Fragment (Sang Foto)

Ibarat Foto di dalam bingkai. Sangat ringan dan dinamis. Kamu bisa mencabut dan mengganti foto tersebut berkali-kali tanpa perlu membuang bingkainya!

2. Kaitan dengan Activity Lifecycle

Mengingat kembali Modul 4, sebuah Activity memiliki Lifecycle (Siklus Hidup). Karena Fragment "menumpang" di dalam Activity, maka nasib Fragment sangat bergantung pada Activity-nya. Jika Activity mati (onDestroy), Fragment pasti ikut mati.

Namun, Fragment punya fase tambahan khusus untuk merakit "Wajah"-nya (UI/XML) yang independen. Perhatikan perbedaan utamanya:

Activity (Tuan Rumah)

onCreate()

Satu langkah! Layout XML langsung ditempel dan dicari ID-nya di sini pakai setContentView() dan findViewById().

onStart() & onResume()

Layar tampil dan siap disentuh user.

onDestroy()

Mati total. Memori dikosongkan.

Fragment (Sang Tamu)

onAttach() & onCreate()

Fragment menempel ke Activity dan lahir. TAPI UI-nya belum ada!

onCreateView()

PENTING: Di sinilah proses inflater.inflate terjadi. Mengubah XML menjadi View (tampilan).

onViewCreated()

View sudah siap. Baru di sini kamu boleh melakukan view.findViewById() untuk tombol dan teks!

onDestroyView()

User pindah tab. Tampilan Fragment dihancurkan agar hemat RAM, tapi instans Fragment-nya sendiri masih hidup!

onDestroy() & onDetach()

Activity mati, maka Fragment ikut mati selamanya.

3. Responsivitas Layar (Multi-pane UI)

Tujuan utama Google menciptakan Fragment adalah untuk mendukung layar besar seperti Tablet atau Smart TV. Dengan Fragment, kita bisa mendaur ulang bagian UI tanpa harus memprogram ulang dari nol!

Di HP (Handset)

Frag A
Frag B

Klik item di Fragment A ➔ Pindah mengganti seisi layar ke Detail.

Di Tablet (Multi-pane UI)

Frag A
Frag B

Klik item di Daftar ➔ Detail langsung update di sebelahnya (1 Layar)!

4. Bottom Navigation & Jetpack

Fungsi Fragment yang paling sering dipakai di industri saat ini adalah membuat Dashboard (Menu Bawah). Untuk membuatnya, ada perpindahan tren dari era Manual ke era Modern (Jetpack).

Era Manual (Legacy)

Programmer harus memprogram logika `switch-case` saat menu diklik, lalu menyuruh `FragmentManager` mencabut fragment lama dan mem-`beginTransaction().replace()` fragment baru ke `FrameLayout` kosong.

Era Jetpack Nav (Modern)

Google merilis Navigation Component. Cukup buat peta XML (`nav_graph`), lalu panggil 1 baris fungsi sakti NavigationUI.setupWithNavController(). Pergantian layar diurus 100% otomatis oleh sistem!

5. Mega Simulator (Device & Engine)

Uji coba aplikasi Cafe di bawah ini.
1. Mainkan menu Home, List, Profile.
2. Coba klik Kopi pada menu List.
3. Ubah tipe perangkat ke Tablet dan rasakan bedanya!
4. Ubah mode kode ke Jetpack untuk melihat bagaimana live code Java di kotak kanan berubah seketika!

Home Fragment

Halaman beranda statis.

Daftar Kopi

Espresso

Rp 20.000

Cappuccino

Rp 28.000

Detail Kopi

Pilih menu di samping.

Budi Santoso

Profile Fragment

Home
List
Profile

FragmentManager & Transaction

Di era manual, kita menggunakan if-else untuk menentukan Fragment mana yang akan dipasang ke dalam FrameLayout menggunakan FragmentManager.

Mode HP (Single Pane)

Layar sempit. Klik kopi pada menu List akan menumpuk layar secara penuh untuk menampilkan Detail.

MainActivity.java Navigasi Sukses
// MANUAL: Mengganti isi wadah FrameLayout (R.id.container)
getSupportFragmentManager()
    .beginTransaction()
    .replace(R.id.container, new HomeFragment())
    .commit();

6. Bedah Kode: Era Manual (Legacy)

Sebelum ada Jetpack, kita membuat Bottom Navigation dan Multi-pane Tablet menggunakan cara manual. Kita membuat folder layout-sw600dp untuk mendeteksi layar lebar (Tablet), dan memanggil FragmentManager saat menu diklik.

Rangka Pondasi Manual (Activity & Menu)

Kita membuat dua versi activity_main.xml. Di MainActivity.java kita melakukan deteksi manual apakah perangkat ini tablet atau bukan dengan cara mencari keberadaan detail_container.

Maksud "sw600dp": Ini adalah singkatan dari Smallest Width 600dp (Ukuran standar minimal lebar Tablet). Jika kita sengaja membuat folder bernama layout-sw600dp, Android secara otomatis dan cerdas akan memakai desain di dalam folder ini khusus jika aplikasinya dibuka di Tablet!

📄 1. res/menu/menu_bottom.xml
<menu 
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item 
        android:id="@+id/nav_home" 
        android:icon="@drawable/ic_home" 
        android:title="Beranda" />
        
    <item 
        android:id="@+id/nav_list" 
        android:icon="@drawable/ic_list" 
        android:title="Pesanan" />
        
    <item 
        android:id="@+id/nav_profile" 
        android:icon="@drawable/ic_person" 
        android:title="Profil" />
</menu>
📄 2. res/layout/activity_main.xml (Desain HP)
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent" 
    android:layout_height="match_parent">

    <!-- Di HP, cuma ada 1 wadah panggung utama -->
    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent" 
        android:layout_height="match_parent"
        android:layout_above="@id/bottom_nav" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent" 
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" 
        app:menu="@menu/menu_bottom" />
</RelativeLayout>
📄 3. res/layout-sw600dp/activity_main.xml (Desain Tablet)
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent" 
    android:layout_height="match_parent">

    <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="match_parent"
        android:orientation="horizontal" 
        android:layout_above="@id/bottom_nav"> 
        
        <!-- Wadah Utama Kiri (Porsi 1) -->
        <FrameLayout 
            android:id="@+id/fragment_container"
            android:layout_width="0dp" 
            android:layout_weight="1" 
            android:layout_height="match_parent" />
            
        <!-- Wadah Kanan Khusus Detail (Porsi 2, lebih lebar) -->
        <FrameLayout 
            android:id="@+id/detail_container"
            android:layout_width="0dp" 
            android:layout_weight="2" 
            android:layout_height="match_parent" />
    </LinearLayout>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent" 
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" 
        app:menu="@menu/menu_bottom" />
</RelativeLayout>
📄 4. MainActivity.java
package com.example.cafe;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import com.google.android.material.bottomnavigation.BottomNavigationView;

public class MainActivity extends AppCompatActivity {

    // Variabel global biar bisa dibaca dari mana saja
    public static boolean isTablet;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // LOGIKA DETEKSI TABLET: Cari wadah detail. Jika ADA, berarti Tablet!
        if (findViewById(R.id.detail_container) != null) {
            isTablet = true;
        } else {
            isTablet = false;
        }

        // LOGIKA BOTTOM NAVIGATION (KUNO)
        BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new HomeFragment()).commit();
        }

        bottomNav.setOnItemSelectedListener(item -> {
            Fragment selected = null;
            int id = item.getItemId();
            
            if (id == R.id.nav_home) selected = new HomeFragment();
            else if (id == R.id.nav_list) selected = new ListFragment();
            else if (id == R.id.nav_profile) selected = new ProfilFragment();

            if (selected != null) {
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, selected).commit();
                
                // (Jika pindah tab, kosongkan detail_container kalau di mode tablet)
                if(isTablet && id != R.id.nav_list) {
                    Fragment detail = getSupportFragmentManager().findFragmentById(R.id.detail_container);
                    if(detail != null) getSupportFragmentManager().beginTransaction().remove(detail).commit();
                }
                return true;
            }
            return false;
        });
    }
}

7. Bedah Kode 100%: Era Modern (Jetpack)

Ini dia bentuk akhir dari Arsitektur Modern kelas Enterprise! Dengan Jetpack Navigation Component, switch-case yang panjang dihilangkan menjadi 1 baris kode saja.

Di bawah ini adalah Semua File Lengkap yang kamu butuhkan untuk menyontek dan membangun simulasi di atas secara nyata di Android Studiomu tanpa error!

Rangka Pondasi (Gradle, Activity, NavGraph)

PENTING: Fitur Jetpack Navigation mewajibkan kita menginstall library tambahan di build.gradle. Setelah itu, pastikan ID di file menu_bottom.xml SAMA PERSIS dengan ID fragment di nav_graph.xml agar navigasinya otomatis!

📄 1. build.gradle.kts (Module :app)
// 🚨 TAMBAHKAN LIBRARY INI di dalam blok dependencies { ... }
def nav_version = "2.7.7"
implementation("androidx.navigation:navigation-fragment:$nav_version")
implementation("androidx.navigation:navigation-ui:$nav_version")
📄 2. res/menu/menu_bottom.xml
<menu 
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
        android:id="@+id/nav_home" 
        android:icon="@drawable/ic_home" 
        android:title="Beranda" />
        
    <item 
        android:id="@+id/nav_list" 
        android:icon="@drawable/ic_list" 
        android:title="Menu" />
        
    <item 
        android:id="@+id/nav_profile" 
        android:icon="@drawable/ic_person" 
        android:title="Profil" />
</menu>
📄 3. res/navigation/nav_graph.xml
<navigation 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph" 
    app:startDestination="@id/nav_home">

    <fragment 
        android:id="@+id/nav_home" 
        android:name="com.example.cafe.HomeFragment" />
        
    <fragment 
        android:id="@+id/nav_profile" 
        android:name="com.example.cafe.ProfilFragment" />
    
    <fragment 
        android:id="@+id/nav_list" 
        android:name="com.example.cafe.ListFragment">
        <!-- ANAK PANAH: List -> Detail -->
        <action 
            android:id="@+id/action_list_to_detail" 
            app:destination="@id/nav_detail" />
    </fragment>

    <fragment 
        android:id="@+id/nav_detail" 
        android:name="com.example.cafe.DetailFragment" />
</navigation>
📄 4. res/layout/activity_main.xml
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent" 
    android:layout_height="match_parent">

    <!-- WADAH PANGGUNG FRAGMENT -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent" 
        android:layout_height="match_parent"
        android:layout_above="@id/bottom_nav"
        app:defaultNavHost="true" 
        app:navGraph="@navigation/nav_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent" 
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" 
        android:background="?android:attr/windowBackground"
        app:menu="@menu/menu_bottom" />
</RelativeLayout>
📄 5. MainActivity.java
package com.example.cafe;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;
import com.google.android.material.bottomnavigation.BottomNavigationView;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);

        // Ajaib! Navigasi tab terurus otomatis 100%
        NavigationUI.setupWithNavController(bottomNav, navController);
    }
}

7. Tugas Super: Dashboard Aplikasi Utuh!

Tampak rumit karena banyak file? Tenang! Di dunia nyata kita membangunnya satu-persatu. Copy-paste kode XML & Java dari Bedah Kode Jetpack di atas secara berurutan sesuai namanya untuk membangun mahakarya arsitektur mutakhir!

Misi Puncak Modul 10:

  1. Buat project baru berarsitektur 1 MainActivity dan dibungkus BottomNavigationView. Gunakan fitur modern Jetpack Navigation. Pastikan kamu menambahkan library Jetpack di `build.gradle` terlebih dahulu!
  2. Buat 3 Pasang Fragment (Home, List, Profil). Coba berkreasi dengan menambahkan foto/warna di XML fragment-mu.
  3. Pastikan transisi dari List Kopi menuju Detail Kopi berjalan mulus dan datanya terbawa!
Peringatan Keras Context Fragment: Jika kalian menyalin Toast atau AlertDialog ke dalam fungsi Fragment, JANGAN gunakan kata kunci this atau MainActivity.this untuk memberikan Context. Gunakanlah perintah requireActivity() atau getContext() karena Fragment menumpang pada Activity!