Google I / O 2019 yang lalu membawa banyak inovasi yang sensasional, banyak di antaranya akan memengaruhi industri pengembangan seluler di tahun-tahun mendatang. Tidak kalah menarik untuk mengikuti tren yang muncul. Pertama, tombol kontrol mekanis turun dalam sejarah, layar smartphone menjadi lebih besar dan bingkai samping menjadi lebih tidak mencolok. Gerakan menggantikan tombol sistem di layar, menyisakan lebih banyak ruang untuk konsumsi konten. Aplikasi ditampilkan pada seluruh permukaan layar yang terlihat, dari bawah ke bingkai atas, tanpa membatasi diri Anda ke batas bersyarat dari bilah status dan panel navigasi. Kita berada di ambang era Edge-to-Edge.

Apa itu Edge-to-Edge? Dipahami secara harfiah, ini berarti bahwa aplikasi Anda harus ditampilkan pada seluruh permukaan layar yang terlihat, dari bawah ke bingkai atas, tanpa membatasi diri ke bilah status dan tombol navigasi yang lebih rendah.
Edge-to-edge pada contoh shell sistem Android.Ketika berbicara tentang Android, ide sederhana masih jauh dari mudah diimplementasikan. Pada artikel ini, kita akan berbicara tentang bagaimana memaksimalkan penggunaan semua ruang yang tersedia di layar perangkat apa pun, terlepas dari pabrikan, versi sistem, dan beragam pengaturan yang disukai pabrikan perangkat dari Kerajaan Tengah (dan tidak hanya) untuk menyenangkan pengguna. Kode yang disajikan dalam artikel ini diuji pada lebih dari 30 perangkat oleh kami secara pribadi, dan pada 231 perangkat yang berbeda oleh 100 ribu pengguna aplikasi kami.
Masalah membuat antarmuka tepi-ke-tepi bukanlah hal baru dalam dirinya sendiri dan relevan jauh sebelum I / O 2019. Tentunya, Anda masing-masing akan ingat bagaimana Anda pertama kali mencari sesuatu di Google dari kategori:
"status bar transparan android" atau
"gradien status bar android" " .
Kriteria utama untuk aplikasi agar sesuai dengan judul edge-to-edge adalah:
- Status Bar transparan;
- Panel Navigasi transparan.
Baca lebih lanjut tentang mereka di
material.io .
Aplikasi Deezer Tidak Khawatir Tentang Kepatuhan Edge-to-Edge
Penting untuk dicatat bahwa kita tidak berbicara tentang menghapusnya sepenuhnya, seperti dalam "
mode layar penuh ". Kami memberikan kesempatan kepada pengguna untuk melihat informasi sistem yang penting dan menggunakan navigasi yang familier.
Persyaratan yang sama pentingnya untuk solusi adalah skalabilitas dan ekstensibilitas. Ada beberapa yang lain:
- Geser layar dengan benar di atas keyboard tanpa merusak dukungan untuk bendera AdjustResize dari Aktivitas;
- Hindari overlay Bar Status dan Bar Navigasi pada elemen UI aplikasi, sambil menampilkan latar belakang yang sesuai di bawahnya;
- Bekerja pada semua perangkat dengan versi Android saat ini dan terlihat identik.
Sedikit teori
Mungkin secara tak terduga membutuhkan banyak waktu untuk menemukan solusi untuk tugas yang tampaknya sederhana ini, yang tidak akan mudah bagi manajer proyek untuk menjelaskan. Dan ketika QA masih menemukan smartphone yang naas, di mana layar Anda tidak terlihat "sesuai dengan kanon" ...
Dalam proyek kami, kami keliru beberapa kali. Hanya sebulan kemudian, setelah melalui serangkaian uji coba yang panjang, apakah kami menyelesaikan masalah itu untuk selamanya.
Pertama-tama, Anda perlu memahami cara Android menggambar panel sistem. Dimulai dengan Android 5.0, API yang nyaman disediakan untuk bekerja dengan inden sistem sepanjang tepi horizontal layar. Mereka disebut WindowInsets, dan pada gambar di bawah ini berwarna merah:
Selain itu, pengembang dari tim Android telah menambahkan pendengar yang memungkinkan Anda berlangganan perubahan indentasi ini, misalnya, saat papan ketik muncul. Sebenarnya, WindowInsets adalah margin dari file layout Anda dari tepi layar. Saat mengubah ukuran Aktivitas Anda (mode layar terbagi, tampilan keyboard), Inset juga akan berubah. Jadi, untuk mendukung edge-to-edge, kita perlu memastikan bahwa indentasi ini tidak ada. Layar dengan null WindowInsets akan terlihat seperti ini:
Implementasi
Dalam implementasi kami, kami akan secara aktif beroperasi di Window dan benderanya.
Semua contoh akan ditulis dalam Kotlin, tetapi Anda dapat dengan mudah mengimplementasikannya di Jawa, menggunakan utilitas alih-alih fungsi ekstensi.
Hal pertama yang harus dilakukan dengan elemen tata letak root adalah mengatur flag secara eksplisit:
android:fitsSystemWindows="true"
Ini diperlukan untuk memastikan bahwa Tampilan root digambar di bawah elemen sistem, serta untuk pengukuran Inset yang benar saat berlangganan perubahannya.
Sekarang kita beralih ke hal yang paling penting - kita menghapus batas layar! Namun, ini harus dilakukan dengan sangat hati-hati. Dan inilah alasannya:
- Mengalahkan Inset yang lebih rendah, kami berisiko kehilangan reaksi jendela terhadap tampilan keyboard: ada lusinan tips tentang StackOverflow untuk mengatur ulang Inset atas, tetapi yang lebih rendah diam diam. Karena itu, NavigationBar tidak sepenuhnya transparan. Saat mengatur ulang Inset yang lebih rendah, flag AdjustResize berhenti berfungsi.
Solusi: Setiap kali Anda mengubah Inset, tentukan apakah ketinggian keyboard yang lebih rendah terdapat di dalamnya dan setel ulang saja sebaliknya. - Ketika Anda mengatur ulang Inset, bagian-bagian Tampilan yang terlihat akan jatuh di bawah Bilah Status dan Bilah Navigasi. Menurut konsep Desain Material (dan akal sehat), tidak ada elemen aktif yang harus ditempatkan di area sistem. Artinya, di area ini tidak boleh ada tombol, bidang untuk memasukkan teks, kotak centang, dll.
Solusi: kami akan menambahkan pendengar ke pendengar sehingga ketika mengubah WindowInsets, terjemahkan indentasi sistem ke Aktivitas, dan tanggapi secara internal dengan mengatur bantalan dan margin yang benar untuk Tampilan.

