Membuat aplikasi dapat disesuaikan dan diakses dengan Jetpack Compose

1. Pengantar

Dalam codelab ini, Anda akan mempelajari cara membuat aplikasi adaptif untuk ponsel, tablet, dan perangkat foldable, serta cara menjaga aksesibilitas sebagai intinya dengan Jetpack Compose. Anda juga akan mempelajari praktik terbaik untuk menggunakan komponen dan tema Material 3.

Sebelum mempelajari lebih dalam, kita harus memahami apa yang dimaksud dengan kemampuan beradaptasi dan aksesibilitas.

Kemampuan untuk disesuaikan

UI untuk aplikasi Anda harus responsif agar dapat memperhitungkan berbagai ukuran layar, orientasi, serta faktor bentuk. Tata letak adaptif berubah berdasarkan ruang layar yang tersedia. Perubahan ini bervariasi mulai dari penyesuaian tata letak sederhana untuk mengisi ruang, memilih gaya navigasi, dan mengubah tata letak sepenuhnya untuk memanfaatkan ruang tambahan.

Aksesibilitas

Aplikasi Android harus dapat digunakan oleh semua orang, termasuk orang yang memiliki kebutuhan aksesibilitas. Aplikasi harus menyesuaikan dengan skenario yang berbeda guna memberikan pengalaman pengguna terbaik dari kontras warna, jangkauan, dan lainnya.

Dalam codelab ini, Anda akan mempelajari cara menggunakan dan memikirkan kemampuan beradaptasi dan aksesibilitas saat menggunakan Jetpack Compose. Anda akan membuat aplikasi bernama REPLY yang menunjukkan cara menerapkan kemampuan penyesuaian untuk semua jenis layar. Anda akan melihat bagaimana kemampuan beradaptasi dan aksesibilitas bekerja sama untuk memberikan pengalaman yang optimal kepada pengguna.

Yang akan Anda pelajari

  • Cara mendesain aplikasi Anda untuk menargetkan semua ukuran layar dengan Jetpack Compose.
  • Cara menargetkan aplikasi untuk perangkat foldable yang berbeda.
  • Cara menggunakan berbagai jenis navigasi untuk jangkauan dan aksesibilitas yang lebih baik.
  • Cara mendesain skema warna Material 3 dan tema dinamis untuk memberikan pengalaman aksesibilitas yang optimal.
  • Cara menggunakan komponen Material 3 untuk memberikan pengalaman terbaik dalam setiap ukuran layar.

Yang Anda butuhkan

  • Android Studio Bumblebee.
  • Pengetahuan tentang Kotlin.
  • Pemahaman dasar tentang Compose (seperti anotasi @Composable).
  • Pemahaman dasar tentang tata letak Compose (misalnya, Row dan Column).
  • Pemahaman dasar tentang pengubah (misalnya, Modifier.padding).

Jika Anda tidak terbiasa dengan Compose, pertimbangkan untuk mengikuti codelab dasar-dasar Jetpack Compose sebelum menyelesaikan codelab ini.

Yang akan Anda buat

  • Aplikasi klien Email Balas interaktif menggunakan praktik terbaik untuk Material 3, tema dinamis, dan desain yang mudah disesuaikan.

Etalase dukungan beberapa perangkat yang akan Anda capai dalam codelab ini

2. Memulai persiapan

Untuk mendownload aplikasi contoh, Anda dapat melakukan:

atau clone repositori GitHub dari command line dengan perintah ini:

git clone https://github.com/googlecodelabs/android-compose-codelabs.git
cd android-compose-codelabs/ReplyAdaptabilityCodelab

Anda dapat menjalankan salah satu modul di Android Studio kapan saja dengan mengubah konfigurasi run di toolbar.

b059413b0cf9113a.png

Membuka Project ke Android Studio

  1. Di jendela Welcome to Android Studio, pilih c01826594f360d94.png Buka Project yang Ada.
  2. Pilih folder [Download Location]/ReplyAdaptabilityCodelab (pastikan Anda memilih direktori ReplyAdaptabilityCodlab yang berisi build.gradle).
  3. Setelah Android Studio mengimpor project, uji apakah Anda dapat menjalankan modul start dan finished.

