Cookbook layar besar

Android menyediakan semua bahan untuk aplikasi layar besar bintang lima. Urutan langkah dalam cookbook ini memilih dan menggabungkan berbagai bahan pilihan untuk memecahkan masalah pengembangan tertentu. Setiap urutan langkah mencakup praktik terbaik, contoh kode berkualitas, dan petunjuk langkah demi langkah agar Anda dapat menjadi koki terbaik untuk perangkat layar besar.

Rating bintang

Urutan langkah diberi rating bintang berdasarkan seberapa baik kesesuaiannya dengan panduan Kualitas aplikasi perangkat layar besar.

Rating bintang lima Memenuhi kriteria Tingkat 1, Memberikan pengalaman yang dirancang khusus untuk perangkat layar besar
Rating bintang empat Memenuhi kriteria Tingkat 2, Dioptimalkan untuk perangkat layar besar
Rating bintang tiga Memenuhi kriteria Tingkat 3, Siap digunakan di perangkat layar besar
Rating bintang dua Memberikan beberapa kemampuan layar besar, tetapi tidak sesuai dengan pedoman kualitas aplikasi perangkat layar besar
Rating bintang satu Memenuhi kebutuhan kasus penggunaan tertentu, tetapi tidak mendukung layar besar dengan optimal

Dukungan kamera Chromebook

Rating bintang tiga

Pastikan pengguna Chromebook dapat menemukan aplikasi Anda di Google Play.

Jika aplikasi kamera Anda dapat berfungsi cukup dengan fitur kamera dasar, pastikan app store tidak mencegah pengguna Chromebook menginstal aplikasi hanya karena Anda tidak sengaja menentukan fitur kamera lanjutan yang tersedia di ponsel kelas atas.

Chromebook memiliki kamera depan bawaan (menghadap pengguna) yang berfungsi dengan baik untuk melakukan konferensi video, snapshot, dan penggunaan lain. Namun, tidak semua Chromebook memiliki kamera belakang (menghadap dunia), dan sebagian besar kamera yang menghadap pengguna di Chromebook tidak mendukung fokus otomatis atau flash.

Praktik terbaik

Aplikasi kamera serbaguna mendukung semua perangkat terlepas dari konfigurasi kamera—perangkat dengan kamera depan, kamera belakang, kamera eksternal yang terhubung dengan USB.

Guna memastikan app store menyediakan aplikasi Anda untuk sebagian besar perangkat, selalu deklarasikan semua fitur kamera yang digunakan oleh aplikasi Anda dan tunjukkan secara eksplisit apakah fitur tersebut diperlukan atau tidak.

Bahan

  • Izin CAMERA: Memberi aplikasi akses ke kamera perangkat
  • Elemen manifes <uses-feature>: Memberi tahu app store tentang fitur yang digunakan oleh aplikasi
  • Atribut required: Menunjukkan kepada app store apakah aplikasi dapat berfungsi tanpa fitur yang ditentukan

Langkah

Ringkasan

Deklarasikan izin CAMERA. Deklarasikan fitur kamera yang menyediakan dukungan kamera dasar. Tentukan apakah setiap fitur diperlukan.

1. Mendeklarasikan izin CAMERA

Tambahkan izin berikut ke manifes aplikasi:

<uses-permission android:name="android.permission.CAMERA" />
2. Mendeklarasikan fitur kamera dasar

Tambahkan fitur berikut ke manifes aplikasi:

<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. Menentukan apakah setiap fitur diperlukan

Setel android:required="false" untuk fitur android.hardware.camera.any guna mengaktifkan akses ke aplikasi Anda dengan perangkat yang memiliki jenis kamera bawaan atau eksternal—atau tidak memiliki kamera sama sekali.

Untuk fitur lainnya, setel android:required="false" untuk memastikan perangkat seperti Chromebook yang tidak memiliki kamera belakang, fokus otomatis, atau flash dapat mengakses aplikasi Anda di app store.

Hasil

Pengguna Chromebook dapat mendownload dan menginstal aplikasi Anda dari Google Play dan app store lainnya. Selain itu, perangkat dengan dukungan kamera berfitur lengkap, seperti ponsel, tidak akan dibatasi fungsi kameranya.

Dengan menyetel fitur kamera yang didukung oleh aplikasi Anda secara eksplisit dan menentukan fitur yang diperlukan aplikasi, Anda telah membuat aplikasi tersedia di sebanyak mungkin perangkat.

