Mengubah Ukuran Aplikasi Android

1. Pengantar

Ekosistem perangkat Android selalu berkembang. Dari masa awal keyboard hardware bawaan hingga lanskap modern perangkat flippable, perangkat foldable, tablet, jendela bentuk bebas yang dapat diubah ukurannya — aplikasi Android belum pernah sebelumnya berjalan di berbagai jenis perangkat seperti saat ini.

Meskipun hal ini baik bagi developer, pengoptimalan aplikasi tertentu diperlukan untuk memenuhi ekspektasi kegunaan dan memberikan pengalaman pengguna yang luar biasa di berbagai ukuran layar. Daripada menargetkan setiap perangkat baru satu per satu, UI yang responsif/adaptif serta arsitektur yang tangguh dapat membantu membuat aplikasi Anda terlihat dan berfungsi dengan baik di perangkat (dengan ukuran dan bentuk apa pun) yang digunakan oleh pengguna Anda saat ini dan pada masa mendatang.

Pengenalan lingkungan Android bentuk bebas yang dapat diubah ukurannya adalah cara terbaik untuk menguji UI responsif/adaptif Anda agar siap digunakan di perangkat apa pun. Codelab ini akan memandu Anda dalam memahami implikasi pengubahan ukuran serta menerapkan beberapa praktik terbaik untuk mengubah ukuran aplikasi dengan cara yang andal dan mudah.

Yang akan Anda bangun

Anda akan mempelajari implikasi pengubahan ukuran bentuk bebas dan mengoptimalkan aplikasi Android untuk mendemonstrasikan praktik terbaik pengubahan ukuran. Aplikasi Anda akan:

Memiliki manifes yang kompatibel

  • Menghapus batasan yang mencegah aplikasi diubah ukurannya secara bebas

Mempertahankan status saat diubah ukurannya

  • Mempertahankan status UI saat diubah ukurannya menggunakan rememberSaveable
  • Menghindari duplikasi pekerjaan latar belakang yang tidak perlu untuk menginisialisasi UI

Yang akan Anda butuhkan

  1. Pengetahuan membuat aplikasi Android dasar
  2. Pengetahuan tentang ViewModel dan Status dalam Compose
  3. Perangkat pengujian yang mendukung pengubahan ukuran jendela bentuk bebas seperti salah satu dari berikut ini:

Jika Anda mengalami masalah (bug kode, kesalahan gramatikal, susunan kata yang tidak jelas, dll.) saat mengerjakan codelab ini, laporkan masalah tersebut melalui link Laporkan kesalahan di pojok kiri bawah codelab.

2. Memulai

Clone repositori dari GitHub.

git clone https://github.com/android/large-screen-codelabs/

...atau download file zip repositori, lalu ekstrak file tersebut

Impor Project

  • Buka Android Studio
  • Pilih Import Project atau File->New->Import Project
  • Buka tempat Anda meng-clone atau mengekstrak project
  • Buka folder resizing.
  • Buka project di folder start. Folder ini berisi kode awal.

Coba Aplikasi

  • Bangun dan jalankan aplikasi
  • Coba ubah ukuran aplikasi

Bagaimana menurut Anda?

Bergantung pada dukungan kompatibilitas perangkat pengujian Anda, Anda mungkin menyadari bahwa pengalaman pengguna tidak optimal. Aplikasi tidak dapat diubah ukurannya dan tetap dalam rasio aspek awalnya. Apa yang terjadi?

Batasan manifes

Jika Anda melihat file AndroidManifest.xml di aplikasi, Anda dapat melihat bahwa ada beberapa batasan tambahan yang mencegah aplikasi berperilaku baik dalam lingkungan pengubahan ukuran jendela bentuk bebas.

AndroidManifest.xml

            android:maxAspectRatio="1.4"
            android:resizeableActivity="false"
            android:screenOrientation="portrait">

Coba hapus tiga baris bermasalah ini dari manifes Anda, bangun ulang aplikasi, lalu coba lagi di perangkat pengujian Anda. Anda akan melihat bahwa aplikasi tidak lagi dibatasi dalam pengubahan ukuran bentuk bebas. Menghapus batasan seperti ini dari manifes Anda adalah langkah penting dalam mengoptimalkan aplikasi untuk pengubahan ukuran jendela bentuk bebas.