Menjelajahi kode awal

Kode mulai berisi empat paket:

  • MainActivity - Aktivitas titik masuk tempat Anda memulai ReplyApp. Anda akan melakukan perubahan pada file ini.
  • ui - Berisi tema, komponen, dan ReplyApp tempat UI penulisan Anda dimulai. Anda akan membuat perubahan pada paket ini.
  • util - Berisi kode bantuan untuk project. Anda tidak perlu mengedit paket ini.

Codelab ini berfokus pada file dalam paket reply. Dalam modul start, ada beberapa file yang perlu dipahami.

File yang akan Anda edit di ui paket

  • MainActivity.kt - Aktivitas Android yang akan menjadi titik awal untuk memulai ReplyApp dan meneruskan info yang diperlukan seperti status lipat, ukuran, dan info tata letak.
  • ReplyApp.kt - Struktur UI aplikasi utama ada di dalam file ReplyApp.kt, yang akan Anda kerjakan.
  • ReplyAppContent.kt - Implementasi Compose dari konten aplikasi dan detail daftar ada di sini.

Mari kita fokuskan pada MainActivity.kt terlebih dahulu Untuk modul mulai, kode seharusnya sudah berfungsi dalam aktivitas Anda.

MainActicity.kt

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

   setContent {
       ReplyTheme {
           val uiState = viewModel.uiState.collectAsState().value
           ReplyApp(uiState)
       }
   }
}

Jika menjalankan aplikasi ini pada ukuran perangkat apa pun, Anda akan melihat regangan layar yang sama untuk mengisi area maksimum tanpa perubahan pada elemen UI. Penyiapan ReplyApp awal tanpa perubahan apa pun.

Mari kita coba menyempurnakannya untuk memanfaatkan ruang layar dan meningkatkan pengalaman pengguna sekaligus tetap menjadikan aksesibilitas sebagai intinya.

3 Membuat aplikasi yang dapat disesuaikan

Bagian ini memperkenalkan apa yang membuat aplikasi mudah disesuaikan, dan komponen apa yang disediakan Material 3 untuk membuatnya lebih mudah bagi kita.

Kita juga akan membahas jenis layar dan status yang akan Anda targetkan, termasuk ponsel, tablet, tablet besar, dan perangkat foldable.

Menangani ukuran jendela

Sebelum membuka aplikasi Reply, mari jelajahi jenis ukuran dan perangkat yang ada di pasar bagi pengguna untuk menggunakan aplikasi kita.

Kami memiliki ponsel mulai dari 4 inci hingga 7 inci. Lalu kami memiliki tablet, yang berukuran mulai dari tablet kecil hingga tablet dengan ukuran hampir laptop.

Pertama-tama, kita bagi ukuran yang berbeda tersebut menjadi 3 kategori berdasarkan WIndowSizeClass. Kategori ini dipilih secara khusus untuk menyeimbangkan kesederhanaan tata letak, dengan fleksibilitas untuk mengoptimalkan aplikasi Anda untuk kasus yang unik. Class ukuran jendela selalu ditentukan oleh ruang layar yang tersedia untuk aplikasi, yang mungkin bukan seluruh layar fisik untuk multitasking atau segmentasi lainnya.

Distribusi ukuran perangkat menurut WindowSizeClass

WindowStateUtils**.kt**

enum class WindowSize { COMPACT, MEDIUM, EXPANDED }

fun getWindowSizeClass(windowDpSize: DpSize): WindowSize = when {
   windowDpSize.width < 0.dp -> throw IllegalArgumentException("Dp value cannot be negative")
   windowDpSize.width < 600.dp -> WindowSize.COMPACT
   windowDpSize.width < 840.dp -> WindowSize.MEDIUM
   else -> WindowSize.EXPANDED
}

WindowStateUtils.kt menyediakan rememberWindowSizeClass(), yang membantu kita mendapatkan status yang diingat Compose sehingga setiap kali ada perubahan ukuran dalam konfigurasi, hierarki UI akan dirender lagi berdasarkan ukuran baru.