Referensi tambahan

Untuk informasi selengkapnya, lihat Fitur hardware kamera dalam dokumentasi <uses-feature>.

Orientasi aplikasi dibatasi di ponsel, tetapi tidak di perangkat layar besar

Rating bintang dua

Aplikasi Anda berfungsi dengan baik pada ponsel dalam orientasi potret, sehingga Anda membatasi aplikasi ke orientasi potret. Namun, Anda melihat peluang untuk melakukan lebih banyak hal pada perangkat layar besar dalam orientasi lanskap.

Bagaimana cara melakukan keduanya—membatasi aplikasi ke orientasi potret di layar kecil, tetapi mengaktifkan orientasi lanskap pada perangkat layar besar?

Praktik terbaik

Aplikasi terbaik menghormati preferensi pengguna seperti orientasi perangkat.

Panduan Kualitas aplikasi perangkat layar besar merekomendasikan agar aplikasi mendukung semua konfigurasi perangkat, termasuk orientasi potret dan lanskap, mode multi-aplikasi, serta status terlipat dan terbentang perangkat foldable. Aplikasi harus mengoptimalkan tata letak dan antarmuka pengguna untuk berbagai konfigurasi, dan aplikasi harus menyimpan serta memulihkan status selama perubahan konfigurasi.

Urutan langkah ini adalah tindakan sementara—dukungan kecil untuk perangkat layar besar. Gunakan urutan langkah ini hingga Anda dapat meningkatkan kualitas aplikasi untuk memberikan dukungan penuh bagi semua konfigurasi perangkat.

Bahan

  • screenOrientation: Setelan manifes aplikasi yang memungkinkan Anda menentukan respons aplikasi terhadap perubahan orientasi perangkat
  • Jetpack WindowManager: Kumpulan library yang memungkinkan Anda menentukan ukuran dan rasio aspek jendela aplikasi; kompatibel dengan versi lama hingga level API 14
  • Activity#setRequestedOrientation(): Metode yang dapat Anda gunakan untuk mengubah orientasi aplikasi saat runtime

Langkah

Ringkasan

Buat aplikasi agar mampu menangani perubahan orientasi secara default di manifes aplikasi. Saat runtime, tentukan ukuran jendela aplikasi. Jika jendela aplikasi kecil, batasi orientasi aplikasi dengan mengganti setelan orientasi manifes.

1. Menentukan setelan orientasi dalam manifes aplikasi

Anda dapat menghindari mendeklarasikan elemen screenOrientation dari manifes aplikasi (jika orientasi default adalah unspecified) atau menyetel orientasi layar ke fullUser. Jika pengguna belum mengunci rotasi berbasis sensor, aplikasi Anda akan mendukung semua orientasi perangkat.

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

Perbedaan antara penggunaan unspecified dan fullUser kecil, tetapi penting. Jika Anda tidak mendeklarasikan nilai screenOrientation, sistem akan memilih orientasi, dan kebijakan yang digunakan sistem untuk menentukan orientasi mungkin berbeda dari satu perangkat ke perangkat lainnya. Di sisi lain, menentukan fullUser lebih cocok dengan perilaku yang ditentukan pengguna untuk perangkat: jika pengguna telah mengunci rotasi berbasis sensor, aplikasi akan mengikuti preferensi pengguna; jika tidak, sistem memungkinkan empat orientasi layar yang memungkinkan (potret, lanskap, potret terbalik, atau lanskap terbalik). Lihat android:screenOrientation.

2. Menentukan ukuran layar

Dengan manifes yang disetel untuk mendukung semua orientasi yang diizinkan pengguna, Anda dapat menentukan orientasi aplikasi secara terprogram berdasarkan ukuran layar.

Tambahkan library Jetpack WindowManager ke file build.gradle atau build.gradle.kts modul:

Kotlin

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Groovy

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

Gunakan metode Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics() untuk mendapatkan ukuran layar perangkat sebagai objek WindowMetrics. Metrik jendela dapat dibandingkan dengan class ukuran jendela untuk menentukan kapan harus membatasi orientasi.

Class ukuran jendela menyediakan titik henti sementara antara layar kecil dan besar.

Gunakan titik henti sementara WindowWidthSizeClass#COMPACT dan WindowHeightSizeClass#COMPACT untuk menentukan ukuran layar:

Kotlin

/** Determines whether the device has a compact screen. **/
fun compactScreen() : Boolean {
    val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
    val width = metrics.bounds.width()
    val height = metrics.bounds.height()
    val density = resources.displayMetrics.density
    val windowSizeClass = WindowSizeClass.compute(width/density, height/density)

    return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT ||
        windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT
}

Java

/** Determines whether the device has a compact screen. **/
private boolean compactScreen() {
    WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this);
    int width = metrics.getBounds().width();
    int height = metrics.getBounds().height();
    float density = getResources().getDisplayMetrics().density;
    WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density);
    return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT ||
                windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT;
}
    Catatan:
  • Contoh di atas diimplementasikan sebagai metode aktivitas; sehingga aktivitas tersebut dihilangkan referensinya sebagai this dalam argumen computeMaximumWindowMetrics().
  • Metode computeMaximumWindowMetrics() digunakan sebagai pengganti computeCurrentWindowMetrics() karena aplikasi dapat diluncurkan dalam mode multi-aplikasi, yang mengabaikan setelan orientasi layar. Anda tidak perlu menentukan ukuran jendela aplikasi dan mengganti setelan orientasi, kecuali jika jendela aplikasi adalah keseluruhan layar perangkat.

Lihat WindowManager untuk mengetahui petunjuk tentang mendeklarasikan dependensi agar metode computeMaximumWindowMetrics() tersedia di aplikasi Anda.

3. Mengganti setelan manifes aplikasi

Setelah menentukan bahwa perangkat memiliki ukuran layar yang kecil, Anda dapat memanggil Activity#setRequestedOrientation() untuk mengganti setelan screenOrientation manifes:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    val container: ViewGroup = binding.container

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(object : View(this) {
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
            requestedOrientation = if (compactScreen())
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
                ActivityInfo.SCREEN_ORIENTATION_FULL_USER
        }
    })
}

Java

@Override
protected void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstanceState);
    if (compactScreen()) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
    }
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    ViewGroup container = binding.container;

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(new View(this) {
        @Override
        protected void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            if (compactScreen()) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
            }
        }
    });
}

Dengan menambahkan logika ke metode onCreate() dan View.onConfigurationChanged(), Anda dapat memperoleh metrik jendela maksimum dan mengganti setelan orientasi setiap kali aktivitas diubah ukurannya atau dipindahkan di antara tampilan, seperti setelah rotasi perangkat atau saat perangkat foldable dilipat atau dibentangkan. Untuk informasi selengkapnya tentang kapan perubahan konfigurasi terjadi dan kapan perubahan tersebut menyebabkan pembuatan ulang aktivitas, lihat Menangani perubahan konfigurasi

Hasil

Aplikasi Anda akan tetap dalam orientasi potret di layar kecil, apa pun rotasi perangkatnya. Pada perangkat layar besar, aplikasi akan mendukung orientasi lanskap dan potret.

Referensi tambahan

Untuk bantuan dalam melakukan upgrade aplikasi Anda agar selalu mendukung semua konfigurasi perangkat, lihat artikel berikut:

Pemutaran media dijeda dan dilanjutkan dengan Spasi keyboard eksternal

Rating bintang empat

Pengoptimalan layar besar mencakup kemampuan untuk menangani input keyboard eksternal, seperti menanggapi saat Spasi ditekan untuk menjeda atau melanjutkan pemutaran video dan media lainnya. Fitur ini sangat berguna untuk tablet, yang sering terhubung ke keyboard eksternal, dan Chromebook, yang biasanya dilengkapi dengan keyboard eksternal tetapi dapat digunakan dalam mode tablet.

Jika media adalah satu-satunya elemen jendela (seperti pemutaran video layar penuh), tanggapi peristiwa penekanan tombol di tingkat aktivitas atau, di Jetpack Compose, pada tingkat layar.

Praktik terbaik

Setiap kali aplikasi Anda memutar file media, pengguna harus dapat menjeda dan melanjutkan pemutaran dengan menekan Spasi pada keyboard fisik.

Bahan

Compose

  • onPreviewKeyEvent: Modifier yang memungkinkan komponen menangkap peristiwa tombol hardware saat komponen tersebut (atau salah satu turunannya) difokuskan.
  • onKeyEvent: Serupa dengan onPreviewKeyEvent, Modifier ini memungkinkan komponen untuk menangkap peristiwa tombol hardware saat komponen tersebut (atau salah satu turunannya) difokuskan.