3. Perubahan konfigurasi pengubahan ukuran

Saat jendela aplikasi Anda diubah ukurannya, Konfigurasi aplikasi akan diperbarui. Pembaruan ini memiliki implikasi untuk aplikasi Anda. Memahami dan mengantisipasi hal ini dapat membantu memberi pengguna Anda pengalaman yang luar biasa. Perubahan yang paling jelas yaitu pada lebar dan tinggi jendela aplikasi Anda, tetapi perubahan ini juga memiliki implikasi untuk rasio aspek dan orientasi.

Mengamati perubahan konfigurasi

Untuk melihat sendiri perubahan yang terjadi di aplikasi yang dibangun dengan sistem tampilan Android, Anda dapat mengganti View.onConfigurationChanged. Di Jetpack Compose, kita memiliki akses ke LocalConfiguration.current, yang otomatis diperbarui setiap kali View.onConfigurationChanged dipanggil.

Untuk melihat perubahan konfigurasi ini di aplikasi contoh Anda, tambahkan composable ke aplikasi yang menampilkan nilai dari LocalConfiguration.current, atau buat project contoh baru dengan composable tersebut. Contoh UI untuk melihat perubahan ini kira-kira seperti ini:

val configuration = LocalConfiguration.current
val isPortrait = configuration.orientation ==
    Configuration.ORIENTATION_PORTRAIT
val screenLayoutSize =
        when (configuration.screenLayout and
                Configuration.SCREENLAYOUT_SIZE_MASK) {
            SCREENLAYOUT_SIZE_SMALL -> "SCREENLAYOUT_SIZE_SMALL"
            SCREENLAYOUT_SIZE_NORMAL -> "SCREENLAYOUT_SIZE_NORMAL"
            SCREENLAYOUT_SIZE_LARGE -> "SCREENLAYOUT_SIZE_LARGE"
            SCREENLAYOUT_SIZE_XLARGE -> "SCREENLAYOUT_SIZE_XLARGE"
            else -> "undefined value"
        }
Column(
    horizontalAlignment = Alignment.CenterHorizontally,
    modifier = Modifier.fillMaxWidth()
) {
    Text("screenWidthDp: ${configuration.screenWidthDp}")
    Text("screenHeightDp: ${configuration.screenHeightDp}")
    Text("smallestScreenWidthDp: ${configuration.smallestScreenWidthDp}")
    Text("orientation: ${if (isPortrait) "portrait" else "landscape"}")
    Text("screenLayout SIZE: $screenLayoutSize")
}

Anda dapat melihat contoh implementasi di folder project observing-configuration-changes. Coba tambahkan ini ke UI aplikasi Anda, jalankan di perangkat pengujian, dan amati update UI saat konfigurasi aplikasi Anda berubah.

saat aplikasi diubah ukurannya, informasi konfigurasi yang berubah ditampilkan di antarmuka aplikasi secara real time

Perubahan konfigurasi aplikasi ini memungkinkan Anda menyimulasikan perubahan cepat dari tampilan ekstrem seperti layar terpisah pada handset kecil ke layar penuh pada tablet atau desktop. Cara ini tidak hanya bagus untuk menguji tata letak aplikasi Anda di berbagai jenis layar, tetapi juga memungkinkan Anda menguji seberapa baik aplikasi dapat menangani peristiwa perubahan konfigurasi yang cepat.

4. Mencatat Peristiwa siklus proses aktivitas

Implikasi lain dari pengubahan ukuran jendela bentuk bebas untuk aplikasi Anda adalah adanya berbagai perubahan siklus proses Activity yang akan terjadi pada aplikasi Anda. Untuk melihat perubahan ini secara real time, tambahkan observer siklus proses ke metode onCreate, dan catat setiap peristiwa siklus proses baru dengan mengganti onStateChanged.

lifecycle.addObserver(object : LifecycleEventObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        Log.d("resizing-codelab-lifecycle", "$event was called")
    }
})

Dengan adanya pencatatan ini, jalankan aplikasi Anda di perangkat pengujian lagi, dan lihat logcat saat Anda mencoba meminimalkan aplikasi dan mengembalikannya lagi ke latar depan.