WindowStateUtils.kt

fun Activity.rememberWindowSizeClass(): WindowSize {
   // Get the size (in pixels) of the window
   val windowSize = rememberWindowSize()

   // Convert the window size to [Dp]
   val windowDpSize = with(LocalDensity.current) {
       windowSize.toDpSize()
   }

   // Calculate the window size class
   return getWindowSizeClass(windowDpSize)
}

Yang perlu Anda lakukan untuk mulai mendukung ukuran yang dapat disesuaikan adalah cukup dengan menambahkan rememberWindowSizeClass() ke awal UI Compose dan meneruskannya ke ReplyApp. Sekarang Anda dapat melakukan perubahan pada MainActivity.kt agar terlihat seperti ini.

MainActivity.kt

setContent {
   ReplyTheme(dynamicColor = false, darkTheme = false) {
       val windowSize = rememberWindowSizeClass()
       ReplyApp(windowSize, uiState)
   }
}

Dengan perubahan ini, Anda dapat melihat bahwa ReplyApp memiliki informasi tentang ukuran jendela terbaru untuk menggunakan ruang dengan benar.

4. Menangani status lipat

Anda juga ingin memastikan bahwa aplikasi merespons perubahan status lipat dan bukan hanya ukuran layar. Ada banyak status lipatan, tetapi mulailah dengan ini untuk menargetkan beberapa kasus. Ini sudah ditentukan di class util.

WindowStateUtils.kt

/**
* Information about the posture of the device
*/
sealed interface DevicePosture {
   object NormalPosture : DevicePosture

   data class TableTopPosture(
       val hingePosition: Rect
   ) : DevicePosture

   data class BookPosture(
       val hingePosition: Rect
   ) : DevicePosture
}

Anda ingin memastikan UI bereaksi saat Anda beralih dari posisi terlipat ke posisi terbentang. Anda juga perlu mempertimbangkan BookPosture dan TableTopPosture dengan posisi engsel, karena Anda tidak ingin merender teks atau informasi berguna lainnya di engsel.

Mari kita periksa postur lipat dalam siklus proses aktivitas kita. Tambahkan kode ini dalam metode onCreate() aktivitas sebelum memanggil setContent().

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

    /* Flow of [DevicePosture] that emits every time there is a change in the windowLayoutInfo
    */
   val devicePostureFlow =  WindowInfoTracker.getOrCreate(this).windowLayoutInfo(this)
       .flowWithLifecycle(this.lifecycle)
       .map { layoutInfo ->
           val foldingFeature =
               layoutInfo.displayFeatures.filterIsInstance<FoldingFeature>().firstOrNull()
           when {
               isTableTopPosture(foldingFeature) ->
                   DevicePosture.TableTopPosture(foldingFeature.bounds)
               isBookPosture(foldingFeature) ->
                   DevicePosture.BookPosture(foldingFeature.bounds)
               isSeparating(foldingFeature) ->
                   DevicePosture.Separating(foldingFeature.bounds, foldingFeature.orientation)
               else -> DevicePosture.NormalPosture
           }
       }
       .stateIn(
           scope = lifecycleScope,
           started = SharingStarted.Eagerly,
           initialValue = DevicePosture.NormalPosture
       )

Sekarang Anda cukup mengamati alur postur perangkat sebagai status Compose, yang membantu UI kami bereaksi terhadap perubahan status lipat. Tambahkan perubahan ini ke setContent().

MainActivity.kt

setContent {
   ReplyTheme(dynamicColor = false, darkTheme = false) {
       val devicePosture = devicePostureFlow.collectAsState().value
       ReplyApp(windowSize, devicePosture, uiState)
   }
}

UI Compose sekarang siap bereaksi terhadap perubahan ukuran perangkat dan perubahan status lipat. Anda bisa melanjutkan dari sini untuk mendesain UI untuk berbagai status. Setiap kali ada perubahan status lipat, kita ingin UI bereaksi seperti ini.

Adaptasi UI perangkat foldable

5. Navigasi dinamis