View

  • onKeyUp(): Dipanggil saat tombol dirilis dan tidak ditangani oleh tampilan dalam aktivitas.

Langkah

Ringkasan

Aplikasi berbasis tampilan dan aplikasi berbasis Jetpack Compose merespons penekanan tombol keyboard dengan cara yang sama: aplikasi harus memproses peristiwa penekanan tombol, memfilter peristiwa, dan merespons penekanan tombol yang dipilih, seperti penekanan tombol Spasi.

1. Memproses peristiwa keyboard

View

Dalam aktivitas di aplikasi Anda, ganti metode onKeyUp():

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
    ...
}

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    ...
}

Metode ini dipanggil setiap kali tombol yang ditekan dilepas, sehingga terpicu hanya satu kali untuk setiap penekanan tombol.

Compose

Dengan Jetpack Compose, Anda dapat memanfaatkan pengubah onPreviewKeyEvent atau onKeyEvent dalam layar yang mengelola penekanan tombol:

Column(modifier = Modifier.onPreviewKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp) {
        ...
    }
    ...
})

atau

Column(modifier = Modifier.onKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp) {
        ...
    }
    ...
})

2. Memfilter penekanan tombol Spasi

Di dalam metode onKeyUp() atau metode pengubah Compose onPreviewKeyEvent dan onKeyEvent, filter KeyEvent.KEYCODE_SPACE untuk mengirim peristiwa yang benar ke komponen media:

View

Kotlin

if (keyCode == KeyEvent.KEYCODE_SPACE) {
    togglePlayback()
    return true
}
return false

Java

if (keyCode == KeyEvent.KEYCODE_SPACE) {
    togglePlayback();
    return true;
}
return false;

Compose

Column(modifier = Modifier.onPreviewKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
        ...
    }
    ...
})

atau

Column(modifier = Modifier.onKeyEvent { event ->
    if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
        ...
    }
    ...
})

Hasil

Aplikasi Anda kini dapat merespons penekanan tombol Spasi untuk menjeda dan melanjutkan video atau media lainnya.

Referensi tambahan

Untuk mempelajari peristiwa keyboard lebih lanjut dan cara mengelolanya, lihat Menangani input keyboard.

Penolakan telapak tangan stilus

Rating bintang lima

Stilus dapat menjadi alat yang sangat produktif dan kreatif di layar besar. Namun, saat menggambar, menulis, atau berinteraksi dengan aplikasi menggunakan stilus, terkadang pengguna menyentuh layar dengan telapak tangan mereka. Peristiwa sentuh dapat dilaporkan ke aplikasi Anda sebelum sistem mengenali dan menolak peristiwa sebagai sentuhan telapak tangan yang tidak disengaja.

Praktik terbaik

Aplikasi Anda harus mengidentifikasi peristiwa sentuh yang tidak relevan dan mengabaikannya. Android membatalkan sentuhan telapak tangan dengan mengirim objek MotionEvent. Periksa objek untuk ACTION_CANCEL atau ACTION_POINTER_UP dan FLAG_CANCELED guna menentukan apakah akan menolak gestur yang disebabkan oleh sentuhan telapak tangan.

Bahan

  • MotionEvent: Mewakili peristiwa sentuh dan gerakan. Berisi informasi yang diperlukan untuk menentukan apakah suatu peristiwa harus diabaikan.
  • OnTouchListener#onTouch(): Menerima objek MotionEvent.
  • MotionEvent#getActionMasked(): Menampilkan tindakan yang terkait dengan peristiwa gerakan.
  • ACTION_CANCEL: Konstanta MotionEvent yang menunjukkan gestur harus diurungkan.
  • ACTION_POINTER_UP: Konstanta MotionEvent yang menunjukkan pointer selain pointer pertama telah naik (yaitu, telah melepaskan kontak dengan layar perangkat).
  • FLAG_CANCELED: Konstanta MotionEvent yang menunjukkan bahwa pointer naik menyebabkan peristiwa sentuh yang tidak disengaja. Menambahkan peristiwa ACTION_POINTER_UP dan ACTION_CANCEL di Android 13 (level API 33) dan versi yang lebih baru.

Langkah

Ringkasan