Amati bahwa aplikasi Anda dijeda saat diminimalkan, dan dilanjutkan lagi saat dikembalikan ke latar depan. Hal ini memiliki implikasi bagi aplikasi Anda yang akan dibahas di bagian berikutnya dalam codelab ini yang berfokus pada kontinuitas.

Logcat menampilkan metode siklus proses aktivitas yang dipanggil saat pengubahan ukuran

Sekarang lihat Logcat untuk mengetahui callback siklus proses aktivitas mana yang dipanggil saat Anda mengubah ukuran aplikasi dari yang terkecil hingga terbesar

Bergantung pada perangkat pengujiannya, Anda mungkin mengamati perilaku yang berbeda-beda, tetapi Anda mungkin melihat bahwa aktivitas Anda dihancurkan dan dibuat ulang ketika ukuran jendela aplikasi diubah secara signifikan, tetapi tidak ketika diubah sedikit. Hal ini karena, di API 24+, hanya perubahan ukuran yang signifikan yang menimbulkan Activity dibuat ulang.

Anda telah melihat beberapa perubahan konfigurasi umum yang biasa terjadi di lingkungan windowing bentuk bebas, tetapi ada perubahan lain yang perlu diketahui. Misalnya, jika ada monitor eksternal yang terhubung ke perangkat pengujian, Anda dapat melihat bahwa Activity dihancurkan dan dibuat ulang untuk mempertimbangkan perubahan konfigurasi seperti kepadatan layar.

Untuk meminimalkan beberapa kompleksitas yang terkait dengan perubahan konfigurasi, gunakan level API yang lebih tinggi seperti WindowSizeClass untuk menerapkan UI adaptif Anda. (Lihat juga Mendukung berbagai ukuran layar.)

5. Kontinuitas - Mempertahankan status internal composable saat aplikasi diubah ukurannya

Di bagian sebelumnya, Anda telah melihat beberapa perubahan konfigurasi yang akan diterapkan di aplikasi Anda dalam lingkungan pengubahan ukuran jendela bentuk bebas. Di bagian ini, Anda akan mempertahankan status UI aplikasi Anda selama perubahan ini.

Mulailah dengan memperluas fungsi composable NavigationDrawerHeader (terdapat di ReplyHomeScreen.kt) untuk menampilkan alamat email saat diklik.

@Composable
private fun NavigationDrawerHeader(
    modifier: Modifier = Modifier
) {
    var showDetails by remember { mutableStateOf(false) }
    Column(
        modifier = modifier.clickable {
                showDetails = !showDetails
            }
    ) {


        Row(
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            ReplyLogo(
                modifier = Modifier
                    .size(dimensionResource(R.dimen.reply_logo_size))
            )
            ReplyProfileImage(
                drawableResource = LocalAccountsDataProvider
                    .userAccount.avatar,
                description = stringResource(id = R.string.profile),
                modifier = Modifier
                    .size(dimensionResource(R.dimen.profile_image_size))
            )
        }
        AnimatedVisibility (showDetails) {
            Text(
                text = stringResource(id = LocalAccountsDataProvider
                        .userAccount.email),
                style = MaterialTheme.typography.labelMedium,
                modifier = Modifier
                    .padding(
                        start = dimensionResource(
                            R.dimen.drawer_padding_header),
                        end = dimensionResource(
                            R.dimen.drawer_padding_header),
                        bottom = dimensionResource(
                            R.dimen.drawer_padding_header)
                ),


            )
        }
    }
}

Setelah Anda menambahkan header yang dapat diluaskan ke aplikasi Anda,

  1. jalankan aplikasi di perangkat pengujian Anda:
  2. ketuk header untuk meluaskannya
  3. coba ubah ukuran jendela

Anda akan melihat bahwa header kehilangan statusnya ketika diubah ukurannya secara signifikan.

Header di panel navigasi aplikasi akan meluas saat diketuk, tetapi header tersebut akan menciut setelah aplikasi diubah ukurannya

Status UI hilang karena remember membantu Anda mempertahankan status di seluruh rekomposisi, tetapi tidak di seluruh pembuatan ulang aktivitas atau proses. Penggunaan pengangkatan status umum dilakukan dan hal ini dapat memindahkan status ke pemanggil composable agar composable menjadi stateless, sehingga masalah ini dapat dihindari sepenuhnya. Dengan demikian, Anda dapat menggunakan remember di beberapa tempat saat mempertahankan status elemen UI di dalam fungsi composable.

