Mem-build aplikasi adaptif dengan navigasi dinamis

1. Pengantar

Salah satu keuntungan besar pengembangan aplikasi Anda di platform Android adalah peluang besar untuk menjangkau pengguna dalam berbagai jenis faktor bentuk, seperti perangkat wearable, perangkat foldable, tablet, desktop, dan bahkan TV. Saat menggunakan aplikasi, pengguna Anda mungkin ingin menggunakan aplikasi yang sama pada perangkat layar besar untuk memanfaatkan peningkatan properti. Pengguna Android yang menggunakan aplikasi mereka di beberapa perangkat dengan berbagai ukuran layar makin banyak, dan mereka mengharapkan pengalaman pengguna berkualitas tinggi di semua perangkat.

Sejauh ini, Anda telah belajar membuat aplikasi yang utamanya untuk perangkat seluler. Dalam codelab ini, Anda akan mempelajari cara mengubah aplikasi agar dapat menyesuaikan ukuran layar lainnya. Anda akan menggunakan pola tata letak navigasi adaptif yang bagus dan dapat digunakan untuk seluler dan perangkat berlayar lebar, seperti perangkat foldable, tablet, dan desktop.

Prasyarat

  • Memahami berbagai aspek terkait pemrograman Kotlin, termasuk class, fungsi, dan kondisional
  • Pemahaman dalam menggunakan class ViewModel
  • Pemahaman dalam membuat fungsi Composables
  • Pengalaman membuat tata letak dengan Jetpack Compose
  • Pengalaman menjalankan aplikasi di perangkat atau emulator

Yang akan Anda pelajari

  • Cara membuat navigasi antarlayar tanpa Grafik Navigasi untuk aplikasi sederhana
  • Cara membuat tata letak navigasi adaptif menggunakan Jetpack Compose
  • Cara membuat pengendali kembali kustom

Yang akan Anda build

  • Anda akan menerapkan navigasi dinamis di aplikasi Reply yang ada agar tata letaknya beradaptasi dengan semua ukuran layar

Produk akhir akan terlihat seperti gambar di bawah ini:

​​ Ilustrasi aplikasi Reply di akhir codelab ini dengan panel navigasi ditampilkan di sebelah kiri. Panel navigasi mencantumkan 4 tab yang dapat dipilih pengguna: "Kotak Masuk", "Terkirim", "Draf", dan "Spam". Di sebelah kanan panel navigasi, daftar contoh email akan ditampilkan.

Yang Anda butuhkan

  • Komputer dengan akses internet, browser web, dan Android Studio
  • Akses ke GitHub

2. Ringkasan aplikasi

Pengantar aplikasi Reply

Aplikasi Reply adalah aplikasi multilayar yang menyerupai program email.

Aplikasi Reply ditampilkan dalam mode ponsel. Halaman ini menampilkan daftar contoh email yang dapat dibaca pengguna. Di bagian bawah layar, terdapat empat ikon yang mewakili kotak masuk, terkirim, draf, dan spam.

Tab tersebut berisi 4 kategori berbeda yang ditampilkan oleh tab yang berbeda, yaitu: kotak masuk, terkirim, draf, dan spam.

Mendownload kode awal

Di Android Studio, buka folder basic-android-kotlin-compose-training-reply-app.

  1. Buka halaman repositori GitHub yang disediakan untuk project.
  2. Pastikan nama cabang cocok dengan nama cabang yang ditentukan dalam codelab. Misalnya, dalam screenshot berikut, nama cabang adalah main (utama).

1e4c0d2c081a8fd2.png

  1. Di halaman GitHub project, klik tombol Code yang akan menampilkan pop-up.

1debcf330fd04c7b.png

  1. Pada pop-up, klik tombol Download ZIP untuk menyimpan project di komputer. Tunggu download selesai.
  2. Temukan file di komputer Anda (mungkin di folder Downloads).
  3. Klik dua kali pada file ZIP untuk mengekstraknya. Tindakan ini akan membuat folder baru yang berisi file project.

Membuka project di Android Studio

  1. Mulai Android Studio.
  2. Di jendela Welcome to Android Studio, klik Open.

d8e9dbdeafe9038a.png

Catatan: Jika Android Studio sudah terbuka, pilih opsi menu File > Open.

8d1fda7396afe8e5.png

  1. Di file browser, buka lokasi folder project yang telah diekstrak (kemungkinan ada di folder Downloads).
  2. Klik dua kali pada folder project tersebut.
  3. Tunggu Android Studio membuka project.
  4. Klik tombol Run 8de56cba7583251f.png untuk mem-build dan menjalankan aplikasi. Pastikan aplikasi di-build seperti yang diharapkan.

3. Panduan kode awal

Direktori penting di aplikasi Reply

Direktori file Aplikasi Reply menampilkan dua sub-direktori yang diperluas: "data" dan "ui". Dalam direktori UI, MainActivity.kt dipilih. MainActivity.kt akan muncul di akhir daftar konten.