Periksa objek MotionEvent yang dikirim ke aplikasi Anda. Gunakan MotionEvent API untuk menentukan karakteristik peristiwa:

  • Peristiwa pointer tunggal — Periksa ACTION_CANCEL. Di Android 13 dan yang lebih tinggi, periksa juga FLAG_CANCELED.
  • Peristiwa multi-pointer — Di Android 13 dan yang lebih tinggi, periksa ACTION_POINTER_UP dan FLAG_CANCELED.

Respons peristiwa ACTION_CANCEL dan ACTION_POINTER_UP/FLAG_CANCELED.

1. Mendapatkan objek peristiwa gerakan

Tambahkan OnTouchListener ke aplikasi Anda:

Kotlin

val myView = findViewById<View>(R.id.myView).apply {
    setOnTouchListener { view, event ->
        // Process motion event.
    }
}

Java

View myView = findViewById(R.id.myView);
myView.setOnTouchListener( (view, event) -> {
    // Process motion event.
});
2. Menentukan tindakan dan tanda peristiwa

Periksa ACTION_CANCEL, yang menunjukkan peristiwa pointer tunggal di semua level API. Di Android 13 dan yang lebih baru, lihat ACTION_POINTER_UP untuk FLAG_CANCELED.

Kotlin

val myView = findViewById<View>(R.id.myView).apply {
    setOnTouchListener { view, event ->
        when (event.actionMasked) {
            MotionEvent.ACTION_CANCEL -> {
                //Process canceled single-pointer motion event for all SDK versions.
            }
            MotionEvent.ACTION_POINTER_UP -> {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
                   (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) {
                    //Process canceled multi-pointer motion event for Android 13 and higher.
                }
            }
        }
        true
    }
}

Java

View myView = findViewById(R.id.myView);
myView.setOnTouchListener( (view, event) -> {
    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_CANCEL:
            // Process canceled single-pointer motion event for all SDK versions.
        case MotionEvent.ACTION_UP:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
               (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) {
                //Process canceled multi-pointer motion event for Android 13 and higher.
            }
    }
    return true;
});
3. Mengurungkan gestur

Setelah mengidentifikasi sentuhan telapak tangan, Anda dapat mengurungkan efek gestur di layar.

Aplikasi Anda harus menyimpan histori tindakan pengguna sehingga input yang tidak disengaja seperti sentuhan telapak tangan dapat diurungkan. Lihat Mengimplementasikan aplikasi gambar dasar di codelab Meningkatkan dukungan stilus di aplikasi Android untuk mengetahui contohnya.

Hasil

Aplikasi Anda kini dapat mengidentifikasi dan menolak sentuhan telapak tangan untuk peristiwa multi-pointer di Android 13 dan level API yang lebih tinggi, serta untuk peristiwa pointer tunggal di semua level API.

Referensi tambahan

Untuk informasi selengkapnya, lihat referensi berikut:

Pengelolaan status WebView

Rating bintang tiga

WebView adalah komponen yang umum digunakan dan menawarkan sistem lanjutan untuk pengelolaan status. WebView harus mempertahankan status dan posisi scroll di seluruh perubahan konfigurasi. WebView dapat kehilangan posisi scroll saat pengguna memutar perangkat atau membuka ponsel foldable, yang memaksa pengguna men-scroll lagi dari bagian atas WebView ke posisi scroll sebelumnya.

Praktik terbaik

Minimalkan frekuensi pembuatan ulang WebView. WebView mampu mengelola statusnya dengan baik, dan Anda dapat memanfaatkan kualitas ini dengan mengelola sebanyak mungkin perubahan konfigurasi. Aplikasi Anda harus menangani perubahan konfigurasi karena pembuatan ulang Activity (cara sistem menangani perubahan konfigurasi) juga membuat ulang WebView, yang menyebabkan WebView kehilangan statusnya.

Bahan

  • android:configChanges: Atribut elemen <activity> manifes. Mencantumkan perubahan konfigurasi yang ditangani oleh aktivitas.
  • View#invalidate(): Metode yang menyebabkan tampilan digambar ulang. Diwarisi oleh WebView.

Langkah

Ringkasan

Untuk menyimpan status WebView, hindari pembuatan ulang Activity sebanyak mungkin, lalu biarkan WebView dibatalkan sehingga dapat diubah ukurannya sambil mempertahankan statusnya.

1. Menambahkan perubahan konfigurasi ke file AndroidManifest.xml aplikasi Anda

