Gdy kierujesz aplikację na pakiet SDK 35 lub nowszy na urządzeniu z Androidem 15 lub nowszym, aplikacja jest wyświetlana bez ramki. Okno obejmuje całą szerokość i wysokość wyświetlacza, rysując za paskami systemu. Paski systemu obejmują pasek stanu, pasek podpisów i pasek nawigacyjny.
Wiele aplikacji ma górny pasek aplikacji. Górny pasek aplikacji powinien rozciągać się do górnej krawędzi ekranu i wyświetlać się za paskiem stanu. Opcjonalnie górny pasek aplikacji może się zmniejszyć do wysokości paska stanu podczas przewijania treści.
Wiele aplikacji ma też dolny pasek aplikacji lub dolny pasek nawigacyjny. Te paski powinny też rozciągać się do dolnej krawędzi ekranu i wyświetlać za paskiem nawigacyjnym. W przeciwnym razie aplikacje powinny wyświetlać przewijane treści za paskiem nawigacyjnym.
Podczas wdrażania w aplikacji układu od krawędzi do krawędzi pamiętaj o tych kwestiach:
- Włączanie wyświetlania bez ramki
- Wdróż układy adaptacyjne, aby zoptymalizować wrażenia użytkowników na różnych formatach.
- Zarządzanie nakładaniem się elementów wizualnych
- Rozważ wyświetlanie scrimów za paskami systemu
Włącz wyświetlanie bez ramki
Jeśli aplikacja jest kierowana na pakiet SDK 35 lub nowszy, tryb od krawędzi do krawędzi jest automatycznie włączany na urządzeniach z Androidem 15 lub nowszym.
Aby włączyć wyświetlanie bez ramki w starszych wersjach Androida, ręcznie wywołaj enableEdgeToEdge w onCreate w Activity.
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.enableEdgeToEdge(window)
...
}
Java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowCompat.enableEdgeToEdge(getWindow());
...
}
Domyślnie enableEdgeToEdge() sprawia, że paski systemu są przezroczyste, z wyjątkiem trybu nawigacji z 3 przyciskami, w którym pasek nawigacyjny jest półprzezroczysty. Kolory ikon systemowych i zasłony są dostosowywane na podstawie jasnego lub ciemnego motywu systemu.
Aby włączyć wyświetlanie bez ramki w aplikacji bez używania funkcji enableEdgeToEdge(), zapoznaj się z artykułem Ręczne konfigurowanie wyświetlania bez ramki.
Obsługa nakładania się za pomocą wcięć
Niektóre widoki aplikacji mogą być rysowane za paskami systemu, jak pokazano na rysunku 3.
Możesz rozwiązać problem nakładania się, reagując na odcięcia, które określają, które części ekranu przecinają się z interfejsem systemu, np. z paskiem nawigacyjnym lub paskiem stanu. Przecinanie się może oznaczać wyświetlanie się nad treścią, ale może też informować aplikację o gestach systemowych.
Typy wcięć, które mają zastosowanie do wyświetlania aplikacji od krawędzi do krawędzi:
Wstawki pasków systemu: najlepiej sprawdzają się w przypadku widoków, które można kliknąć i które nie mogą być wizualnie zasłonięte przez paski systemu.
Wstawki wycięcia w ekranie: w obszarach, w których może występować wycięcie w ekranie ze względu na kształt urządzenia.
Wstawki gestów systemowych: w przypadku obszarów nawigacji przy użyciu gestów używanych przez system, które mają pierwszeństwo przed aplikacją.
Wstawki pasków systemu
Wstawki paska systemowego to najczęściej używany typ wstawek. Reprezentują one obszar, w którym interfejs systemu jest wyświetlany na osi Z nad aplikacją. Najlepiej używać ich do przenoszenia lub wypełniania widoków w aplikacji, które można klikać i które nie mogą być wizualnie zasłonięte przez paski systemowe.
Na przykład pływający przycisk polecenia (FAB) na rysunku 3 jest częściowo zasłonięty przez pasek nawigacyjny:
Aby uniknąć tego rodzaju nakładania się elementów wizualnych w trybie gestów lub przycisków, możesz zwiększyć marginesy widoku, używając getInsets(int) z WindowInsetsCompat.Type.systemBars().
Poniższy przykładowy kod pokazuje, jak zaimplementować wstawki paska systemowego:
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(fab) { v, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) // Apply the insets as a margin to the view. This solution sets // only the bottom, left, and right dimensions, but you can apply whichever // insets are appropriate to your layout. You can also update the view padding // if that's more appropriate. v.updateLayoutParams<MarginLayoutParams> { leftMargin = insets.left bottomMargin = insets.bottom rightMargin = insets.right } // Return CONSUMED if you don't want the window insets to keep passing // down to descendant views. WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(fab, (v, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); // Apply the insets as a margin to the view. This solution sets only the // bottom, left, and right dimensions, but you can apply whichever insets are // appropriate to your layout. You can also update the view padding if that's // more appropriate. MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); mlp.leftMargin = insets.left; mlp.bottomMargin = insets.bottom; mlp.rightMargin = insets.right; v.setLayoutParams(mlp); // Return CONSUMED if you don't want the window insets to keep passing // down to descendant views. return WindowInsetsCompat.CONSUMED; });
Jeśli zastosujesz to rozwiązanie w przykładzie pokazanym na rysunku 3, w trybie przycisku nie będzie nakładania się elementów, jak pokazano na rysunku 4:
To samo dotyczy trybu nawigacji przy użyciu gestów, jak pokazano na rysunku 5:
Wstawki wycięcia w ekranie
Niektóre urządzenia mają wycięcia w ekranie. Zwykle wycięcie znajduje się u góry ekranu i jest uwzględnione na pasku stanu. Gdy ekran urządzenia jest w trybie poziomym, wycięcie może znajdować się na krawędzi pionowej. W zależności od treści, które aplikacja wyświetla na ekranie, należy zastosować dopełnienie, aby uniknąć wycięć w ekranie, ponieważ domyślnie aplikacje będą wyświetlane w wycięciu w ekranie.
Na przykład wiele ekranów aplikacji wyświetla listę elementów. Nie zasłaniaj elementów listy wycięciem w ekranie ani paskami systemu.
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, insets -> val bars = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) v.updatePadding( left = bars.left, top = bars.top, right = bars.right, bottom = bars.bottom, ) WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(mBinding.recyclerView, (v, insets) -> { Insets bars = insets.getInsets( WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() ); v.setPadding(bars.left, bars.top, bars.right, bars.bottom); return WindowInsetsCompat.CONSUMED; });
Określ wartość WindowInsetsCompat, stosując logiczne OR do pasków systemu i typów wycięć w ekranie.
Ustaw clipToPadding na RecyclerView, aby dopełnienie przewijało się wraz z elementami listy. Dzięki temu elementy mogą się przesuwać za paski systemowe, gdy użytkownik przewija ekran, jak w tym przykładzie.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
Wstawki gestów systemowych
Odcięcia gestów systemowych to obszary okna, w których gesty systemowe mają pierwszeństwo przed aplikacją. Te obszary są oznaczone na rysunku 6 na pomarańczowo:
Podobnie jak w przypadku dopełnień pasków systemowych, możesz uniknąć nakładania się dopełnień gestów systemowych, używając getInsets(int) z WindowInsetsCompat.Type.systemGestures().
Użyj tych wstawek, aby odsunąć widoki z możliwością przesuwania od krawędzi. Typowe przypadki użycia to plansze dolne, przesuwanie w grach i karuzele zaimplementowane za pomocą ViewPager2.
Na Androidzie 10 lub nowszym odcięcia gestów systemowych zawierają dolne odcięcie dla gestu ekranu głównego oraz lewe i prawe odcięcie dla gestów wstecz:
Poniższy przykładowy kod pokazuje, jak zaimplementować wstawki gestów systemowych:
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) // Apply the insets as padding to the view. Here, set all the dimensions // as appropriate to your layout. You can also update the view's margin if // more appropriate. view.updatePadding(insets.left, insets.top, insets.right, insets.bottom) // Return CONSUMED if you don't want the window insets to keep passing down // to descendant views. WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()); // Apply the insets as padding to the view. Here, set all the dimensions // as appropriate to your layout. You can also update the view's margin if // more appropriate. view.setPadding(insets.left, insets.top, insets.right, insets.bottom); // Return CONSUMED if you don't want the window insets to keep passing down // to descendant views. return WindowInsetsCompat.CONSUMED; });
Komponenty Material Design
Wiele opartych na widokach komponentów Android Material (com.google.android.material) automatycznie obsługuje wstawki, w tym BottomAppBar, BottomNavigationView, NavigationRailView i NavigationView.
Jednak AppBarLayout nie obsługuje automatycznie wcięć. Dodaj android:fitsSystemWindows="true", aby obsłużyć górne wcięcia.
Dowiedz się, jak obsługiwać elementy wewnętrzne za pomocą komponentów Material Design w Compose.
Zgodne wstecznie wysyłanie wstawki
Aby zatrzymać wysyłanie dopełnień do widoków podrzędnych i uniknąć nadmiernego dopełnienia, możesz wykorzystać dopełnienia za pomocą stałej WindowInsetsCompat.CONSUMED. Jednak na urządzeniach z Androidem 10 (poziom interfejsu API 29 i starsze) po wywołaniu WindowInsetsCompat.CONSUMED wstawki nie są wysyłane do elementów równorzędnych, co może powodować niezamierzone nakładanie się elementów wizualnych.
Aby mieć pewność, że elementy wewnętrzne są wysyłane do elementów równorzędnych we wszystkich obsługiwanych wersjach Androida, przed wykorzystaniem elementów wewnętrznych użyj ViewGroupCompat#installCompatInsetsDispatch. Jest to dostępne w AndroidX Core i Core-ktx 1.16.0-alpha01 i nowszych wersjach.
Kotlin
// Use the i.d. assigned to your layout's root view, e.g. R.id.main val rootView = findViewById(R.id.main) // Call before consuming insets ViewGroupCompat.installCompatInsetsDispatch(rootView)
Java
// Use the i.d. assigned to your layout's root view, e.g. R.id.main LinearLayout rootView = findViewById(R.id.main); // Call before consuming insets ViewGroupCompat.installCompatInsetsDispatch(rootView);
Tryb pojemny
Niektóre treści najlepiej ogląda się na pełnym ekranie, co zapewnia użytkownikom bardziej wciągające wrażenia. Paski systemu w trybie pojemnym możesz ukryć za pomocą bibliotek WindowInsetsController i WindowInsetsControllerCompat:
Kotlin
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView) // Hide the system bars. windowInsetsController.hide(Type.systemBars()) // Show the system bars. windowInsetsController.show(Type.systemBars())
Java
Window window = getWindow(); WindowInsetsControllerCompat windowInsetsController = WindowCompat.getInsetsController(window, window.getDecorView()); if (windowInsetsController == null) { return; } // Hide the system bars. windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()); // Show the system bars. windowInsetsController.show(WindowInsetsCompat.Type.systemBars());
Więcej informacji o implementacji tej funkcji znajdziesz w sekcji Ukrywanie pasków systemu w trybie pojemnym.
Ikony na pasku systemu
Wywołanie enableEdgeToEdge zapewnia aktualizację kolorów ikon na pasku systemowym po zmianie motywu urządzenia.
Podczas przechodzenia na tryb od krawędzi do krawędzi może być konieczne ręczne zaktualizowanie kolorów ikon paska systemu, aby kontrastowały z tłem aplikacji. Aby na przykład utworzyć jasne ikony na pasku stanu:
Kotlin
WindowCompat.getInsetsController(window, window.decorView) .isAppearanceLightStatusBars = false
Java
WindowCompat.getInsetsController(window, window.getDecorView()) .setAppearanceLightStatusBars(false);
Ochrona paska systemu
Gdy aplikacja będzie kierowana na pakiet SDK 35 lub nowszy, będzie wymuszane wyświetlanie bez ramki.
Pasek stanu systemu i paski nawigacji przy użyciu gestów są przezroczyste, ale pasek nawigacji z 3 przyciskami jest półprzezroczysty. Wywołaj enableEdgeToEdge, aby zapewnić zgodność wsteczną.
Ustawienia domyślne systemu mogą jednak nie działać we wszystkich przypadkach użycia. Zapoznaj się ze wskazówkami dotyczącymi projektowania pasków systemu Android i projektowania aplikacji bez ramki, aby określić, czy używać przezroczystych czy półprzezroczystych pasków systemu.
Tworzenie przezroczystych pasków systemowych
Utwórz przezroczysty pasek stanu, kierując aplikację na Androida 15 (SDK 35) lub nowszego albo wywołując enableEdgeToEdge() z domyślnymi argumentami w przypadku wcześniejszych wersji.
Utwórz przezroczysty pasek nawigacji przy użyciu gestów, kierując aplikację na Androida 15 lub nowszego albo wywołując funkcję enableEdgeToEdge() z domyślnymi argumentami w przypadku wcześniejszych wersji. W przypadku paska nawigacji z 3 przyciskami ustaw Window.setNavigationBarContrastEnforced na false, w przeciwnym razie zostanie zastosowana półprzezroczysta nakładka.
Tworzenie półprzezroczystych pasków systemowych
Aby utworzyć półprzezroczysty pasek stanu, wykonaj te czynności:
- Zaktualizuj zależność
androidx-coredo wersji 1.16.0-beta01 lub nowszej. - Umieść układ XML w elemencie
androidx.core.view.insets.ProtectionLayouti przypisz identyfikator. - Uzyskaj dostęp do
ProtectionLayoutza pomocą kodu, aby ustawić zabezpieczenia, określając stronę iGradientProtectionpaska stanu.
<androidx.core.view.insets.ProtectionLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list_protection" android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:id="@+id/item_list" android:clipToPadding="false" android:layout_width="match_parent" android:layout_height="match_parent"> <!--items--> </ScrollView> </androidx.core.view.insets.ProtectionLayout>
findViewById<ProtectionLayout>(R.id.list_protection) .setProtections( listOf( GradientProtection( WindowInsetsCompat.Side.TOP, // Ideally, this is the pane's background color paneBackgroundColor ) ) )
Upewnij się, że wartość ColorInt przekazywana do GradientProtection pasuje do tła treści. Na przykład układ szczegółowej listy wyświetlany na urządzeniu składanym może mieć różne GradientProtections w różnych kolorach dla panelu listy i panelu szczegółów.
Nie twórz półprzezroczystego paska nawigacji przy użyciu gestów. Aby utworzyć półprzezroczysty pasek nawigacyjny z 3 przyciskami, wykonaj jedną z tych czynności:
- Jeśli układ jest już opakowany w element
ProtectionView, możesz przekazać dodatkowy elementColorProtectionlubGradientProtectiondo metodysetProtections. Zanim to zrobisz, upewnij się, żewindow.isNavigationBarContrastEnforced = false. - W przeciwnym razie ustaw
window.isNavigationBarContrastEnforced = true.
Inne wskazówki
Dodatkowe wskazówki dotyczące obsługi wcięć.
Wyświetlanie przewijanych treści na całej powierzchni ekranu
Sprawdź, czy ostatni element listy nie jest zasłonięty przez paski systemowe w RecyclerView lub NestedScrollView, obsługując wstawki i ustawiając clipToPadding na false.
Poniższy film przedstawia aplikację RecyclerView z wyłączonym (po lewej) i włączonym (po prawej) wyświetlaniem od krawędzi do krawędzi:
Przykładowy kod znajdziesz we fragmentach kodu w sekcji Tworzenie list dynamicznych za pomocą RecyclerView.
Wyświetlanie okien pełnoekranowych bez ramki
Aby okno dialogowe wyświetlało się na pełnym ekranie, wywołaj enableEdgeToEdge w obiekcie Dialog.
Kotlin
class MyAlertDialogFragment : DialogFragment() {
override fun onStart(){
super.onStart()
dialog?.window?.let { WindowCompat.enableEdgeToEdge(it) }
}
...
}
Java
public class MyAlertDialogFragment extends DialogFragment {
@Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
Window window = dialog.getWindow();
if (window != null) {
WindowCompat.enableEdgeToEdge(window);
}
}
}
...
}
Dodatkowe materiały
Więcej informacji o wyświetlaniu od krawędzi do krawędzi znajdziesz w tych materiałach.
Blogi
- Wskazówki dotyczące obsługi wcięć w przypadku egzekwowania wyświetlania bez ramki na Androidzie 15
- WindowInsets – od detektorów do układów
Design
Inna dokumentacja
Filmy