Lapisan data dan UI project aplikasi Reply dipisahkan ke dalam berbagai direktori. ReplyViewModel, ReplyUiState, dan composable lainnya terletak di direktori ui. Class data dan enum yang menentukan lapisan data dan class penyedia data terletak di direktori data.

Inisialisasi data di aplikasi Reply

Aplikasi Reply dilakukan inisialisasi dengan data melalui metode initilizeUIState() di ReplyViewModel, yang dijalankan dalam fungsi init.

ReplyViewModel.kt

...
   init {
        initializeUIState()
    }

   private fun initializeUIState() {
        var mailboxes: Map<MailboxType, List<Email>> =
            LocalEmailsDataProvider.allEmails.groupBy { it.mailbox }
        _uiState.value =
            ReplyUiState(
                mailboxes = mailboxes,
                currentSelectedEmail = mailboxes[MailboxType.Inbox]?.get(0)
                    ?: LocalEmailsDataProvider.defaultEmail
            )
    }
...

Composable tingkat layar

Seperti aplikasi lainnya, aplikasi Reply menggunakan composable ReplyApp sebagai composable utama tempat viewModel dan uiState dideklarasikan. Berbagai fungsi viewModel juga diteruskan sebagai argumen lambda untuk composable ReplyHomeScreen.

ReplyApp.kt

...
@Composable
fun ReplyApp(modifier: Modifier = Modifier) {
    val viewModel: ReplyViewModel = viewModel()
    val replyUiState = viewModel.uiState.collectAsState().value

    ReplyHomeScreen(
        replyUiState = replyUiState,
        onTabPressed = { mailboxType: MailboxType ->
            viewModel.updateCurrentMailbox(mailboxType = mailboxType)
            viewModel.resetHomeScreenStates()
        },
        onEmailCardPressed = { email: Email ->
            viewModel.updateDetailsScreenStates(
                email = email
            )
        },
        onDetailScreenBackPressed = {
            viewModel.resetHomeScreenStates()
        },
        modifier = modifier
    )
}

Composable lainnya

  • ReplyHomeScreen.kt: berisi composable layar untuk layar utama, termasuk elemen navigasi.
  • ReplyHomeContent.kt: berisi composable yang menentukan composable layar utama yang lebih mendetail.
  • ReplyDetailsScreen.kt: berisi composable layar dan composable yang lebih kecil untuk layar detail.

Anda dapat membaca setiap file secara mendetail untuk mendapatkan pemahaman yang lebih baik tentang composable sebelum melanjutkan ke bagian codelab berikutnya.

4. Mengubah layar tanpa grafik navigasi

Di jalur sebelumnya, Anda telah mempelajari cara menggunakan class NavHostController untuk berpindah dari satu layar ke layar lainnya. Dengan Compose, Anda juga dapat mengubah layar dengan pernyataan bersyarat sederhana dengan menggunakan status runtime yang dapat diubah. Ini sangat berguna terutama pada aplikasi kecil seperti aplikasi Reply, karena Anda hanya ingin beralih di antara dua layar.

Mengubah layar dengan perubahan status

Di Compose, layar dikomposisi ulang saat terjadi perubahan status. Anda dapat mengubah layar menggunakan kondisional sederhana untuk merespons perubahan status.

Anda akan menggunakan kondisional untuk menampilkan konten layar utama saat pengguna berada di layar utama, dan layar detail saat pengguna tidak berada di layar utama.

Modifikasi aplikasi Reply untuk mengizinkan perubahan layar saat status berubah dengan menyelesaikan langkah berikut:

  1. Buka kode awal di Android Studio.
  2. Di composable ReplyHomeScreen di ReplyHomeScreen.kt, gabungkan composable ReplyAppContent dengan pernyataan if saat properti isShowingHomepage dari objek replyUiState adalah true.

ReplyHomeScreen.kt

@Composable
fun ReplyHomeScreen(
    replyUiState: ReplyUiState,
    onTabPressed: (MailboxType) -> Unit = {},
    onEmailCardPressed: (Int) -> Unit = {},
    onDetailScreenBackPressed: () -> Unit = {},
    modifier: Modifier = Modifier
) {

...
    if (replyUiState.isShowingHomepage) {
        ReplyAppContent(
            replyUiState = replyUiState,
            onTabPressed = onTabPressed,
            onEmailCardPressed = onEmailCardPressed,
            navigationItemContentList = navigationItemContentList,
            modifier = modifier

        )
    }
}

Anda kini harus memperhitungkan skenario saat pengguna tidak ada di layar utama dengan menampilkan layar detail.

  1. Tambahkan cabang else dengan composable ReplyDetailsScreen di isinya. Menambahkan replyUIState, onDetailScreenBackPressed, dan modifier sebagai argumen untuk composable ReplyDetailsScreen.

ReplyHomeScreen.kt