Hindari pembuatan ulang aktivitas dengan menentukan perubahan konfigurasi yang ditangani oleh aplikasi Anda (bukan oleh sistem):

<activity
  android:name=".MyActivity"
  android:configChanges="screenLayout|orientation|screenSize
      |keyboard|keyboardHidden|smallestScreenSize" />

2. Membatalkan WebView setiap kali aplikasi Anda menerima perubahan konfigurasi

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    webView.invalidate()
}

Java

@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    webview.invalidate();
}

Langkah ini hanya berlaku untuk sistem tampilan karena Jetpack Compose tidak perlu membatalkan apa pun untuk mengubah ukuran elemen Composable dengan benar. Namun, Compose sering membuat ulang WebView jika tidak dikelola dengan benar. Gunakan wrapper Accompanist WebView untuk menyimpan dan memulihkan status WebView di aplikasi Compose Anda.

Hasil

Komponen WebView aplikasi Anda sekarang mempertahankan status dan posisi scroll di beberapa perubahan konfigurasi, mulai dari mengubah ukuran hingga perubahan orientasi hingga melipat dan membentangkan.

Referensi tambahan

Untuk mempelajari lebih lanjut perubahan konfigurasi dan cara mengelolanya, lihat Menangani perubahan konfigurasi.

Pengelolaan status RecyclerView

Rating bintang tiga

RecyclerView dapat menampilkan data dalam jumlah besar menggunakan resource grafis yang minimal. Saat RecyclerView men-scroll daftar itemnya, RecyclerView akan menggunakan kembali instance View item yang telah di-scroll keluar layar untuk membuat item baru saat di-scroll di layar. Namun, perubahan konfigurasi, seperti rotasi perangkat, dapat mereset status RecyclerView, yang memaksa pengguna kembali men-scroll ke posisi sebelumnya dalam daftar item RecyclerView.

Praktik terbaik

RecyclerView harus mempertahankan statusnya — terutama, posisi scroll — dan status elemen daftarnya selama semua perubahan konfigurasi.

Bahan

Langkah

Ringkasan

Menyetel kebijakan pemulihan status RecyclerView.Adapter untuk menyimpan posisi scroll RecyclerView. Menyimpan status RecyclerView item daftar. Menambahkan status item daftar ke adaptor RecyclerView, dan memulihkan status item daftar saat terikat ke ViewHolder.

1. Mengaktifkan kebijakan pemulihan status Adapter

Aktifkan kebijakan pemulihan status adaptor RecyclerView sehingga posisi scroll RecyclerView tetap dipertahankan saat terjadi perubahan konfigurasi. Tambahkan spesifikasi kebijakan ke konstruktor adaptor:

Kotlin

class MyAdapter() : RecyclerView.Adapter() {
    init {
        stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY
    }
    ...
}

Java

class MyAdapter extends RecyclerView.Adapter {

    public Adapter() {
        setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY);
    }
    ...
}

2. Menyimpan status item daftar stateful

Simpan status item daftar RecyclerView kompleks, seperti item yang berisi elemen EditText. Misalnya, untuk menyimpan status EditText, tambahkan callback yang mirip dengan pengendali onClick untuk menangkap perubahan teks. Dalam callback, tentukan data yang akan disimpan:

Kotlin

input.addTextChangedListener(
    afterTextChanged = { text ->
        text?.let {
            // Save state here.
        }
    }
)

Java

input.addTextChangedListener(new TextWatcher() {
    
    ...

    @Override
    public void afterTextChanged(Editable s) {
        // Save state here.
    }
});

Deklarasikan callback di Activity atau Fragment Anda. Gunakan ViewModel untuk menyimpan status.

3. Menambahkan status item daftar ke Adapter

Tambahkan status item daftar ke RecyclerView.Adapter Anda. Teruskan status item ke konstruktor adaptor saat Activity atau Fragment host Anda dibuat:

Kotlin

val adapter = MyAdapter(items, viewModel.retrieveState())

Java

MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());

4. Memulihkan status item daftar dalam ViewHolder adaptor

Di RecyclerView.Adapter, saat Anda mengikat ViewHolder ke item, pulihkan status item:

Kotlin

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    ...
    val item = items[position]
    val state = states.firstOrNull { it.item == item }

    if (state != null) {
        holder.restore(state)
    }
}