Di bagian terakhir, Anda membuat UI bereaksi terhadap perubahan ukuran, konfigurasi, dan status lipat. Sekarang Anda perlu memahami cara menyesuaikan interaksi pengguna dengan perangkat saat mereka melalui berbagai status.

Mari kita mulai dengan navigasi karena ini adalah hal pertama yang akan berinteraksi dengan pengguna. Perhatikan bahwa pengguna memiliki jenis perangkat yang berbeda secara berbeda. Mari kita lihat beberapa komponen navigasi Material.navi

Navigasi bawah

Navigasi bawah sempurna untuk ukuran kecil, karena kami dengan alami memegang perangkat di mana ibu jari kami dapat dengan mudah menjangkau semua titik sentuh navigasi bawah. Anda menggunakannya setiap kali memiliki ukuran perangkat yang ringkas atau perangkat foldable dalam keadaan terlipat yang ringkas.

Untuk perangkat berukuran sedang, atau sebagian besar ponsel dalam orientasi lanskap, kolom samping navigasi sangat bagus untuk navigasi dan jangkauan yang mudah karena ibu jari kita secara alami berada di kiri atas perangkat. Anda juga dapat menggunakan panel navigasi, serta kolom navigasi, untuk menampilkan informasi selengkapnya.

Panel navigasi memberikan cara mudah untuk melihat informasi mendetail untuk tab navigasi, dan mudah diakses saat Anda menggunakan tablet atau perangkat yang lebih besar. Anda dapat menggunakan panel navigasi bersama dengan rel navigasi, Navigasi bawah dan menggunakan panel navigasi Permanen untuk navigasi tetap bagi perangkat yang sangat lebar.

Sekarang, mari kita beralih di antara berbagai jenis navigasi saat status perangkat dan ukuran berubah, sekaligus mempertahankan interaksi pengguna dan aksesibilitas sebagai intinya.

Mari kita tambahkan navigasi dinamis ke aplikasi. Buka ReplyApp.kt dan tambahkan ini di dalam composable ReplyApp,

ReplyApp.kt

/**
* This will help us select type of navigation depending on window size and
* fold state of the device.
*/
val navigationType: ReplyNavigationType

when (windowSize) {
   WindowSize.COMPACT -> {
       navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
   }
   WindowSize.MEDIUM -> {
       navigationType = ReplyNavigationType.NAVIGATION_RAIL
   }
   WindowSize.EXPANDED -> {
       navigationType = if (foldingDevicePosture is DevicePosture.BookPosture) {
           ReplyNavigationType.NAVIGATION_RAIL
       } else {
           ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
       }
   }
}

Karena panel navigasi berfungsi sebagai UI penampung untuk ReplyAppContent,digabungkan dengan panel navigasi permanen atau modal, bergantung pada navigationType kami seperti ini ,

ReplyApp.kt

if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER) {
   PermanentNavigationDrawer(drawerContent = {                    NavigationDrawerContent(selectedDestination) }) {
       ReplyAppContent(navigationType, contentType, replyHomeUIState)
   }
} else {
   ModalNavigationDrawer(
       drawerContent = {
           NavigationDrawerContent(
               selectedDestination,
               onDrawerClicked = {
                   scope.launch {
                       drawerState.close()
                   }
               }
           )
       },
       drawerState = drawerState
   ) {
       ReplyAppContent(navigationType, contentType, replyHomeUIState,
           onDrawerClicked = {
               scope.launch {
                   drawerState.open()
               }
           }
       )
   }
}

Sekarang Anda memiliki NavigationType dinamis yang dapat digunakan untuk mengubah navigasi setiap kali ada perubahan konfigurasi. Mari kita tambahkan navigationType ke ReplyAppContent() untuk membuat navigasi menjadi dinamis,

ReplyApp.kt