@Composable
fun ReplyHomeScreen(
    replyUiState: ReplyUiState,
    onTabPressed: (MailboxType) -> Unit = {},
    onEmailCardPressed: (Int) -> Unit = {},
    onDetailScreenBackPressed: () -> Unit = {},
    modifier: Modifier = Modifier
) {

...

    if (replyUiState.isShowingHomepage) {
        ReplyAppContent(
            replyUiState = replyUiState,
            onTabPressed = onTabPressed,
            onEmailCardPressed = onEmailCardPressed,
            navigationItemContentList = navigationItemContentList,
            modifier = modifier

        )
    } else {
        ReplyDetailsScreen(
            replyUiState = replyUiState,
            onBackPressed = onDetailScreenBackPressed,
            modifier = modifier
        )
    }
}

Objek replyUiState adalah objek status. Dengan demikian, jika ada perubahan pada properti isShowingHomepage dari objek replyUiState, composable ReplyHomeScreen akan dibuat ulang dan pernyataan if/else akan dievaluasi ulang saat runtime. Pendekatan ini mendukung navigasi antarlayar yang berbeda tanpa menggunakan class NavHostController.

Ilustrasi animasi dari aplikasi Reply pada emulator ponsel yang menampilkan perubahan layar dari Layar utama ke halaman detail. Layar utama menampilkan daftar email dengan 4 ikon untuk pesan di bagian bawah (kotak masuk, terkirim, draf, dan spam). Halaman detail menampilkan teks lengkap email contoh, dengan tombol Reply dan Reply All di bawahnya.

Membuat pengendali kembali kustom

Salah satu keuntungan menggunakan composable NavHost untuk beralih antarlayar adalah rute layar sebelumnya disimpan di data sebelumnya. Layar yang disimpan ini memungkinkan tombol kembali sistem menavigasi dengan mudah ke layar sebelumnya saat dipanggil. Karena aplikasi Reply tidak menggunakan NavHost, Anda harus menambahkan kode untuk menangani tombol kembali secara manual. Anda akan melakukannya nanti.

Selesaikan langkah-langkah berikut untuk membuat pengendali kembali kustom di aplikasi Reply:

  1. Pada baris pertama composable ReplyDetailsScreen, tambahkan composable BackHandler.
  2. Panggil fungsi onBackPressed() dalam isi composable BackHandler.

ReplyDetailsScreen.kt