Perilaku ini seharusnya tidak diizinkan (Bilah Alat merayapi di Bilah Status).
Fungsi
removeSystemInsets () terlihat seperti ini:
fun removeSystemInsets(view: View, listener: OnSystemInsetsChangedListener) { ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets -> val desiredBottomInset = calculateDesiredBottomInset( view, insets.systemWindowInsetTop, insets.systemWindowInsetBottom, listener ) ViewCompat.onApplyWindowInsets( view, insets.replaceSystemWindowInsets(0, 0, 0, desiredBottomInset) ) } }
Fungsi
calculDesiredBottomInset () menghitung Inset bawah dengan atau tanpa keyboard, tergantung pada konfigurasi perangkat saat ini.
fun calculateDesiredBottomInset( view: View, topInset: Int, bottomInset: Int, listener: OnSystemInsetsChangedListener ): Int { val hasKeyboard = isKeyboardAppeared(view, bottomInset) val desiredBottomInset = if (hasKeyboard) bottomInset else 0 listener(topInset, if (hasKeyboard) 0 else bottomInset) return desiredBottomInset }
Gunakan metode
isKeyboardAppeared () untuk memeriksa ketinggian keyboard
. Kami memercayai hipotesis bahwa keyboard tidak dapat menempati kurang dari seperempat dari ketinggian layar. Jika diinginkan, Anda dapat memodifikasi logika verifikasi sesuka Anda.
private fun View.isKeyboardAppeared(bottomInset: Int) = bottomInset / resourdisplayMetrics.heightPixels.toDouble() > .25
Metode
removeSystemInsets () menggunakan pendengar. Sebenarnya, ini hanya typealias untuk ekspresi lambda. Kode lengkapnya:
typealias OnSystemBarsSizeChangedListener = (statusBarSize: Int, navigationBarSize: Int) -> Unit
Langkah selanjutnya adalah mengatur transparansi ke bilah sistem:
window.statusBarColor = Color.TRANSPARENT window.navigationBarColor = Color.TRANSPARENT
Setelah mengkompilasi semua hal di atas, kami memperoleh metode berikut:
fun Activity.setWindowTransparency( listener: OnSystemInsetsChangedListener = { _, _ -> } ) { InsetUtil.removeSystemInsets(window.decorView, listener) window.navigationBarColor = Color.TRANSPARENT window.statusBarColor = Color.TRANSPARENT }
Sekarang, untuk mengaktifkan mode ujung ke ujung dari Aktivitas yang diinginkan, Anda hanya perlu memanggil fungsi berikut dalam metode
onCreate () :
setWindowTransparency { statusBarSize, navigationBarSize ->
Jadi, dalam waktu kurang dari 30 baris kode, kami telah mencapai efek "ujung-ke-ujung", sementara tidak melanggar prinsip-prinsip UX dan tanpa menghilangkan pengguna kontrol sistem yang biasa. Implementasi seperti itu mungkin terlihat sederhana dan sepele bagi seseorang, tetapi itu yang memastikan operasi aplikasi Anda yang andal di perangkat apa pun.
Anda dapat mencapai efek "ujung ke ujung" dalam sekitar seratus cara yang berbeda (jumlah
kiat semacam itu tentang StackOverflow adalah konfirmasi yang jelas tentang hal ini), tetapi banyak di antaranya mengarah ke perilaku yang salah pada versi Android yang berbeda, atau tidak memperhitungkan parameter akun seperti kebutuhan untuk menampilkan lama. daftar, atau pecahkan ukuran layar saat menampilkan keyboard.
Terbang di salep
Solusi yang dijelaskan dalam artikel ini cocok untuk semua perangkat saat ini. Sebenarnya berarti perangkat di Android Lollipop (5.0) dan lebih tinggi. Bagi mereka, solusi di atas akan bekerja dengan sempurna. Tetapi untuk versi Android yang lebih lama, Anda akan membutuhkan implementasi Anda sendiri, karena tidak ada yang diketahui tentang WindowInsets pada masa itu.
Berita baiknya adalah bahwa pada Android KitKat (4.4), transparansi panel sistem masih didukung. Tetapi versi yang lebih lama tidak mendukung keindahan semacam itu sama sekali, Anda bahkan tidak dapat mencobanya.
Mari kita fokus pada pergeseran Insets di Android 4.4. Ini dapat dilakukan dalam metode
fitSystemWindows () . Dengan demikian, elemen utama dalam tata letak Anda harus berupa wadah dengan metode fitSystemWindows yang ditimpa yang berisi implementasi yang persis sama dengan pendengar kami dalam contoh untuk versi Android saat ini.
class KitkatTransparentSystemBarsFrame @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = -1 ) : FrameLayout(context, attrs, defStyleAttr), KitkatTransparentSystemBarsContainer { override var onSystemInsetsChangedListener: OnSystemInsetsChangedListener = { _, _ -> } override fun fitSystemWindows(insets: Rect?): Boolean { insets ?: return false val desiredBottomInset = InsetUtil.calculateDesiredBottomInset( this, insets.top, insets.bottom, onSystemInsetsChangedListener ) return super.fitSystemWindows(Rect(0, 0, 0, desiredBottomInset)) }
Pada perangkat dengan Android 4.4, hanya sebagian transparansi yang bekerja melalui pengaturan bendera yang transparan:
window.addFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION )
Bendera ini membuat bilah sistem transparan, menambahkan sedikit gradien pada mereka, yang, sayangnya, tidak dapat dihapus. Namun, gradien dapat diubah menjadi bilah warna transparan menggunakan pustaka ini:
https://github.com/jgilfelt/SystemBarTint . Dia telah membantu kami lebih dari sekali di masa lalu. Perubahan terbaru dibuat ke perpustakaan 5 tahun yang lalu, jadi itu hanya akan mengungkapkan daya tariknya ke kota retrograde sejati.
Seluruh proses penandaan untuk Kitkat akan terlihat seperti ini:
fun Activity.setWindowTransparencyKitkat( rootView: KitkatTransparentSystemBarsContainer, listener: OnSystemBarsSizeChangedListener = { _, _ -> } ) { rootView.onSystemBarsSizeChangedListener = listener window.addFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION ) }
Dengan mengingat hal ini, kami menulis metode universal yang dapat membuat bilah sistem transparan (atau setidaknya tembus), terlepas dari aplikasi mana yang berjalan pada perangkat dengan versi Android mana:
when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> setWindowTransparency(::updateMargins) Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT -> setWindowTransparencyKitkat(root_container, ::updateMargins) else -> { } }
Di bawah spoiler di bawah ini, Anda dapat melihat bagaimana sampel yang disajikan dalam artikel terlihat pada beberapa perangkat bermasalah:
Tangkapan layarHuawei Honor 8, Android 7.0

Xiaomi Redmi Note 4, Android 6.1

HTC Desire Dual Sim, Android 4.4.2

Samsung J3, Android 7.0

M3 Meizu, Android 5.1

Asus Zenfone 3 Max, Android 6.0

Umi Rome, Android 5.0

Nexus 5X, Android 8.0

Samsung Galaxy S8, Android 9.0

Sebagai kesimpulan, saya ingin mengatakan bahwa solusi untuk tugas yang tampaknya sederhana seperti pengaturan transparansi untuk elemen-elemen dari sistem UI dapat menyeret Anda di sepanjang berbagai macam jebakan dan pada akhirnya tidak mengarah pada hasil yang diinginkan, tetapi justru akan menyebabkan bug yang tidak menyenangkan. Untung Anda memiliki artikel ini sekarang.
Anda dapat menemukan daftar lengkap program dan contoh pekerjaan di
repositori git kami.
Materi ini terinspirasi oleh laporan Chris Banes "Menjadi tukang jendela utama".
Saya mengucapkan terima kasih kepada Surf studio dan Evgeny Saturov atas bantuannya dalam mempersiapkan materi.