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:
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.
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)
Klik item di Fragment A ➔ Pindah mengganti seisi layar ke Detail.
Di Tablet (Multi-pane UI)
Klik item di Daftar ➔ Detail langsung update di sebelahnya (1 Layar)!
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.
Budi Santoso
Profile Fragment
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.
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!
<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>
<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>
<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>
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;
});
}
}
Layar Statis (Home & Profil)
Setiap fragment wajib terdiri dari
pasangan XML dan Java. Gunakan inflater.inflate untuk memunculkan XML ke
layar.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_home" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Beranda Cafe"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
package com.example.cafe;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
public class HomeFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home, container, false);
}
}
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Profil User"
android:textSize="24sp"/>
</LinearLayout>
package com.example.cafe;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
public class ProfilFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_profil, container, false);
}
}
Layar Daftar (RecyclerView)
Menampilkan list menu Kopi. Perhatikan
pada bagian `onItemClick`, kita menggunakan if(MainActivity.isTablet) untuk
membedakan apakah harus membuka Activity baru dengan `Intent` atau sekadar menimpa kotak
kanan dengan `FragmentTransaction`!
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Daftar Menu Kopi"
android:textSize="20sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvKopi"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp" />
</LinearLayout>
package com.example.cafe;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class ListFragment extends Fragment implements KopiAdapter.OnItemClickListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
RecyclerView rvKopi = view.findViewById(R.id.rvKopi);
rvKopi.setLayoutManager(new LinearLayoutManager(requireContext()));
// Siapkan Data Dummy
ArrayList<Kopi> list = new ArrayList<>();
list.add(new Kopi("Espresso", "Kopi super pekat", "Rp 20.000"));
list.add(new Kopi("Cappuccino", "Espresso dengan foam susu", "Rp 28.000"));
// Lempar ke Adapter & set listener klik (this)
KopiAdapter adapter = new KopiAdapter(list, this);
rvKopi.setAdapter(adapter);
return view;
}
// Dijalankan saat item di Adapter ditekan user
@Override
public void onItemClick(Kopi itemKopi) {
if (MainActivity.isTablet) {
// TABLET MODE: Layar lebar! Ganti isi kotak kanan (detail_container)
DetailFragment fragDetail = new DetailFragment();
// Titipkan data kopi ke dalam Bundle Fragment
Bundle bundle = new Bundle();
bundle.putSerializable("DATA_KOPI", itemKopi);
fragDetail.setArguments(bundle);
requireActivity().getSupportFragmentManager().beginTransaction()
.replace(R.id.detail_container, fragDetail)
.commit();
} else {
// PHONE MODE: Layar sempit! Harus buka layar penutup baru pakai Intent.
Intent intent = new Intent(requireActivity(), DetailActivity.class);
intent.putExtra("DATA_KOPI", itemKopi);
startActivity(intent);
}
}
}
Layar Detail & Wrapper Activity
Daripada kita bikin desain UI dua kali,
kita cukup bikin DetailActivity kosong yang tugasnya hanya menampung
DetailFragment saat dibuka di layar HP!
<!-- Bungkus kosong penampung Detail Fragment untuk mode HP -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/detail_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent" />
package com.example.cafe;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// XML ini isinya cuma <FrameLayout android:id="@+id/detail_wrapper" ... />
setContentView(R.layout.activity_detail_wrapper);
if (savedInstanceState == null) {
// Kita daur ulang DetailFragment yang sama dengan Tablet!
DetailFragment detailFrag = new DetailFragment();
// Serahkan kembali data Kopi dari Intent ke dalam Bundle Fragment
detailFrag.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction()
.replace(R.id.detail_wrapper, detailFrag)
.commit();
}
}
}
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="24dp">
<TextView
android:id="@+id/tvDetNama"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="28sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvDetHarga"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#ec4899"
android:layout_marginVertical="16dp" />
<TextView
android:id="@+id/tvDetDesc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center" />
</LinearLayout>
package com.example.cafe;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
public class DetailFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_detail, container, false);
// PENTING! Bedanya Activity dan Fragment ada di sini!
// Kamu wajib pakai view.findViewById() karena view-nya baru diciptakan di atas
TextView tvNama = view.findViewById(R.id.tvDetNama);
TextView tvHarga = view.findViewById(R.id.tvDetHarga);
TextView tvDesc = view.findViewById(R.id.tvDetDesc);
// Tangkap tas (Bundle Arguments) yang dikirim oleh Fragment/Activity sebelumnya
if (getArguments() != null) {
Kopi kopiMasuk = (Kopi) getArguments().getSerializable("DATA_KOPI");
if(kopiMasuk != null) {
tvNama.setText(kopiMasuk.getNama());
tvHarga.setText(kopiMasuk.getHarga());
tvDesc.setText(kopiMasuk.getDesc());
}
}
return view;
}
}
Area RecyclerView
Bagian ini 100% sama dengan Modul 8.
Jangan lupa tambahkan Interface OnItemClickListener pada Adapter agar
sinyal klik bisa ditangkap oleh Fragment!
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:layout_marginBottom="8dp"
android:background="?attr/selectableItemBackground">
<TextView
android:id="@+id/tvRowNama"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold" />
<TextView
android:id="@+id/tvRowHarga"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#6366f1" />
</LinearLayout>
package com.example.cafe;
import java.io.Serializable;
// Serializable agar objek ini bisa dikirim lewat Intent/Bundle
public class Kopi implements Serializable {
private String nama, desc, harga;
public Kopi(String nama, String desc, String harga) { this.nama=nama; this.desc=desc; this.harga=harga; }
public String getNama() { return nama; }
public String getDesc() { return desc; }
public String getHarga() { return harga; }
}
package com.example.cafe;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class KopiAdapter extends RecyclerView.Adapter<KopiAdapter.KopiViewHolder> {
private ArrayList<Kopi> listKopi;
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(Kopi item);
}
public KopiAdapter(ArrayList<Kopi> list, OnItemClickListener listener) {
this.listKopi = list; this.listener = listener;
}
@NonNull
@Override
public KopiViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_kopi, parent, false);
return new KopiViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull KopiViewHolder holder, int position) {
Kopi item = listKopi.get(position);
holder.tvNama.setText(item.getNama());
holder.tvHarga.setText(item.getHarga());
// Sinyal klik ke Fragment
holder.itemView.setOnClickListener(v -> listener.onItemClick(item));
}
@Override
public int getItemCount() { return listKopi.size(); }
class KopiViewHolder extends RecyclerView.ViewHolder {
TextView tvNama, tvHarga;
public KopiViewHolder(@NonNull View itemView) {
super(itemView);
tvNama = itemView.findViewById(R.id.tvRowNama);
tvHarga = itemView.findViewById(R.id.tvRowHarga);
}
}
}
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!
// 🚨 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")
<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>
<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>
<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>
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);
}
}
Layar Statis (Home & Profil)
Setiap fragment wajib terdiri dari
pasangan XML dan Java. Gunakan inflater.inflate untuk memunculkan XML ke
layar.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_home" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Beranda Cafe"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
package com.example.cafe;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
public class HomeFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_home, container, false);
}
}
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Profil User"
android:textSize="24sp"/>
</LinearLayout>
package com.example.cafe;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
public class ProfilFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_profil, container, false);
}
}
Layar Daftar (RecyclerView)
Menampilkan list menu Kopi. Perhatikan
pada bagian `onItemClick`, kita menggunakan Bundle untuk membungkus objek Kopi
(yang sudah `Serializable`), lalu menyerahkannya ke NavController agar
meluncur ke layar Detail!
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Daftar Menu Kopi"
android:textSize="20sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvKopi"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp" />
</LinearLayout>
package com.example.cafe;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class ListFragment extends Fragment implements KopiAdapter.OnItemClickListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_list, container, false);
// PENTING! Bedanya Activity dan Fragment ada di sini!
// Kamu wajib pakai view.findViewById() karena view-nya baru diciptakan di atas
RecyclerView rvKopi = view.findViewById(R.id.rvKopi);
// Di Fragment, kita TIDAK BOLEH pakai "this" untuk Context. Pakailah "requireContext()" atau "getContext()"
rvKopi.setLayoutManager(new LinearLayoutManager(requireContext()));
// Siapkan Data Dummy
ArrayList<Kopi> list = new ArrayList<>();
list.add(new Kopi("Espresso", "Kopi super pekat", "Rp 20.000"));
list.add(new Kopi("Cappuccino", "Espresso dengan foam susu", "Rp 28.000"));
// Lempar ke Adapter & set listener klik (this)
KopiAdapter adapter = new KopiAdapter(list, this);
rvKopi.setAdapter(adapter);
return view;
}
// Dijalankan saat item di Adapter ditekan user
@Override
public void onItemClick(Kopi itemKopi) {
// 1. Bungkus data kopi ke dalam tas (Bundle)
Bundle tas = new Bundle();
tas.putSerializable("DATA_KOPI", itemKopi);
// 2. Perintahkan NavController menuju layar Detail sambil bawa tasnya!
Navigation.findNavController(getView()).navigate(R.id.action_list_to_detail, tas);
}
}
Layar Detail
Menangkap tas (Bundle Arguments) yang dikirim oleh NavController, lalu membongkarnya ke layar.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="24dp">
<TextView
android:id="@+id/tvDetNama"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="28sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvDetHarga"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#ec4899"
android:layout_marginVertical="16dp" />
<TextView
android:id="@+id/tvDetDesc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center" />
</LinearLayout>
package com.example.cafe;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
public class DetailFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_detail, container, false);
// PENTING! Bedanya Activity dan Fragment ada di sini!
// Kamu wajib pakai view.findViewById() karena view-nya baru diciptakan di atas
TextView tvNama = view.findViewById(R.id.tvDetNama);
TextView tvHarga = view.findViewById(R.id.tvDetHarga);
TextView tvDesc = view.findViewById(R.id.tvDetDesc);
// Tangkap tas (Bundle Arguments) yang dikirim oleh NavController
if (getArguments() != null) {
Kopi kopiMasuk = (Kopi) getArguments().getSerializable("DATA_KOPI");
if(kopiMasuk != null) {
tvNama.setText(kopiMasuk.getNama());
tvHarga.setText(kopiMasuk.getHarga());
tvDesc.setText(kopiMasuk.getDesc());
}
}
return view;
}
}
Area RecyclerView
Ini adalah pondasi RecyclerView biasa.
Bagian terpenting ada di Adapter, di mana kita membuat
Interface OnItemClickListener agar aksi klik bisa dikirim kembali ke
`ListFragment`.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:layout_marginBottom="8dp"
android:background="?attr/selectableItemBackground">
<TextView
android:id="@+id/tvRowNama"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold" />
<TextView
android:id="@+id/tvRowHarga"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#6366f1" />
</LinearLayout>
package com.example.cafe;
import java.io.Serializable;
// Serializable agar objek ini bisa dikirim lewat Intent/Bundle
public class Kopi implements Serializable {
private String nama, desc, harga;
public Kopi(String nama, String desc, String harga) { this.nama=nama; this.desc=desc; this.harga=harga; }
public String getNama() { return nama; }
public String getDesc() { return desc; }
public String getHarga() { return harga; }
}
package com.example.cafe;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class KopiAdapter extends RecyclerView.Adapter<KopiAdapter.KopiViewHolder> {
private ArrayList<Kopi> listKopi;
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(Kopi item);
}
public KopiAdapter(ArrayList<Kopi> list, OnItemClickListener listener) {
this.listKopi = list; this.listener = listener;
}
@NonNull
@Override
public KopiViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_kopi, parent, false);
return new KopiViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull KopiViewHolder holder, int position) {
Kopi item = listKopi.get(position);
holder.tvNama.setText(item.getNama());
holder.tvHarga.setText(item.getHarga());
// Sinyal klik ke Fragment
holder.itemView.setOnClickListener(v -> listener.onItemClick(item));
}
@Override
public int getItemCount() { return listKopi.size(); }
class KopiViewHolder extends RecyclerView.ViewHolder {
TextView tvNama, tvHarga;
public KopiViewHolder(@NonNull View itemView) {
super(itemView);
tvNama = itemView.findViewById(R.id.tvRowNama);
tvHarga = itemView.findViewById(R.id.tvRowHarga);
}
}
}
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:
- Buat project baru berarsitektur 1 MainActivity dan dibungkus
BottomNavigationView. Gunakan fitur modern Jetpack Navigation. Pastikan kamu menambahkan library Jetpack di `build.gradle` terlebih dahulu! - Buat 3 Pasang Fragment (Home, List, Profil). Coba berkreasi dengan menambahkan foto/warna di XML fragment-mu.
- Pastikan transisi dari List Kopi menuju Detail Kopi berjalan mulus dan datanya terbawa!
this atau
MainActivity.this untuk memberikan Context. Gunakanlah perintah
requireActivity() atau getContext() karena Fragment menumpang
pada Activity!