Platform Android bertanggung jawab untuk menggambar UI sistem, seperti {i>status bar<i} dan {i>navigation bar<i}. UI sistem ini ditampilkan, terlepas dari aplikasi yang digunakan pengguna.
WindowInsets
memberikan informasi tentang sistem
UI untuk memastikan aplikasi Anda menggambar di area yang benar dan UI Anda tidak terhalang
oleh UI sistem.
Di Android 14 (level API 34) dan yang lebih rendah, UI aplikasi Anda tidak digambar di bawahnya kolom sistem dan potongan layar secara default.
Di Android 15 (level API 35) dan yang lebih baru, aplikasi Anda digambar di bawah sistem batang dan potongan layar setelah aplikasi menargetkan SDK 35. Hasilnya adalah pengalaman pengguna yang lancar dan memungkinkan aplikasi Anda untuk memanfaatkan sepenuhnya ruang jendela yang tersedia untuknya.
Menampilkan konten di balik UI sistem disebut edge-to-edge. Di sini Anda akan mempelajari berbagai jenis inset, cara menampilkan tata letak layar penuh, dan cara menggunakan API inset untuk menganimasikan UI dan memastikan konten aplikasi Anda tidak terhalang oleh elemen UI sistem.
Dasar-dasar inset
Saat aplikasi ditampilkan di layar penuh, Anda perlu memastikan bahwa konten dan interaksi tidak terhalang oleh UI sistem. Misalnya, jika sebuah tombol ditempatkan di belakang bilah navigasi, pengguna mungkin tidak dapat mengkliknya.
Ukuran UI sistem dan informasi penempatannya telah ditentukan melalui inset.
Setiap bagian dari UI sistem memiliki jenis inset yang sesuai yang menjelaskan ukuran dan letaknya. Misalnya, {i>inset<i} status bar memberikan ukuran dan posisi {i>status bar<i}, sedangkan {i> inset<i} bilah navigasi menyediakan ukuran dan posisi bilah navigasi. Setiap jenis inset terdiri dari empat dimensi piksel: atas, kiri, kanan, dan bawah. Dimensi ini menentukan seberapa jauh UI sistem diperluas dari sisi jendela aplikasi yang sesuai. Untuk menghindari tumpang tindih dengan jenis UI sistem tersebut, oleh karena itu, UI aplikasi harus disisipkan oleh jumlah tersebut.
Jenis inset Android bawaan ini tersedia melalui WindowInsets
:
Inset yang menjelaskan status bar. Ini adalah bilah 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 yang imersif), inset status bar utama akan kosong, tetapi inset ini tidak akan kosong. |
|
Inset yang menjelaskan menu navigasi. Ini adalah kolom UI sistem di sisi kiri, kanan, atau bawah perangkat, yang menjelaskan ikon taskbar atau navigasi. Kontrol ini dapat berubah saat runtime berdasarkan metode navigasi pilihan pengguna dan interaksi dengan taskbar. |
|
Inset menu navigasi saat terlihat. Jika menu navigasi saat ini disembunyikan (karena memasuki mode layar penuh yang imersif), inset menu navigasi utama akan kosong, tetapi inset ini tidak akan kosong. |
|
Inset yang menjelaskan dekorasi jendela UI sistem jika dalam jendela bentuk bebas, seperti kolom judul atas. |
|
Inset kolom teks saat terlihat. Jika kolom teks saat ini tersembunyi, inset bilah teks utama akan kosong, tetapi inset ini tidak akan kosong. |
|
Penyatuan inset kolom sistem, yang mencakup status bar, menu navigasi, dan kolom teks. |
|
Inset kolom sistem saat terlihat. Jika kolom sistem saat ini disembunyikan (karena memasuki mode layar penuh yang imersif), inset kolom 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 lebih mendetail tentang UI navigasi, yang memberikan jumlah ruang untuk "mengetuk" akan ditangani oleh sistem, dan bukan oleh 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 mencegat gestur untuk navigasi. Aplikasi dapat menentukan penanganan gestur ini dalam jumlah terbatas melalui |
|
Subset gestur sistem yang akan selalu ditangani oleh sistem, dan yang tidak dapat dikecualikan melalui |
|
Inset yang mewakili jumlah spasi yang diperlukan untuk menghindari tumpang-tindih dengan potongan layar (notch atau lubang jarum). |
|
Inset yang mewakili area melengkung tampilan waterfall. Tampilan waterfall memiliki area melengkung di sepanjang tepi layar tempat layar mulai digabungkan di sepanjang sisi perangkat. |
Jenis ini diringkas dalam tiga jenis kata "aman" jenis inset yang memastikan dikaburkan:
Metrik "aman" ini jenis inset melindungi konten dengan cara yang berbeda, berdasarkan inset platform dasar:
- Gunakan
WindowInsets.safeDrawing
untuk melindungi konten yang tidak boleh digambar di bawah UI sistem apa pun. Ini adalah penggunaan {i>inset<i} yang paling umum: untuk mencegah menggambar konten yang dikaburkan oleh UI sistem (baik sebagian atau sepenuhnya). - Gunakan
WindowInsets.safeGestures
untuk melindungi konten dengan gestur. Ini menghindari gestur sistem yang bertentangan dengan gestur aplikasi (seperti untuk gestur bawah spreadsheet, korsel, atau dalam {i>game<i}). - Gunakan
WindowInsets.safeContent
sebagai kombinasi dariWindowInsets.safeDrawing
danWindowInsets.safeGestures
untuk memastikan konten tidak memiliki tumpang tindih visual dan tidak ada gestur yang tumpang tindih.
Penyiapan inset
Untuk memberi aplikasi kontrol penuh atas tempat menggambar konten, ikuti penyiapan ini langkah. Tanpa langkah-langkah ini, aplikasi Anda dapat menggambar warna hitam atau solid di belakang UI sistem, atau tidak menganimasikan secara sinkron dengan keyboard virtual.
- Targetkan SDK 35 atau yang lebih baru untuk menerapkan tata letak layar penuh di Android 15 dan yang lebih baru. Aplikasi Anda ditampilkan di belakang UI sistem. Anda dapat menyesuaikan UI aplikasi dengan menangani inset.
- Jika perlu, panggil
enableEdgeToEdge()
diActivity.onCreate()
, yang memungkinkan aplikasi Anda ditampilkan di layar penuh dari layar sebelumnya Versi Android. Tetapkan
android:windowSoftInputMode="adjustResize"
di Aktivitas AndaAndroidManifest.xml
entri. Setelan ini memungkinkan aplikasi Anda menerima ukuran IME software sebagai inset, yang dapat Anda gunakan untuk menambahkan dan menata konten dengan 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">
API Compose
Setelah Aktivitas mengambil kontrol untuk menangani semua inset, Anda dapat menggunakan Compose API untuk memastikan konten tidak terkabur dan elemen yang dapat berinteraksi 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. Sementara ini memastikan bahwa
elemen yang bisa berinteraksi tidak
tumpang tindih dengan UI sistem, ini juga berarti bahwa tidak ada aplikasi yang akan menggambar di belakang
UI sistem untuk memberikan efek edge-to-edge. Untuk memaksimalkan penggunaan seluruh
Anda perlu menyesuaikan tempat inset diterapkan di setiap layar
atau komponen-per-komponen.
Semua jenis inset ini dianimasikan secara otomatis dengan animasi IME di-backport ke API 21. Dengan demikian, semua tata letak Anda yang menggunakan inset ini juga secara otomatis dianimasikan saat nilai inset berubah.
Ada dua cara utama untuk menggunakan jenis inset ini guna menyesuaikan Composable Anda tata letak: pengubah padding dan pengubah ukuran inset.
Pengubah padding
Modifier.windowInsetsPadding(windowInsets: WindowInsets)
menerapkan
inset jendela tertentu sebagai padding, yang bertindak seperti Modifier.padding
.
Misalnya, Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
menerapkan
{i>inset<i} gambar yang aman sebagai pelapis di keempat sisinya.
Ada juga beberapa metode utilitas bawaan untuk jenis inset yang paling umum.
Modifier.safeDrawingPadding()
adalah salah satu metode tersebut, setara dengan
Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
. Ada juga solusi analog
pengubah untuk jenis inset lainnya.
Pengubah ukuran inset
Pengubah berikut menerapkan jumlah inset jendela dengan menyetel ukuran komponennya 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 mengubah ukuran Spacer
yang menggunakan
ruang inset:
LazyColumn( Modifier.imePadding() ) { // Other content item { Spacer( Modifier.windowInsetsBottomHeight( WindowInsets.systemBars ) ) } }
Pemakaian inset
Pengubah padding inset (windowInsetsPadding
dan helper seperti
safeDrawingPadding
) otomatis menggunakan bagian inset yang
diterapkan sebagai padding. Saat mempelajari hierarki komposisi lebih dalam, inset bertingkat
pengubah padding dan pengubah ukuran inset tahu bahwa beberapa bagian
inset telah digunakan oleh pengubah padding inset luar, dan hindari
menggunakan bagian inset yang sama lebih
dari sekali yang akan mengakibatkan
banyak ruang ekstra.
Pengubah ukuran inset juga menghindari penggunaan bagian inset yang sama lebih dari sekali jika inset sudah digunakan. Namun, karena mereka mengubah ukuran secara langsung, mereka tidak memakai inset itu sendiri.
Akibatnya, pengubah {i>nesting padding<i} secara otomatis mengubah jumlah padding yang diterapkan ke setiap composable.
Dengan melihat contoh LazyColumn
yang sama seperti sebelumnya, LazyColumn
diproses
diubah ukurannya oleh pengubah imePadding
. Di dalam LazyColumn
, item terakhir adalah
berukuran setinggi bagian bawah bilah sistem:
LazyColumn( Modifier.imePadding() ) { // Other content item { Spacer( Modifier.windowInsetsBottomHeight( WindowInsets.systemBars ) ) } }
Ketika 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
adalah ukuran
sisi bawah bilah sistem.
Saat IME terbuka, inset IME akan dianimasikan untuk menyesuaikan dengan ukuran IME, dan
Pengubah imePadding()
mulai menerapkan padding bawah untuk mengubah ukuran
LazyColumn
saat IME terbuka. Saat pengubah imePadding()
mulai diterapkan
padding bawah, model juga mulai memakai sejumlah inset. Oleh karena itu,
tinggi Spacer
mulai berkurang, sebagai bagian dari jarak untuk sistem
batang telah diterapkan oleh pengubah imePadding()
. Setelah
Pengubah imePadding()
menerapkan jumlah padding bawah yang lebih besar
dari kolom sistem, tinggi Spacer
adalah nol.
Saat IME tertutup, perubahan terjadi secara terbalik: Spacer
mulai
diperluas dari ketinggian nol setelah imePadding()
diterapkan kurang dari
sisi bawah kolom sistem, hingga akhirnya Spacer
cocok dengan tinggi
sisi bawah bilah sistem setelah IME dianimasikan sepenuhnya.
Perilaku ini dicapai melalui komunikasi antara semua
Pengubah windowInsetsPadding
, dan dapat dipengaruhi dalam beberapa
cara.
Modifier.consumeWindowInsets(insets: WindowInsets)
juga menggunakan inset
dengan cara yang sama seperti Modifier.windowInsetsPadding
, tetapi tidak berlaku
inset yang digunakan sebagai padding. Hal ini berguna dalam kombinasi dengan inset
pengubah ukuran, untuk menunjukkan kepada saudara kandung
bahwa sejumlah inset tertentu memiliki
sudah dipakai:
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 menginformasikan
turunan ketika padding atau spasi disediakan oleh beberapa mekanisme selain
pengubah padding inset, seperti Modifier.padding
biasa atau tinggi tetap
pengatur jarak:
@OptIn(ExperimentalLayoutApi::class) Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) { // content Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) }
Jika inset jendela mentah diperlukan tanpa pemakaian, gunakan
WindowInsets
nilai secara langsung, atau gunakan WindowInsets.asPaddingValues()
untuk
menampilkan PaddingValues
inset yang tidak terpengaruh oleh konsumsi.
Namun, karena catatan di bawah, lebih suka menggunakan padding inset jendela
dan pengubah ukuran inset jendela jika memungkinkan.
Fase Inset dan Jetpack Compose
Compose menggunakan API inti AndroidX dasar untuk mengupdate dan menganimasikan inset, yang menggunakan API platform dasar yang mengelola inset. Karena platform itu perilakunya, inset memiliki hubungan khusus dengan fase Jetpack Tulis.
Nilai inset diperbarui setelah fase komposisi, tetapi sebelum fase tata letak. Ini berarti bahwa membaca nilai inset dalam komposisi umumnya menggunakan nilai inset yang terlambat satu frame. Fitur bawaan pengubah yang dijelaskan di halaman ini dibuat untuk menunda penggunaan nilai inset hingga fase tata letak, yang memastikan bahwa nilai inset digunakan di {i>frame<i} 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 kontainer.
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 kemudahan 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
Di bawah ini adalah daftar dokumentasi Materi Komponen yang menangani inset secara otomatis.
Panel aplikasi
TopAppBar
/SmallTopAppBar
/CenterAlignedTopAppBar
/MediumTopAppBar
/LargeTopAppBar
: Menerapkan sisi atas dan horizontal bilah 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 {i>default<i},
Scaffold
menyediakan inset sebagai parameter paddingValues
untuk Anda gunakan dan gunakan.
Scaffold
tidak menerapkan inset ke konten; tanggung jawab ini
menjadi 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 bisa berupa jenis
inset jendela untuk diterapkan sebagai gantinya, atau dinonaktifkan dengan meneruskan instance kosong:
WindowInsets(0, 0, 0, 0)
.
Misalnya, untuk menonaktifkan
penanganan inset pada
LargeTopAppBar
,
Tetapkan parameter windowInsets
ke instance kosong:
LargeTopAppBar( windowInsets = WindowInsets(0, 0, 0, 0), title = { Text("Hi") } )
Interop dengan inset sistem View
Anda mungkin perlu mengganti inset {i>default<i} ketika layar Anda memiliki View dan Tulis kode dalam hierarki yang sama. Dalam hal ini, Anda harus menyatakan dengan jelas mana yang harus memakai inset, dan mana yang harus mengabaikannya.
Misalnya, jika tata letak terluar adalah tata letak Android View, Anda harus
memakai inset di sistem View dan mengabaikannya untuk Compose.
Atau, jika tata letak terluar adalah composable, Anda harus menggunakan
inset di Compose, dan berikan padding pada composable AndroidView
.
Secara default, setiap ComposeView
memakai semua inset pada
Tingkat konsumsi WindowInsetsCompat
. Untuk mengubah perilaku default ini, setel
ComposeView.consumeWindowInsets
ke false
.
Referensi
- Kini di Android — aplikasi Android yang berfungsi penuh dan dibangun sepenuhnya dengan Kotlin dan Jetpack Compose.
- Menangani penerapan menyeluruh di Android 15 — codelab yang menjelaskan penerapan tata letak layar penuh Android 15
- 3 hal untuk meningkatkan pengalaman aplikasi Android Anda: Edge to Edge, Kembali Prediktif, dan Glance — video YouTube yang berbicara tentang penerapan edge-to-edge Android 15
- Edge-to-edge dan inset | Tips Compose — video YouTube yang menunjukkan cara menangani inset untuk menggambar tata letak layar penuh
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Komponen Material dan tata letak
- Memigrasikan
CoordinatorLayout
ke Compose - Pertimbangan lainnya