Untuk mengatasi masalah ini, ganti remember dengan rememberSaveable. Hal ini berfungsi karena rememberSaveable menyimpan dan memulihkan nilai yang diingat ke savedInstanceState. Ubah remember menjadi rememberSaveable, jalankan aplikasi Anda di perangkat pengujian, lalu coba ubah ukuran aplikasi lagi. Anda akan melihat bahwa status header yang dapat diluaskan dipertahankan selama pengubahan ukuran, sebagaimana mestinya.

6. Menghindari duplikasi pekerjaan latar belakang yang tidak perlu

Anda telah mengetahui cara menggunakan rememberSaveable untuk mempertahankan status UI internal composable melalui perubahan konfigurasi yang sering terjadi akibat pengubahan ukuran jendela bentuk bebas. Namun, aplikasi harus sering mengangkat logika dan status UI dari composable. Memindahkan kepemilikan status ke ViewModel adalah salah satu cara terbaik untuk mempertahankan status selama pengubahan ukuran. Saat Anda mengangkat status menjadi ViewModel, Anda mungkin mengalami masalah dengan pekerjaan latar belakang yang berjalan lama seperti panggilan jaringan atau akses sistem file berat yang diperlukan untuk melakukan inisialisasi layar Anda.

Untuk melihat contoh jenis masalah yang mungkin Anda hadapi, tambahkan laporan log ke metode initializeUIState di ReplyViewModel.

fun initializeUIState() {
    Log.d("resizing-codelab", "initializeUIState() called in the viewmodel")
    val mailboxes: Map<MailboxType, List<Email>> =
        LocalEmailsDataProvider.allEmails.groupBy { it.mailbox }
    _uiState.value =
        ReplyUiState(
            mailboxes = mailboxes,
            currentSelectedEmail = mailboxes[MailboxType.Inbox]?.get(0)
                ?: LocalEmailsDataProvider.defaultEmail
        )
}

Sekarang, jalankan aplikasi di perangkat pengujian Anda, dan coba ubah ukuran jendela aplikasi Anda beberapa kali.

Saat memeriksa Logcat, Anda akan melihat bahwa aplikasi Anda telah menjalankan metode inisialisasi beberapa kali. Hal ini bisa menjadi masalah bagi pekerjaan yang hanya ingin Anda jalankan sekali untuk menginisialisasi UI Anda. Panggilan jaringan tambahan, I/O file, atau pekerjaan lain dapat menghambat performa perangkat, dan menyebabkan masalah lain yang tidak diinginkan.

Untuk menghindari pekerjaan latar belakang yang tidak perlu, hapus panggilan ke initializeUIState() dari metode onCreate() aktivitas Anda. Sebagai gantinya, lakukan inisialisasi data di metode init dari ViewModel. Hal ini memastikan bahwa metode inisialisasi hanya berjalan sekali, saat ReplyViewModel pertama kali dibuat instance-nya:

init {
    initializeUIState()
}

Coba jalankan aplikasi lagi, dan Anda dapat melihat simulasi tugas inisialisasi yang tidak diperlukan hanya berjalan satu kali, berapa kali pun Anda mengubah ukuran jendela aplikasi. Hal ini karena ViewModel dapat melampaui siklus proses Activity. Dengan menjalankan kode inisialisasi hanya sekali pada saat pembuatan ViewModel, kita memisahkannya dari pembuatan ulang Activity dan mencegah pekerjaan yang tidak perlu. Jika hal ini ternyata merupakan panggilan server yang mahal atau operasi I/O file yang berat untuk menginisialisasi UI, Anda akan menghemat banyak resource sekaligus meningkatkan pengalaman pengguna.

7. SELAMAT!

Anda berhasil! Kerja bagus! Anda telah menerapkan beberapa praktik terbaik guna memungkinkan aplikasi Android diubah ukurannya dengan baik di ChromeOS dan lingkungan multi-aplikasi serta multilayar lainnya.

Contoh Kode Sumber

Clone repositori dari GitHub

git clone https://github.com/android/large-screen-codelabs/

...atau download file zip repositori, lalu ekstrak file tersebut