...
import androidx.activity.compose.BackHandler
...
@Composable
fun ReplyDetailsScreen(
    replyUiState: ReplyUiState,
    modifier: Modifier = Modifier,
    onBackPressed: () -> Unit = {},
) {
    BackHandler {
        onBackPressed()
    }
...

5. Menjalankan aplikasi di perangkat layar besar

Memeriksa aplikasi dengan emulator yang dapat diubah ukurannya

Untuk membuat aplikasi yang dapat digunakan, developer harus memahami pengalaman pengguna dalam berbagai faktor bentuk. Oleh karena itu, Anda harus menguji aplikasi pada berbagai faktor bentuk dari awal proses pengembangan.

Anda dapat menggunakan banyak emulator dengan berbagai ukuran layar untuk mencapai sasaran ini. Namun, melakukannya bisa jadi rumit, terutama saat Anda mem-build untuk beberapa ukuran layar sekaligus. Anda mungkin juga perlu menguji bagaimana aplikasi yang berjalan merespons perubahan ukuran layar, seperti perubahan orientasi, perubahan ukuran jendela di desktop, dan perubahan status lipat di perangkat foldable.

Android Studio membantu Anda menguji skenario ini dengan memperkenalkan emulator yang dapat diubah ukurannya.

Selesaikan langkah-langkah berikut untuk menyiapkan emulator yang dapat diubah ukurannya:

  1. Pastikan Anda menjalankan Android Studio Chipmunk | 2021.2.1 atau lebih tinggi.
  2. Di Android Studio, pilih Tools > Device Manager.

Menu Tools menampilkan daftar opsi. Device Manager, yang muncul di sebagian ke bawah daftar, dipilih.

  1. Di Device Manager, klik Create device. Toolbar pengelola perangkat menampilkan dua opsi menu: "Virtual" dan "Physical". Di bawah opsi tersebut, terdapat tombol Create Device.
  2. Pilih kategori Phone dan perangkat Resizable (Experimental).
  3. Klik Berikutnya.

Jendela Device Manager menampilkan permintaan untuk memilih definisi perangkat. Daftar opsi akan ditampilkan dengan kolom penelusuran di atasnya. Kategori "Phone" dipilih, dan nama definisi perangkat "Resizable (Experimental)" dipilih.

  1. Pilih API Level 33.
  2. Klik Berikutnya.

Jendela Virtual Device Configuration menampilkan permintaan untuk memilih image sistem. Tiramisu API dipilih.

  1. Beri nama Android Virtual Device baru Anda.
  2. Klik Finish.

Layar Konfigurasi Virtual di Android Virtural Device (AVD) akan ditampilkan. Layar konfigurasi menyertakan kolom teks untuk memasukkan nama AVD. Di bawah kolom nama terdapat daftar opsi perangkat, termasuk definisi perangkat (Resizable Experimental), image sistem (Tiramisu), dan orientasi, dengan orientasi Potret dipilih secara default. Tombol yang bertuliskan "Change" akan muncul di sebelah kanan definisi perangkat dan informasi image sistem, dan opsi Lanskap ada di sebelah kanan opsi orientasi Potret yang dipilih. Di pojok kanan bawah, terdapat 4 tombol: Cancel, Previous, Next (yang berwarna abu-abu dan tidak dapat dipilih), dan Finish,

Menjalankan aplikasi pada emulator perangkat layar besar

Setelah penyiapan emulator yang dapat diubah ukurannya, mari kita lihat tampilan aplikasi di layar besar.

  1. Jalankan aplikasi di emulator yang dapat diubah ukurannya.
  2. Pilih Tablet untuk mode tampilan.

Emulator yang dapat diubah ukurannya menampilkan aplikasi Reply di layar ponsel. Aplikasi akan menampilkan daftar pesan dengan ikon 4 di bagian bawah layar untuk Kotak Masuk, Terkirim, Draf, dan Spam.

  1. Periksa aplikasi dalam mode Tablet dalam mode lanskap.

Emulator yang dapat diubah ukurannya menampilkan aplikasi Reply di layar tablet, dengan bagian body yang diperpanjang. Ikon ditampilkan di bagian bawah layar untuk kotak masuk, email terkirim, draf, dan spam.

Perhatikan bahwa tampilan layar tablet dibuat panjang secara horizontal. Meskipun fungsional, orientasi ini mungkin bukan penggunaan real estate layar besar yang terbaik. Mari kita bahas masalah ini berikutnya.

Desain untuk perangkat layar besar

Saat pertama kali melihat aplikasi ini di tablet, pikiran Anda adalah salah desain dan tidak menarik. Tepat sekali: tata letak ini tidak didesain untuk penggunaan pada perangkat layar besar.

Saat membuat desain untuk perangkat layar besar, seperti tablet dan perangkat foldable, Anda harus mempertimbangkan ergonomi pengguna dan kedekatan jari pengguna dengan layar. Dengan perangkat seluler, jari pengguna dapat dengan mudah menjangkau sebagian besar layar; lokasi elemen interaktif, seperti tombol dan elemen navigasi, tidak terlalu penting. Namun, untuk layar besar, memiliki elemen interaktif penting di bagian tengah layar dapat membuatnya sulit dijangkau.

Seperti yang Anda lihat di aplikasi Reply, desain untuk perangkat layar besar tidak hanya meregangkan atau memperbesar elemen UI agar sesuai dengan layar. Ini adalah peluang untuk menggunakan properti yang ditingkatkan untuk menciptakan pengalaman yang berbeda bagi pengguna Anda. Misalnya, Anda dapat menambahkan tata letak lain di layar yang sama untuk mencegah keharusan menavigasi ke layar lain atau memungkinkan multitasking.

Aplikasi Reply dengan layar detail yang ditampilkan di halaman beranda bersama dengan panel navigasi dan daftar email. Contoh email ditampilkan di sebelah kanan daftar email. Tombol Reply dan Reply All ditampilkan di bawah contoh email.

Desain ini dapat meningkatkan produktivitas pengguna dan mendorong interaksi yang lebih besar. Namun, sebelum men-deploy desain ini, Anda harus terlebih dahulu mempelajari cara membuat tata letak yang berbeda untuk ukuran layar yang berbeda.

6. Membuat tata letak beradaptasi dengan berbagai ukuran layar

Apa itu titik henti sementara?

Anda mungkin bertanya-tanya bagaimana Anda dapat menampilkan berbagai tata letak untuk aplikasi yang sama. Jawaban singkatnya adalah dengan menggunakan kondisional pada berbagai status, seperti yang Anda lakukan di awal codelab ini.

Untuk membuat aplikasi adaptif, Anda memerlukan tata letak untuk berubah berdasarkan ukuran layar. Titik pengukuran yang perubahan tata letaknya dikenal sebagai titik henti sementara. Desain Material membuat rentang titik henti sementara yang tidak sesuai yang mencakup sebagian besar layar Android.

Tabel yang berisi rentang titik henti sementara (dalam dp) untuk berbagai jenis dan penyiapan perangkat. 0 hingga 599 dp adalah untuk handset dalam mode potret, ponsel dalam mode lanskap, ukuran jendela yang rapat, 4 kolom, dan 8 margin minimum. 600 hingga 839 dp adalah untuk tablet kecil yang dapat dilipat dalam mode portait atau lanskap, class ukuran jendela sedang, 12 kolom, dan 12 margin minimum. 840 dp atau lebih besar untuk tablet besar dalam mode potret atau lanskap, class ukuran jendela yang diperluas, 12 kolom, dan 32 margin minimum. Catatan tabel menyatakan bahwa margin dan gutter fleksibel dan tidak perlu sama ukurannya dan bahwa ponsel dalam lanskap dianggap sebagai pengecualian agar tetap pas dalam rentang titik henti sementara 0 hingga 599 dp.

Tabel rentang titik henti sementara ini menunjukkan, misalnya, jika aplikasi Anda saat ini berjalan di perangkat dengan ukuran layar kurang dari 600 dp, Anda harus menampilkan tata letak seluler.

Menggunakan Window Size Class

WindowSizeClass API yang diperkenalkan untuk Compose membuat penerapan titik henti sementara Desain Material lebih sederhana.

Window Size Class memperkenalkan tiga kategori ukuran: Ringkas, Sedang, dan Diperluas, untuk lebar dan tinggi.

Diagram ini mewakili class ukuran jendela berbasis lebar. Diagram ini mewakili class ukuran jendela berbasis tinggi.

Selesaikan langkah berikut untuk menerapkan WindowSizeClass API di aplikasi Reply:

  1. Tambahkan dependensi material3-window-size-class ke file build.gradle modul.

build.gradle

...
dependencies {
...
"androidx.compose.material3:material3-window-size-class:$material3_version"
...
  1. Klik Sync Now untuk menyinkronkan gradle setelah menambahkan dependensi.

Tombol Sync Now ditampilkan di bawah tab untuk memilih file .kt dan .gradle yang berbeda. Di sebelah kanan tombol Sync Now terdapat tombol lain yang bertuliskan Ignore these changes.

Dengan file build.grade terbaru, Anda kini dapat membuat variabel yang menyimpan ukuran jendela aplikasi pada waktu tertentu.

  1. Pada fungsi onCreate() di file MainActivity.kt, tetapkan metode calculateWindowSizeClass dengan konteks this yang diteruskan dalam parameter ke variabel bernama windowSize.
  2. Impor paket calculateWindowSizeClass yang sesuai.

MainActivity.kt

...
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass

...

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ReplyTheme {
                val windowSize = calculateWindowSizeClass(this)
                ReplyApp()

...
  1. Perhatikan garis bawah merah sintaksis calculateWindowSizeClass, yang menampilkan bohlam merah. Klik bohlam merah di sebelah kiri variabel windowSize dan pilih Opt in for ‘ExperimentalMaterial3WindowSizeClassApi' di ‘onCreate' untuk membuat anotasi di atas metode onCreate().

Dalam kode, baris "val windowSize = calculateWindowSizeClass(this)" dipilih, dengan ikon bohlam kanan ditampilkan di sebelah kiri baris kode. Di bawah bohlam yang dipilih terdapat daftar opsi untuk mengatasi error, dengan "Opt in for 'ExperimentalMaterial3WindowSizeClassApi' di 'onCreate' dipilih.

Anda dapat menggunakan variabel WindowWidthSizeClass di MainActivity.kt untuk menentukan tata letak mana yang akan ditampilkan di berbagai composable. Mari siapkan composable ReplyApp untuk menerima nilai ini.

  1. Dalam file ReplyApp.kt, ubah composable ReplyApp untuk menerima WindowWidthSizeClass sebagai parameter dan impor paket yang sesuai.

ReplyApp.kt

...
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
...

@Composable
fun ReplyApp(
    windowSize: WindowWidthSizeClass,
    modifier: Modifier = Modifier
) {
...
  1. Teruskan variabel windowSize ke komponen ReplyApp dalam metode onCreate() file MainActivity.kt.

MainActivity.kt

...
         setContent {
            ReplyTheme {
                val windowSize = calculateWindowSizeClass(this)
                ReplyApp(
                    windowSize = windowSize.widthSizeClass
                )
...

Anda juga harus memperbarui pratinjau aplikasi untuk parameter windowSize.

  1. Teruskan WindowWidthSizeClass.Compact sebagai parameter windowSize ke composable ReplyApp untuk komponen pratinjau, lalu impor paket yang sesuai.

MainActivity.kt

...
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
...

@Preview(showBackground = true)
@Composable
fun ReplyAppPreview() {
    ReplyTheme {
        ReplyApp(
            windowSize = WindowWidthSizeClass.Compact,
        )
    }
}
  1. Untuk mengubah tata letak aplikasi berdasarkan ukuran layar, tambahkan pernyataan when dalam composable ReplyApp berdasarkan nilai WindowWidthSizeClass.

ReplyApp.kt

...

@Composable
fun ReplyApp(
    windowSize: WindowWidthSizeClass,
    modifier: Modifier = Modifier
) {
    val viewModel: ReplyViewModel = viewModel()
    val replyUiState = viewModel.uiState.collectAsState().value

    when (windowSize) {
        WindowWidthSizeClass.Compact -> {
        }
        WindowWidthSizeClass.Medium -> {
        }
        WindowWidthSizeClass.Expanded -> {
        }
        else -> {
        }
    }
...

Pada tahap ini, Anda telah menetapkan dasar untuk menggunakan nilai WindowSizeClass guna mengubah tata letak di aplikasi. Langkah berikutnya adalah menentukan tampilan aplikasi yang Anda inginkan pada ukuran layar yang berbeda.

7. Mengimplementasikan tata letak navigasi adaptif

Mengimplementasikan navigasi UI adaptif

Saat ini, navigasi bawah digunakan untuk semua ukuran layar.

Navigasi bawah untuk aplikasi Reply.

Seperti yang telah dibahas sebelumnya, elemen navigasi ini tidak ideal karena pengguna dapat mengalami kesulitan untuk menjangkau elemen navigasi yang penting ini di layar yang lebih besar. Untungnya, ada pola yang direkomendasikan untuk berbagai elemen navigasi untuk berbagai class ukuran jendela di navigasi untuk UI yang responsif. Untuk aplikasi Reply, Anda dapat menerapkan elemen berikut:

Tabel mencantumkan class ukuran jendela dan beberapa item yang ditampilkan. Lebar rapat akan menampilkan menu navigasi bawah. Lebar sedang menampilkan kolom samping navigasi. Lebar yang diperluas menampilkan panel navigasi persisten dengan tepi depan.

Kolom samping navigasi adalah komponen navigasi lain dengan desain material yang memungkinkan opsi navigasi ringkas untuk tujuan utama dapat diakses dari sisi aplikasi.

Contoh kolom samping navigasi di aplikasi Reply menampilkan 4 ikon secara vertikal: kotak masuk, terkirim, draf, dan spam.

Demikian pula, panel navigasi persisten/permanen dibuat oleh desain material sebagai opsi lain untuk memberikan akses ergonomis untuk layar yang lebih besar.

Panel navigasi permanen di aplikasi Reply secara vertikal mencantumkan 4 tab dengan ikon dan nama tab: Kotak Masuk, Terkirim, Draf, dan Spam.

Mengimplementasikan panel navigasi

Untuk membuat panel navigasi bagi layar yang diperluas, Anda dapat menggunakan parameter navigationType. Selesaikan langkah-langkah berikut untuk melakukannya:

  1. Untuk merepresentasikan berbagai jenis elemen navigasi, buat file baru WindowStateUtils.kt dalam paket utils baru, yang berada di direktori ui.
  2. Tambahkan class Enum untuk mewakili berbagai jenis elemen navigasi.

WindowStateUtils.kt

package com.example.reply.ui.utils

enum class ReplyNavigationType {
    BOTTOM_NAVIGATION, NAVIGATION_RAIL, PERMANENT_NAVIGATION_DRAWER
}

Agar berhasil menerapkan panel navigasi, Anda perlu menentukan jenis navigasi berdasarkan ukuran jendela aplikasi.

  1. Pada composable ReplyApp, buat variabel navigationType dan tetapkan nilai ReplyNavigationType yang sesuai, sesuai dengan ukuran layar dalam pernyataan when.

ReplyApp.kt

...
import com.example.reply.ui.utils.ReplyNavigationType
...
    when (windowSize) {
        WindowWidthSizeClass.Compact -> {
            navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
        }
        WindowWidthSizeClass.Medium -> {
            navigationType = ReplyNavigationType.NAVIGATION_RAIL
        }
        WindowWidthSizeClass.Expanded -> {
            navigationType = ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
        }
        else -> {
            navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
        }
    }
...

Anda dapat menggunakan nilai navigationType dalam composable ReplyHomeScreen. Anda dapat mempersiapkannya dengan menjadikannya parameter untuk composable.

  1. Pada composable ReplyHomeScreen, tambahkan navigationType sebagai parameter.

ReplyHomeScreen.kt

...
@Composable
fun ReplyHomeScreen(
    navigationType: ReplyNavigationType,
    replyUiState: ReplyUiState,
    onTabPressed: (MailboxType) -> Unit = {},
    onEmailCardPressed: (Email) -> Unit = {},
    onDetailScreenBackPressed: () -> Unit = {},
    modifier: Modifier = Modifier
)

...

  1. Teruskan navigationType ke composable ReplyHomeScreen.

ReplyApp.kt

...
   ReplyHomeScreen(
        navigationType = navigationType,
        replyUiState = replyUiState,
        onTabPressed = { mailboxType: MailboxType ->
            viewModel.updateCurrentMailbox(mailboxType = mailboxType)
            viewModel.resetHomeScreenStates()
        },
        onEmailCardPressed = { email: Email ->
            viewModel.updateDetailsScreenStates(
                email = email
            )
        },
        onDetailScreenBackPressed = {
            viewModel.resetHomeScreenStates()
        },
        modifier = modifier
    )
...

Selanjutnya, Anda dapat membuat cabang untuk menampilkan konten aplikasi dengan panel navigasi saat pengguna membuka aplikasi pada layar yang diperluas dan menampilkan layar utama.

  1. Dalam isi composable ReplyHomeScreen, tambahkan pernyataan if untuk kondisi navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER && replyUiState.isShowingHomepage.

ReplyHomeScreen.kt

import androidx.compose.material3.PermanentNavigationDrawer
...
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReplyHomeScreen(
    navigationType: ReplyNavigationType,
    replyUiState: ReplyUiState,
    onTabPressed: (MailboxType) -> Unit = {},
    onEmailCardPressed: (Email) -> Unit = {},
    onDetailScreenBackPressed: () -> Unit = {},
    modifier: Modifier = Modifier
) {
...
    if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
        && replyUiState.isShowingHomepage
    ) {
    }

    if (replyUiState.isShowingHomepage) {
            ReplyAppContent(
                replyUiState = replyUiState,
...
  1. Untuk membuat panel samping permanen, buat composable PermanentNavigationDrawer dalam isi pernyataan if dan tambahkan composable NavigationDrawerContent sebagai input untuk parameter drawerContent.
  2. Tambahkan composable ReplyAppContent sebagai argumen lambda final dari PermanentNavigationDrawer.

ReplyHomeScreen.kt

...
    if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
        && replyUiState.isShowingHomepage
    ) {
        PermanentNavigationDrawer(
            drawerContent = {
                NavigationDrawerContent(
                    selectedDestination = replyUiState.currentMailbox,
                    onTabPressed = onTabPressed,
                    navigationItemContentList = navigationItemContentList
                )
            }
        ) {
            ReplyAppContent(
                replyUiState = replyUiState,
                onTabPressed = onTabPressed,
                onEmailCardPressed = onEmailCardPressed,
                navigationItemContentList = navigationItemContentList,
                modifier = modifier

            )
        }
    }

...
  1. Tambahkan cabang else yang menggunakan isi composable sebelumnya untuk mempertahankan cabang sebelumnya untuk layar yang tidak diperluas.

ReplyHomeScreen.kt

...
    if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
        && replyUiState.isShowingHomepage
    ) {
        PermanentNavigationDrawer(
            drawerContent = {
                NavigationDrawerContent(
                    selectedDestination = replyUiState.currentMailbox,
                    onTabPressed = onTabPressed,
                    navigationItemContentList = navigationItemContentList
                )
            }
        ) {
            ReplyAppContent(
                replyUiState = replyUiState,
                onTabPressed = onTabPressed,
                onEmailCardPressed = onEmailCardPressed,
                navigationItemContentList = navigationItemContentList,
                modifier = modifier

            )
        }
    } else {
        if (replyUiState.isShowingHomepage) {
            ReplyAppContent(
                replyUiState = replyUiState,
                onTabPressed = onTabPressed,
                onEmailCardPressed = onEmailCardPressed,
                navigationItemContentList = navigationItemContentList,
                modifier = modifier
            )
        } else {
            ReplyDetailsScreen(
                replyUiState = replyUiState,
                onBackPressed = onDetailScreenBackPressed,
                modifier = modifier
            )
        }
    }
}
...
  1. Tambahkan anotasi eksperimental ke composable ReplyHomeScreen. Anda memerlukannya karena PermanentNavigationDrawer API masih bersifat eksperimental.

ReplyHomeScreen.kt

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReplyHomeScreen(
    navigationType: ReplyNavigationType,
    replyUiState: ReplyUiState,
    onTabPressed: (MailboxType) -> Unit = {},
    onEmailCardPressed: (Email) -> Unit = {},
    onDetailScreenBackPressed: () -> Unit = {},
    modifier: Modifier = Modifier
) {
...
  1. Jalankan aplikasi dalam mode Tablet. Anda akan melihat layar berikut:

Aplikasi Reply ditampilkan dalam mode tablet dengan panel navigasi di sisi kiri layar dan daftar email di sebelah kanannya.

Mengimplementasikan kolom samping navigasi

Serupa dengan implementasi panel navigasi, Anda harus menggunakan parameter navigationType untuk beralih di antara elemen navigasi.

Pertama, mari kita tambahkan kolom samping navigasi untuk layar berukuran sedang.

  1. Mulai dengan menyiapkan composable ReplyAppContent dengan menambahkan navigationType sebagai parameter.

ReplyHomeScreen.kt

...
@Composable
private fun ReplyAppContent(
    navigationType: ReplyNavigationType,
    replyUiState: ReplyUiState,
    onTabPressed: ((MailboxType) -> Unit) = {},
    onEmailCardPressed: (Email) -> Unit = {},
    navigationItemContentList: List<NavigationItemContent>,
    modifier: Modifier = Modifier
) {
...
  1. Teruskan nilai navigationType ke kedua composable ReplyAppContent.

ReplyHomeScreen.kt

...
            ReplyAppContent(
                navigationType = navigationType,
                replyUiState = replyUiState,
                onTabPressed = onTabPressed,
                onEmailCardPressed = onEmailCardPressed,
                navigationItemContentList = navigationItemContentList,
                modifier = modifier
            )
        }
    } else {
        if (replyUiState.isShowingHomepage) {
            ReplyAppContent(
                navigationType = navigationType,
                replyUiState = replyUiState,
                onTabPressed = onTabPressed,
                onEmailCardPressed = onEmailCardPressed,
                navigationItemContentList = navigationItemContentList,
                modifier = modifier
            )
...

Selanjutnya, mari kita tambahkan cabang, yang memungkinkan aplikasi menampilkan kolom samping navigasi untuk beberapa skenario.

  1. Di baris pertama isi composable ReplyAppContent, gabungkan composable ReplyNavigationRail di sekitar composable AnimatedVisibility dan setel parameter visibility menjadi true jika nilai ReplyNavigationType adalah NavigationRail.

ReplyHomeScreen.kt

...
@Composable
private fun ReplyAppContent(
    navigationType: ReplyNavigationType,
    replyUiState: ReplyUiState,
    onTabPressed: ((MailboxType) -> Unit) = {},
    onEmailCardPressed: (Email) -> Unit = {},
    navigationItemContentList: List<NavigationItemContent>,
    modifier: Modifier = Modifier
) {
        AnimatedVisibility(visible = navigationType == ReplyNavigationType.NAVIGATION_RAIL) {
            ReplyNavigationRail(
                currentTab = replyUiState.currentMailbox,
                onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
            )
        }
        Column(
            modifier = Modifier
                .fillMaxSize()            .background(MaterialTheme.colorScheme.inverseOnSurface)
        ) {
            ReplyListOnlyContent(
                replyUiState = replyUiState,
                onEmailCardPressed = onEmailCardPressed,
                modifier = Modifier.weight(1f)
            )
            ReplyBottomNavigationBar(
                currentTab = replyUiState.currentMailbox,
                onTabPressed = onTabPressed,
                navigationItemContentList = navigationItemContentList

            )
        }

}
...
  1. Untuk meratakan composable dengan benar, gabungkan composable AnimatedVisibility dan composable Column yang ditemukan dalam isi ReplyAppContent di composable Row.

ReplyHomeScreen.kt

...
@Composable
private fun ReplyAppContent(
    navigationType: ReplyNavigationType,
    replyUiState: ReplyUiState,
    onTabPressed: ((MailboxType) -> Unit) = {},
    onEmailCardPressed: (Email) -> Unit = {},
    navigationItemContentList: List<NavigationItemContent>,
    modifier: Modifier = Modifier
) {
    Row(modifier = modifier.fillMaxSize()) {
        AnimatedVisibility(visible = navigationType == ReplyNavigationType.NAVIGATION_RAIL) {
            ReplyNavigationRail(
                currentTab = replyUiState.currentMailbox,
                onTabPressed = onTabPressed,
                navigationItemContentList = navigationItemContentList
            )
        }
        Column(
            modifier = Modifier
                .fillMaxSize()            .background(MaterialTheme.colorScheme.inverseOnSurface)
        ) {
            ReplyListOnlyContent(
                replyUiState = replyUiState,
                onEmailCardPressed = onEmailCardPressed,
                modifier = Modifier.weight(1f)
            )
            ReplyBottomNavigationBar(
                currentTab = replyUiState.currentMailbox,
                onTabPressed = onTabPressed,
                navigationItemContentList = navigationItemContentList

            )
        }
    }
}
...

Terakhir, pastikan navigasi bawah ditampilkan dalam beberapa skenario.

  1. Setelah composable ReplyListOnlyContent, gabungkan composable ReplyBottomNavigationBar dengan composable AnimatedVisibility.
  2. Tetapkan parameter visible saat nilai ReplyNavigationType adalah BOTTOM_NAVIGATION.

ReplyHomeScreen.kt

...
            ReplyListOnlyContent(
                replyUiState = replyUiState,
                onEmailCardPressed = onEmailCardPressed,
                modifier = Modifier.weight(1f)
            )
            AnimatedVisibility(visible = navigationType == ReplyNavigationType.BOTTOM_NAVIGATION) {
                ReplyBottomNavigationBar(
                    currentTab = replyUiState.currentMailbox,
                    onTabPressed = onTabPressed,
                    navigationItemContentList = navigationItemContentList
                )
            }
...
  1. Jalankan aplikasi dalam mode perangkat foldable yang ditutup. Anda akan melihat layar berikut:

Aplikasi Reply ditampilkan di perangkat foldable dengan kolom samping navigasi di sisi kiri layar dan daftar email di sebelah kanannya.

8. Mendapatkan kode solusi

Guna mendownload kode untuk codelab yang sudah selesai, Anda dapat menggunakan perintah git ini:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-reply-app.git

cd basic-android-kotlin-compose-training-reply-app
git checkout nav-update

Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.

Jika Anda ingin melihat kode solusi, lihat di GitHub.

9. Kesimpulan

Selamat! Anda selangkah lebih dekat untuk membuat aplikasi Reply dapat menyesuaikan semua ukuran layar dengan menerapkan tata letak navigasi adaptif. Anda meningkatkan pengalaman pengguna menggunakan banyak faktor bentuk Android. Pada codelab berikutnya, Anda akan meningkatkan keterampilan menggunakan aplikasi adaptif dengan menerapkan tata letak, pengujian, dan pratinjau konten adaptif.

Jangan lupa untuk membagikan karya Anda di media sosial dengan #AndroidBasics.

Pelajari lebih lanjut