@Composable
fun ReplyAppContent(
   navigationType: ReplyNavigationType,
   contentType: ReplyContentType,
   replyHomeUIState: ReplyHomeUIState,
   onDrawerClicked: () -> Unit = {}
) {
   Row(modifier = Modifier.fillMaxSize()) {
       AnimatedVisibility(visible = navigationType == ReplyNavigationType.NAVIGATION_RAIL) {
           ReplyNavigationRail(
               onDrawerClicked = onDrawerClicked
           )
       }
       Column(modifier = Modifier
           .fillMaxSize()
           .background(MaterialTheme.colorScheme.inverseOnSurface)
       ) {
           // Reply List content

           AnimatedVisibility(visible = navigationType == ReplyNavigationType.BOTTOM_NAVIGATION) {
               ReplyBottomNavigationBar()
           }
       }
   }
}

Menjalankan aplikasi lagi untuk mencoba navigasi dinamis

Saat menjalankan aplikasi lagi, Anda akan melihat bahwa setiap kali konfigurasi layar berubah atau Anda membuka perangkat foldable, navigasi akan berubah ke jenis yang sesuai untuk ukuran tersebut.

Menampilkan perubahan kemampuan beradaptasi untuk berbagai ukuran perangkat.

Selamat, Anda telah mempelajari berbagai jenis navigasi untuk mendukung berbagai jenis ukuran layar dan status.

Di bagian berikutnya, Anda akan menjelajahi cara memanfaatkan area layar yang tersisa, bukan meregangkan tepi item daftar yang sama.

6. Penggunaan ruang layar

Anda dapat melihat di aplikasi bahwa layar direntangkan untuk mengisi ruang yang tersisa, tidak peduli apakah itu tablet kecil, perangkat yang dibentangkan, atau tablet besar. Anda ingin memastikan Anda dapat memanfaatkan ruang layar tersebut untuk menampilkan info selengkapnya kepada pengguna.

Seperti halnya navigationType, Anda akan membuat contentType yang membantu kami memutuskan antara hanya konten daftar, atau untuk menampilkan daftar dan detail konten secara dinamis di perubahan status layar,

ReplyApp.kt

val contentType: ReplyContentType
when (windowSize) {
   WindowSize.COMPACT -> {
       contentType = ReplyContentType.LIST_ONLY
   }
   WindowSize.MEDIUM -> {
       contentType = if (foldingDevicePosture != DevicePosture.NormalPosture) {
           ReplyContentType.LIST_AND_DETAIL
       } else {
           ReplyContentType.LIST_ONLY
       }
   }
   WindowSize.EXPANDED -> {
       contentType = ReplyContentType.LIST_AND_DETAIL
   }
}

Sekarang Anda dapat meneruskan jenis konten ini ke ReplyAppContent, dan setiap kali ada perubahan konfigurasi, beradaptasi dengan tata letak yang benar. Anda juga dapat mempertimbangkan postur lipat dan posisi engsel untuk menentukan penentuan posisi daftar dan tata letak detail sehingga Anda tidak akan melihat konten di posisi engsel,

ReplyApp.kt

@Composable
fun ReplyAppContent(
   navigationType: ReplyNavigationType,
   contentType: ReplyContentType,
   replyHomeUIState: ReplyHomeUIState,
   onDrawerClicked: () -> Unit = {}
) {
   Row(modifier = Modifier.fillMaxSize()) {
       Column(modifier = Modifier
           .fillMaxSize()
           .background(MaterialTheme.colorScheme.inverseOnSurface)
       ) {
           if (contentType == ReplyContentType.LIST_AND_DETAIL) {
               ReplyListAndDetailContent(
                   replyHomeUIState = replyHomeUIState,
                   modifier = Modifier.weight(1f),
               )
           } else {
               ReplyListOnlyContent(replyHomeUIState = replyHomeUIState, modifier = Modifier.weight(1f))
           }
       }
   }
}

Tampilan terakhir ReplyApp setelah menambahkan semua perubahan

Jalankan aplikasi lagi untuk mencoba aplikasi yang sepenuhnya dapat disesuaikan

Jalankan aplikasi lagi dan perhatikan bahwa setiap kali konfigurasi layar berubah, atau kita membuka perangkat foldable, navigasi dan konten layar secara dinamis berubah sebagai respons terhadap status perangkat. Jetpack Compose membuat perubahan semacam ini sangat mudah ditulis dalam pola deklaratif.