Java

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
    ...
    Item item = items[position];
    Arrays.stream(states).filter(state -> state.item == item)
        .findFirst()
        .ifPresent(state -> holder.restore(state));
}

Hasil

RecyclerView Anda sekarang dapat memulihkan posisi scroll dan status setiap item dalam daftar RecyclerView.

Referensi tambahan

Pengelolaan keyboard yang dapat dilepas

Rating bintang tiga

Dukungan untuk keyboard yang dapat dilepas membantu memaksimalkan produktivitas pengguna di perangkat layar besar. Android memicu perubahan konfigurasi setiap kali keyboard dipasangkan atau dilepas dari perangkat, yang dapat menyebabkan hilangnya status UI. Aplikasi Anda dapat menyimpan dan memulihkan statusnya, sehingga sistem dapat menangani pembuatan ulang aktivitas, atau membatasi pembuatan ulang aktivitas untuk perubahan konfigurasi keyboard. Biasanya, semua data yang terkait dengan keyboard disimpan dalam objek Configuration. Kolom keyboard dan keyboardHidden objek konfigurasi berisi informasi tentang jenis keyboard dan ketersediaannya.

Praktik terbaik

Aplikasi yang dioptimalkan untuk perangkat layar besar mendukung setiap jenis perangkat input, mulai dari keyboard software dan hardware hingga stilus, mouse, trackpad, dan perangkat periferal lainnya.

Dukungan untuk keyboard eksternal melibatkan perubahan konfigurasi, yang dapat Anda kelola dengan salah satu dari dua cara berikut:

  1. Mengizinkan sistem membuat ulang aktivitas yang sedang berjalan, dan Anda mengelola status aplikasi.
  2. Kelola sendiri perubahan konfigurasi (aktivitas tidak akan dibuat ulang):
    • Mendeklarasikan semua nilai konfigurasi terkait keyboard
    • Membuat pengendali perubahan konfigurasi

Aplikasi produktivitas, yang sering kali memerlukan kontrol UI yang baik untuk entri teks dan input lainnya, dapat memanfaatkan pendekatan lakukan sendiri untuk menangani perubahan konfigurasi.

Dalam kasus khusus, Anda mungkin ingin mengubah tata letak aplikasi saat keyboard hardware dipasang atau dilepas, misalnya, untuk memberi lebih banyak ruang bagi alat atau mengedit jendela.

Karena satu-satunya cara yang andal untuk memproses perubahan konfigurasi adalah dengan mengganti metode onConfigurationChanged() tampilan, Anda dapat menambahkan instance View baru ke aktivitas aplikasi dan merespons dalam pengendali onConfigurationChanged() tampilan terhadap perubahan konfigurasi yang disebabkan oleh keyboard yang dipasang atau dilepas.

Bahan

  • android:configChanges: Atribut elemen <activity> manifes aplikasi. Memberi tahu sistem tentang perubahan konfigurasi yang dikelola aplikasi.
  • View#onConfigurationChanged(): Metode yang bereaksi terhadap penerapan konfigurasi aplikasi baru.

Langkah

Ringkasan

Deklarasikan atribut configChanges dan tambahkan nilai terkait keyboard. Tambahkan View ke hierarki tampilan aktivitas dan proses perubahan konfigurasi.

1. Mendeklarasikan atribut configChanges

Update elemen <activity> di manifes aplikasi dengan menambahkan nilai keyboard|keyboardHidden ke daftar perubahan konfigurasi yang sudah dikelola:

<activity
      …
      android:configChanges="...|keyboard|keyboardHidden">

2. Menambahkan tampilan kosong ke hierarki tampilan

Deklarasikan tampilan baru dan tambahkan kode pengendali di dalam metode onConfigurationChanged() tampilan:

Kotlin

val v = object : View(this) {
  override fun onConfigurationChanged(newConfig: Configuration?) {
    super.onConfigurationChanged(newConfig)
    // Handler code here.
  }
}

Java

View v = new View(this) {
    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Handler code here.
    }
};

Hasil

Aplikasi Anda kini akan merespons keyboard eksternal yang dipasang atau dilepas tanpa membuat ulang aktivitas yang sedang berjalan.

Referensi tambahan

Untuk mempelajari cara menyimpan status UI aplikasi selama perubahan konfigurasi seperti lampiran atau pelepasan keyboard, lihat Menyimpan status UI.