Platform Android bertanggung jawab untuk menggambar UI sistem, seperti status bar dan menu navigasi. UI sistem ini ditampilkan, apa pun aplikasi yang digunakan pengguna.
WindowInsets
memberikan informasi tentang UI
sistem untuk memastikan aplikasi Anda menggambar di area yang benar dan UI Anda tidak tertutup
oleh UI sistem.
Di Android 14 (API level 34) dan yang lebih lama, UI aplikasi Anda tidak digambar di bawah kolom sistem dan menampilkan potongan secara default.
Di Android 15 (API level 35) dan yang lebih tinggi, aplikasi Anda akan menggambar di bawah batang sistem dan menampilkan potongan setelah aplikasi menargetkan SDK 35. Hal ini menghasilkan pengalaman pengguna yang lebih lancar dan memungkinkan aplikasi Anda memanfaatkan sepenuhnya ruang jendela yang tersedia.
Menampilkan konten di belakang UI sistem disebut layar penuh. Di halaman ini, Anda akan mempelajari berbagai jenis inset, cara menggunakan inset dari tepi ke tepi, dan cara menggunakan API inset untuk menganimasikan UI dan memastikan konten aplikasi tidak terhalang oleh elemen UI sistem.
Dasar-dasar inset
Saat aplikasi menggunakan layar penuh, Anda harus memastikan bahwa konten dan interaksi penting tidak terhalang oleh UI sistem. Misalnya, jika tombol diletakkan di belakang menu navigasi, pengguna mungkin tidak dapat mengkliknya.
Ukuran UI sistem dan informasi tentang tempat penempatannya ditentukan melalui inset.
Setiap bagian UI sistem memiliki jenis inset yang sesuai yang menjelaskan ukuran dan tempat penempatannya. Misalnya, inset status bar memberikan ukuran dan posisi status bar, sedangkan inset menu navigasi memberikan ukuran dan posisi menu navigasi. Setiap jenis inset terdiri dari empat dimensi piksel: atas, kiri, kanan, dan bawah. Dimensi ini menentukan seberapa jauh UI sistem meluas dari sisi jendela aplikasi yang sesuai. Oleh karena itu, untuk menghindari tumpang-tindih dengan jenis UI sistem tersebut, UI aplikasi harus disisipkan dengan jumlah tersebut.
Jenis inset Android bawaan ini tersedia melalui WindowInsets
:
Inset yang menjelaskan status bar. Ini adalah panel UI sistem atas yang berisi ikon notifikasi dan indikator lainnya. |
|
Inset status bar saat terlihat. Jika status bar saat ini disembunyikan (karena memasuki mode layar penuh imersif), inset status bar utama akan kosong, tetapi inset ini tidak akan kosong. |
|
Inset yang menjelaskan menu navigasi. Ini adalah panel UI sistem di sisi kiri, kanan, atau bawah perangkat, yang menjelaskan taskbar atau ikon navigasi. Hal ini dapat berubah saat runtime berdasarkan metode navigasi pilihan pengguna dan berinteraksi dengan taskbar. |
|
Inset menu navigasi saat terlihat. Jika menu navigasi saat ini disembunyikan (karena memasuki mode layar penuh imersif), inset menu navigasi utama akan kosong, tetapi inset ini tidak akan kosong. |
|
Inset yang menjelaskan dekorasi jendela UI sistem jika berada di jendela bentuk bebas, seperti kolom judul atas. |
|
Panel teks yang disisipkan saat terlihat. Jika panel teks saat ini disembunyikan, inset panel teks utama akan kosong, tetapi inset ini tidak akan kosong. |
|
Gabungan inset panel sistem, yang mencakup status bar, menu navigasi, dan panel teks. |
|
Inset panel sistem saat terlihat. Jika panel sistem saat ini disembunyikan (karena memasuki mode layar penuh imersif), inset panel sistem utama akan kosong, tetapi inset ini tidak akan kosong. |
|
Inset yang menjelaskan jumlah ruang di bagian bawah yang ditempati keyboard software. |
|
Inset yang menjelaskan jumlah ruang yang ditempati keyboard software sebelum animasi keyboard saat ini. |
|
Inset yang menjelaskan jumlah ruang yang akan ditempati keyboard virtual setelah animasi keyboard saat ini. |
|
Jenis inset yang menjelaskan informasi yang lebih mendetail tentang UI navigasi, yang memberikan jumlah ruang tempat "ketukan" akan ditangani oleh sistem, bukan aplikasi. Untuk menu navigasi transparan dengan navigasi gestur, beberapa elemen aplikasi dapat diketuk melalui UI navigasi sistem. |
|
Inset elemen yang dapat diketuk saat terlihat. Jika elemen yang dapat diketuk saat ini disembunyikan (karena memasuki mode layar penuh imersif), inset elemen utama yang dapat diketuk akan kosong, tetapi inset ini tidak akan kosong. |
|
Inset yang mewakili jumlah inset tempat sistem akan menangkap gestur untuk navigasi. Aplikasi dapat menentukan secara manual penanganan gestur ini dalam jumlah terbatas melalui |
|
Subkumpulan gestur sistem yang akan selalu ditangani oleh sistem, dan yang tidak dapat dinonaktifkan melalui |
|
Inset yang mewakili jumlah spasi yang diperlukan untuk menghindari tumpang-tindih dengan potongan layar (notch atau pinhole). |
|
Inset yang mewakili area melengkung pada tampilan waterfall. Layar waterfall memiliki area melengkung di sepanjang tepi layar tempat layar mulai meliuk di sepanjang sisi perangkat. |
Jenis ini diringkas oleh tiga jenis inset "aman" yang memastikan konten tidak terhalang:
Jenis inset "aman" ini melindungi konten dengan cara yang berbeda, berdasarkan inset platform yang mendasarinya:
- Gunakan
WindowInsets.safeDrawing
untuk melindungi konten yang tidak boleh digambar di bawah UI sistem apa pun. Ini adalah penggunaan inset yang paling umum: untuk mencegah menggambar konten yang dikaburkan oleh UI sistem (sebagian atau sepenuhnya). - Gunakan
WindowInsets.safeGestures
untuk melindungi konten dengan gestur. Hal ini menghindari gestur sistem yang bertentangan dengan gestur aplikasi (seperti untuk panel bawah, carousel, atau dalam game). - Gunakan
WindowInsets.safeContent
sebagai kombinasiWindowInsets.safeDrawing
danWindowInsets.safeGestures
untuk memastikan konten tidak memiliki tumpang-tindih visual dan tidak ada tumpang-tindih gestur.
Penyiapan inset
Untuk mengizinkan aplikasi Anda memiliki kontrol penuh atas tempat aplikasi menggambar konten, ikuti langkah-langkah penyiapan berikut. Tanpa langkah-langkah ini, aplikasi Anda dapat menggambar warna hitam atau solid di belakang UI sistem, atau tidak menganimasikan secara sinkron dengan keyboard software.
- Targetkan SDK 35 atau yang lebih baru untuk menerapkan tampilan penuh di Android 15 dan yang lebih baru. Aplikasi Anda ditampilkan di belakang UI sistem. Anda dapat menyesuaikan UI aplikasi dengan menangani inset.
- Atau, panggil
enableEdgeToEdge()
diActivity.onCreate()
, yang memungkinkan aplikasi Anda menjadi layar penuh di versi Android sebelumnya. Tetapkan
android:windowSoftInputMode="adjustResize"
di entriAndroidManifest.xml
Aktivitas Anda. Setelan ini memungkinkan aplikasi Anda menerima ukuran IME software sebagai inset, yang dapat Anda gunakan untuk mengisi dan menata konten secara tepat saat IME muncul dan menghilang di aplikasi Anda.<!-- in your AndroidManifest.xml file: --> <activity android:name=".ui.MainActivity" android:label="@string/app_name" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.MyApplication" android:exported="true">
Compose API
Setelah Aktivitas mengambil kendali untuk menangani semua inset, Anda dapat menggunakan API Compose untuk memastikan konten tidak dikaburkan dan elemen yang dapat berinteraksi tidak tumpang-tindih dengan UI sistem. API ini juga menyinkronkan tata letak aplikasi Anda dengan perubahan inset.
Misalnya, ini adalah metode paling dasar untuk menerapkan inset ke konten seluruh aplikasi Anda:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { Box(Modifier.safeDrawingPadding()) { // the rest of the app } } }
Cuplikan ini menerapkan inset jendela safeDrawing
sebagai padding di sekitar
seluruh konten aplikasi. Meskipun hal ini memastikan bahwa elemen yang dapat berinteraksi tidak
tumpang-tindih dengan UI sistem, hal ini juga berarti bahwa tidak ada aplikasi yang akan menggambar di belakang
UI sistem untuk mencapai efek dari tepi ke tepi. Untuk memanfaatkan seluruh
jendela sepenuhnya, Anda perlu menyesuaikan tempat inset diterapkan berdasarkan layar
atau komponen.
Semua jenis inset ini dianimasikan secara otomatis dengan animasi IME yang di-backport ke API 21. Secara luas, semua tata letak Anda yang menggunakan inset ini juga akan otomatis dianimasikan saat nilai inset berubah.
Ada dua cara utama untuk menggunakan jenis inset ini guna menyesuaikan tata letak Composable: pengubah padding dan pengubah ukuran inset.
Pengubah padding
Modifier.windowInsetsPadding(windowInsets: WindowInsets)
menerapkan
inset jendela yang diberikan sebagai padding, yang berfungsi seperti Modifier.padding
.
Misalnya, Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
menerapkan
inset gambar yang aman sebagai padding di keempat sisi.
Ada juga beberapa metode utilitas bawaan untuk jenis inset yang paling umum.
Modifier.safeDrawingPadding()
adalah salah satu metode tersebut, yang setara dengan
Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
. Ada pengubah
analog untuk jenis inset lainnya.
Pengubah ukuran inset
Pengubah berikut menerapkan jumlah inset jendela dengan menetapkan ukuran komponen menjadi ukuran inset:
Menerapkan sisi awal windowInsets sebagai lebar (seperti |
|
Menerapkan sisi akhir windowInsets sebagai lebar (seperti |
|
Menerapkan sisi atas windowInsets sebagai tinggi (seperti |
|
|
Menerapkan sisi bawah windowInsets sebagai tinggi (seperti |
Pengubah ini sangat berguna untuk menentukan ukuran Spacer
yang menggunakan
ruang inset:
LazyColumn( Modifier.imePadding() ) { // Other content item { Spacer( Modifier.windowInsetsBottomHeight( WindowInsets.systemBars ) ) } }
Konsumsi inset
Pengubah padding inset (windowInsetsPadding
dan helper seperti
safeDrawingPadding
) secara otomatis menggunakan bagian inset yang
diterapkan sebagai padding. Saat masuk lebih dalam ke hierarki komposisi, pengubah padding
inset bertingkat dan pengubah ukuran inset mengetahui bahwa beberapa bagian
inset telah digunakan oleh pengubah padding inset luar, dan menghindari
penggunaan bagian inset yang sama lebih dari sekali yang akan menghasilkan terlalu banyak
ruang tambahan.
Pengubah ukuran inset juga menghindari penggunaan bagian inset yang sama lebih dari sekali jika inset telah digunakan. Namun, karena mengubah ukuran secara langsung, komponen ini tidak menggunakan inset.
Akibatnya, pengubah padding bertingkat akan otomatis mengubah jumlah padding yang diterapkan ke setiap composable.
Melihat contoh LazyColumn
yang sama seperti sebelumnya, LazyColumn
sedang
diubah ukurannya oleh pengubah imePadding
. Di dalam LazyColumn
, item terakhir
diukur agar memiliki tinggi bagian bawah panel sistem:
LazyColumn( Modifier.imePadding() ) { // Other content item { Spacer( Modifier.windowInsetsBottomHeight( WindowInsets.systemBars ) ) } }
Saat IME ditutup, pengubah imePadding()
tidak menerapkan padding, karena
IME tidak memiliki tinggi. Karena pengubah imePadding()
tidak menerapkan padding,
tidak ada inset yang digunakan, dan tinggi Spacer
akan menjadi ukuran
sisi bawah panel sistem.
Saat IME terbuka, inset IME akan dianimasikan agar cocok dengan ukuran IME, dan
pengubah imePadding()
mulai menerapkan padding bawah untuk mengubah ukuran
LazyColumn
saat IME terbuka. Saat pengubah imePadding()
mulai menerapkan
padding bawah, pengubah juga mulai menggunakan jumlah inset tersebut. Oleh karena itu, tinggi Spacer
mulai berkurang, karena bagian spasi untuk panel
sistem telah diterapkan oleh pengubah imePadding()
. Setelah
pengubah imePadding()
menerapkan jumlah padding bawah yang lebih besar
dari kolom sistem, tinggi Spacer
adalah nol.
Saat IME ditutup, perubahan terjadi secara terbalik: Spacer
mulai
diperluas dari tinggi nol setelah imePadding()
menerapkan lebih sedikit dari
sisi bawah panel sistem, hingga akhirnya Spacer
cocok dengan tinggi
sisi bawah panel sistem setelah IME sepenuhnya dianimasikan.
Perilaku ini dilakukan melalui komunikasi antara semua
pengubah windowInsetsPadding
, dan dapat dipengaruhi dengan beberapa cara
lain.
Modifier.consumeWindowInsets(insets: WindowInsets)
juga menggunakan inset
dengan cara yang sama seperti Modifier.windowInsetsPadding
, tetapi tidak menerapkan
inset yang digunakan sebagai padding. Hal ini berguna jika dikombinasikan dengan pengubah
ukuran inset, untuk menunjukkan kepada saudara bahwa jumlah inset tertentu
telah digunakan:
Column(Modifier.verticalScroll(rememberScrollState())) { Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars)) Column( Modifier.consumeWindowInsets( WindowInsets.systemBars.only(WindowInsetsSides.Vertical) ) ) { // content Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) } Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars)) }
Modifier.consumeWindowInsets(paddingValues: PaddingValues)
berperilaku sangat
mirip dengan versi dengan argumen WindowInsets
, tetapi memerlukan
PaddingValues
arbitrer untuk digunakan. Hal ini berguna untuk memberi tahu
turunan saat padding atau spasi disediakan oleh beberapa mekanisme selain
pengubah padding inset, seperti Modifier.padding
biasa atau pengatur jarak
tinggi tetap:
Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) { // content Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) }
Jika inset jendela mentah diperlukan tanpa penggunaan, gunakan
nilai WindowInsets
secara langsung, atau gunakan WindowInsets.asPaddingValues()
untuk
menampilkan PaddingValues
inset yang tidak terpengaruh oleh penggunaan.
Namun, karena pengecualian di bawah, sebaiknya gunakan pengubah padding
inset jendela dan pengubah ukuran inset jendela jika memungkinkan.
Fase Insets dan Jetpack Compose
Compose menggunakan API inti AndroidX yang mendasarinya untuk mengupdate dan menganimasikan inset, yang menggunakan API platform yang mendasarinya yang mengelola inset. Karena perilaku platform tersebut, inset memiliki hubungan khusus dengan fase Jetpack Compose.
Nilai inset diperbarui setelah fase komposisi, tetapi sebelum fase tata letak. Artinya, membaca nilai inset dalam komposisi umumnya menggunakan nilai inset yang terlambat satu frame. Pengubah bawaan yang dijelaskan di halaman ini dibuat untuk menunda penggunaan nilai inset hingga fase tata letak, yang memastikan bahwa nilai inset digunakan pada bingkai yang sama saat diperbarui.
Animasi IME keyboard dengan WindowInsets
Anda dapat menerapkan Modifier.imeNestedScroll()
ke penampung scroll untuk membuka dan
menutup IME secara otomatis saat men-scroll ke bagian bawah penampung.
class WindowInsetsExampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { MaterialTheme { MyScreen() } } } } @OptIn(ExperimentalLayoutApi::class) @Composable fun MyScreen() { Box { LazyColumn( modifier = Modifier .fillMaxSize() // fill the entire window .imePadding() // padding for the bottom for the IME .imeNestedScroll(), // scroll IME at the bottom content = { } ) FloatingActionButton( modifier = Modifier .align(Alignment.BottomEnd) .padding(16.dp) // normal 16dp of padding for FABs .navigationBarsPadding() // padding for navigation bar .imePadding(), // padding for when IME appears onClick = { } ) { Icon(imageVector = Icons.Filled.Add, contentDescription = "Add") } } }
Dukungan inset untuk Komponen Material 3
Untuk memudahkan penggunaan, banyak composable Material 3 bawaan
(androidx.compose.material3
)
menangani inset sendiri, berdasarkan cara composable ditempatkan di aplikasi Anda
sesuai dengan spesifikasi Material.
Composable penanganan inset
Berikut adalah daftar Komponen Material yang otomatis menangani inset.
Panel aplikasi
TopAppBar
/SmallTopAppBar
/CenterAlignedTopAppBar
/MediumTopAppBar
/LargeTopAppBar
: Menerapkan sisi atas dan horizontal panel sistem sebagai padding karena digunakan di bagian atas jendela.BottomAppBar
: Menerapkan sisi bawah dan horizontal kolom sistem sebagai padding.
Penampung konten
ModalDrawerSheet
/DismissibleDrawerSheet
/PermanentDrawerSheet
(konten di dalam panel navigasi modal): Menerapkan inset vertikal dan awal ke konten.ModalBottomSheet
: Menerapkan inset bawah.NavigationBar
: Menerapkan inset bawah dan horizontal.NavigationRail
: Menerapkan inset vertikal dan awal.
Scaffold
Secara default,
Scaffold
menyediakan inset sebagai parameter paddingValues
untuk Anda gunakan dan konsumsi.
Scaffold
tidak menerapkan inset ke konten; tanggung jawab ini adalah milik Anda.
Misalnya, untuk menggunakan inset ini dengan LazyColumn
di dalam Scaffold
:
Scaffold { innerPadding -> // innerPadding contains inset information for you to use and apply LazyColumn( // consume insets as scaffold doesn't do it by default modifier = Modifier.consumeWindowInsets(innerPadding), contentPadding = innerPadding ) { items(count = 100) { Box( Modifier .fillMaxWidth() .height(50.dp) .background(colors[it % colors.size]) ) } } }
Mengganti inset default
Anda dapat mengubah parameter windowInsets
yang diteruskan ke composable untuk
mengonfigurasi perilaku composable. Parameter ini dapat berupa jenis
inset jendela yang berbeda untuk diterapkan, atau dinonaktifkan dengan meneruskan instance kosong:
WindowInsets(0, 0, 0, 0)
.
Misalnya, untuk menonaktifkan penanganan inset di
LargeTopAppBar
,
tetapkan parameter windowInsets
ke instance kosong:
LargeTopAppBar( windowInsets = WindowInsets(0, 0, 0, 0), title = { Text("Hi") } )
Interop dengan inset sistem View
Jika aplikasi Anda berisi kode Compose dan View, Anda mungkin perlu menjelaskan inset sistem yang harus digunakan masing-masing dan memastikan bahwa inset dikirim ke tampilan saudara.
Mengganti inset default
Anda mungkin perlu mengganti inset default saat layar memiliki kode View dan Compose dalam hierarki yang sama. Dalam hal ini, Anda harus menentukan dengan jelas mana yang harus menggunakan inset, dan mana yang harus mengabaikannya.
Misalnya, jika tata letak terluar Anda adalah tata letak View Android, Anda harus
menggunakan inset di sistem View dan mengabaikannya untuk Compose.
Atau, jika tata letak terluar Anda adalah composable, Anda harus menggunakan
inset di Compose, dan menambahkan padding pada composable AndroidView
sebagaimana mestinya.
Secara default, setiap ComposeView
menggunakan semua inset pada
tingkat konsumsi WindowInsetsCompat
. Untuk mengubah perilaku default ini, tetapkan
ComposeView.consumeWindowInsets
ke false
.
Pengiriman inset yang kompatibel dengan versi lama untuk tampilan
Jika aplikasi Anda berisi kode View, Anda mungkin perlu mengonfirmasi bahwa inset dikirim ke tampilan saudara di perangkat yang menjalankan Android 10 (API level 29) atau yang lebih rendah. Lihat panduan View dari tepi ke tepi untuk informasi selengkapnya.
Ikon panel sistem
Memanggil enableEdgeToEdge
memastikan warna ikon panel sistem diperbarui saat tema
perangkat berubah.
Saat menggunakan layar penuh, Anda mungkin perlu memperbarui warna ikon menu sistem secara manual agar kontras dengan latar belakang aplikasi. Misalnya, untuk membuat ikon status bar terang:
Kotlin
WindowCompat.getInsetsController(window, window.decorView) .isAppearanceLightStatusBars = false
Java
WindowCompat.getInsetsController(window, window.getDecorView()) .setAppearanceLightStatusBars(false);
Perlindungan kolom sistem
Setelah aplikasi Anda menargetkan SDK 35 atau yang lebih baru, edge-to-edge akan diterapkan. Status bar sistem dan menu navigasi gestur transparan, tetapi menu navigasi tiga tombol transparan.
Untuk menghapus perlindungan latar belakang navigasi tiga tombol transparan default,
tetapkan Window.setNavigationBarContrastEnforced
ke false
.
Referensi
- Kolom sistem Android, panduan desain kolom sistem
- Now in Android — aplikasi Android yang berfungsi penuh dan sepenuhnya di-build dengan Kotlin dan Jetpack Compose.
- Menangani penerapan layar penuh di Android 15 — codelab yang membahas penerapan layar penuh di Android 15
- Tips penanganan inset untuk penerapan layar penuh Android 15
- Melihat pratinjau dan menguji UI layar penuh aplikasi Anda
- 3 hal untuk meningkatkan pengalaman aplikasi Android Anda: Tampilan Layar Penuh, Kembali Prediktif, dan Sekilas — video YouTube yang membahas penerapan tampilan layar penuh di Android 15
- Edge-to-edge and insets | Compose Tips — video YouTube yang menunjukkan cara menangani inset untuk menggambar edge-to-edge
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Komponen dan tata letak Material
- Memigrasikan
CoordinatorLayout
ke Compose - Pertimbangan lainnya