Selamat, Anda telah berhasil membuat aplikasi dapat disesuaikan untuk semua jenis status dan ukuran perangkat. Lanjutkan dan cobalah menjalankan aplikasi di perangkat foldable, tablet, atau perangkat seluler lainnya.

Di beberapa bagian berikutnya, Anda akan mempelajari bagaimana perubahan penyesuaian ini juga membantu kami mengatur struktur untuk aksesibilitas.

7. Meningkatkan aksesibilitas

Jangkauan

Jangkauan adalah kemampuan untuk menjelajahi atau menggunakan perangkat tanpa memerlukan posisi tangan yang ekstrem atau mengubah penempatan tangan untuk memulai interaksi dengan aplikasi.

Di aplikasi Reply, di bagian navigasi Dinamis, Anda menambahkan beberapa mode navigasi yang akan digunakan bergantung pada status layar. Komponen material, seperti menu navigasi bawah, kolom navigasi, dan panel navigasi membuat navigasi mudah dijangkau berdasarkan cara kami menyimpan perangkat dengan berbagai faktor bentuk.

Demo jangkauan yang menampilkan kolom navigasi dan panel navigasi untuk berbagai ukuran tablet.

Kami juga menambahkan Faktor bentuk daftar dan detail yang memungkinkan pengguna beralih antar-thread dengan mudah, dan men-scroll-nya di perangkat besar menggunakan tangan kiri dan kanan tanpa mengubah penempatan.

Kontras warna

Aplikasi Reply mendukung tema dinamis untuk Android 12 dan yang lebih baru, tempat skema warna dibuat melalui pemilihan wallpaper dan setelan penyesuaian lainnya. Produk yang menggunakan warna dinamis memenuhi persyaratan aksesibilitas karena kombinasi algoritme yang dapat dialami pengguna akhir dirancang untuk memenuhi standar tersebut.

Untuk mempelajari lebih lanjut, baca Warna dinamis.

Skema warna Material 3 untuk mode terang dan gelap.

Untuk aplikasi ini, kami juga menggunakan skema warna Material 3 yang sudah didesain untuk memenuhi standar aksesibilitas kontras warna. Sistem palet tonal sangat penting untuk membuat skema warna apa pun dapat diakses secara default.

Demo kontras warna dengan tema material 3.

Menggabungkan warna berdasarkan nada suara, bukan nilai hex atau hue, adalah salah satu sistem utama yang membuat output warna dapat diakses. Anda dapat membuat skema warna Material 3 lengkap dengan memilih kumpulan warna primer, sekunder, dan tersier yang tepat, serta menggunakan pembuat tema Material untuk membuat skema warna Material 3 baik untuk variasi terang maupun gelap. Variasi yang dihasilkan sudah mematuhi standar aksesibilitas untuk kontras warna.

Di Android 11 dan versi yang lebih lama, jika tema dinamis tidak tersedia, kita akan kembali ke skema warna Material 3 tetap yang dibuat dengan builder tema Material.

Anda dapat mencoba tema warna baru dengan menggunakan Pembuat tema material.

Anda dapat menempatkan warna yang dihasilkan secara langsung di file ui/theme/Color.kt untuk melihat cara kerjanya.

8 Selamat

Selamat, Anda berhasil menyelesaikan codelab ini dan mempelajari cara mendesain aplikasi yang mudah disesuaikan dan dapat diakses menggunakan Jetpack Compose.

Anda telah mempelajari cara memeriksa ukuran dan status lipatan perangkat serta mengupdate UI aplikasi, navigasi, dan fungsi lainnya sebagaimana mestinya. Anda juga telah mempelajari cara memanfaatkan skema warna dan tipografi Material 3 untuk meningkatkan pengalaman pengguna dan aksesibilitas.

Apa selanjutnya?

Lihat codelab lain di jalur Compose

Aplikasi contoh

  • Aplikasi contoh adalah kumpulan banyak aplikasi yang menggabungkan praktik terbaik yang dijelaskan dalam codelab.

Dokumen referensi