Gdy kierujesz aplikację na pakiet SDK 35 lub nowszy na urządzeniu z Androidem 15 lub nowszym, jest ona wyświetlana bez ramki. Okno zajmuje całą szerokość i wysokość wyświetlacza, ponieważ jest rysowane za paskami systemu. Paski systemu to 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ę zmniejszać do wysokości paska stanu, gdy przewijana jest treść.
Wiele aplikacji ma też dolny pasek aplikacji lub dolny pasek nawigacyjny. Te paski powinny również rozciągać się do dolnej krawędzi ekranu i wyświetlać się za paskiem nawigacyjnym. W przeciwnym razie aplikacje powinny wyświetlać przewijaną treść za paskiem nawigacyjnym.
Podczas implementowania w aplikacji układu bez ramki pamiętaj o tych kwestiach:
- Włącz wyświetlanie bez ramki.
- Zaimplementuj układy adaptacyjne, aby zoptymalizować wrażenia użytkowników na różnych urządzeniach.
- Obsłuż nakładanie się elementów wizualnych.
- Rozważ wyświetlanie półprzezroczystych nakładek za paskami systemu.
Włącz wyświetlanie bez ramki
Jeśli aplikacja jest kierowana na pakiet SDK 35 lub nowszy, wyświetlanie bez ramki jest automatycznie włączane na urządzeniach z Androidem 15 lub nowszym.
Aby włączyć wyświetlanie bez ramki w poprzednich wersjach Androida, ręcznie wywołaj
enableEdgeToEdge w onCreate klasy 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 funkcja enableEdgeToEdge() sprawia, że paski systemu są przezroczyste, z wyjątkiem trybu nawigacji przy użyciu 3 przycisków, w którym pasek nawigacyjny ma półprzezroczystą nakładkę. Kolory ikon systemowych i nakładki są dostosowywane na podstawie jasnego lub ciemnego motywu systemu.
Aby włączyć wyświetlanie bez ramki w aplikacji bez użycia funkcji
enableEdgeToEdge(), przeczytaj artykuł
Ręczne konfigurowanie wyświetlania bez ramki.
Obsługa nakładania się elementów za pomocą odcięć
Niektóre widoki aplikacji mogą być rysowane za paskami systemu, jak pokazano na rysunku 3.
Nakładanie się elementów możesz rozwiązać, 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 nad treścią, ale może też informować aplikację o gestach systemowych.
Typy odcięć, które mają zastosowanie do wyświetlania aplikacji bez ramki:
Odcięcia pasków systemu: najlepsze w przypadku widoków, które można kliknąć i które nie mogą być wizualnie zasłonięte przez paski systemu.
Odcięcia wycięcia w ekranie: w przypadku obszarów, w których może występować wycięcie w ekranie ze względu na kształt urządzenia.
Odcięcia gestów systemowych: w przypadku obszarów nawigacji gestami używanych przez system, które mają priorytet przed aplikacją.
Odcięcia pasków systemu
Odcięcia pasków systemu to najczęściej używany typ odcięć. Reprezentują one obszar, w którym interfejs systemu wyświetla się na osi Z nad aplikacją. Najlepiej używać ich do przenoszenia lub dodawania dopełnienia do widoków w aplikacji, które można kliknąć i które nie mogą być wizualnie zasłonięte przez paski systemu.
Na przykład pływający przycisk czynności (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 trybie przycisków, możesz zwiększyć marginesy widoku za pomocą
getInsets(int)
z parametrem
WindowInsetsCompat.Type.systemBars().
Poniższy przykład kodu pokazuje, jak zaimplementować odcięcia pasków systemu:
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 do przykładu pokazanego na rysunku 3, w trybie przycisków nie będzie nakładania się elementów wizualnych, jak pokazano na rysunku 4:
To samo dotyczy trybu nawigacji przy użyciu gestów, jak pokazano na rysunku 5:
Odcięcia 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ędniane 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 wyświetlanej przez aplikację na ekranie należy zaimplementować dopełnienie, aby uniknąć wycięć w ekranie, ponieważ domyślnie aplikacje będą rysować w wycięciu w ekranie.
Na przykład na wielu ekranach aplikacji wyświetla się lista 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, wykonując logiczną operację lub na paskach systemu i typach wycięć w ekranie.
Ustaw wartość clipToPadding na RecyclerView, aby dopełnienie przewijało się z elementami listy. Dzięki temu elementy mogą znajdować się za paskami systemu, gdy użytkownik przewija, jak pokazano 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" />
Odcięcia gestów systemowych
Odcięcia gestów systemowych reprezentują obszary okna, w których gesty systemowe mają priorytet przed aplikacją. Obszary te są oznaczone na rysunku 6 kolorem pomarańczowym:
Podobnie jak w przypadku odcięć pasków systemu, możesz uniknąć nakładania się odcięć gestów systemowych
za pomocą
getInsets(int)
z parametrem
WindowInsetsCompat.Type.systemGestures().
Użyj tych odcięć, aby przenosić lub dodawać dopełnienie do widoków, które można przesuwać, z dala od krawędzi. Typowe przypadki użycia to arkusze dolne, przesuwanie w grach i karuzele zaimplementowane za pomocąViewPager2.
W Androidzie 10 lub nowszym odcięcia gestów systemowych zawierają dolne odcięcie dla gestu powrotu do ekranu głównego oraz lewe i prawe odcięcie dla gestów wstecznych:
Poniższy przykład kodu pokazuje, jak zaimplementować odcięcia 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
Wiele komponentów Material na Androida opartych na widokach
Android Material Components
(com.google.android.material) automatycznie obsługuje odcięcia, w tym
BottomAppBar,
BottomNavigationView,
NavigationRailView
i NavigationView
Jednak AppBarLayout
nie obsługuje automatycznie odcięć. Aby obsługiwać górne odcięcia, dodaj
android:fitsSystemWindows="true".
Dowiedz się, jak obsługiwać odcięcia za pomocą komponentów Material w Compose.
Wysyłanie odcięć zgodne wstecz
Aby zatrzymać wysyłanie odcięć do widoków podrzędnych i uniknąć nadmiernego dopełnienia, możesz
użyć stałej
WindowInsetsCompat.CONSUMED. Jednak na urządzeniach z Androidem 10 (API na poziomie 29 i starszym) odcięcia nie są wysyłane do elementów równorzędnych po wywołaniu WindowInsetsCompat.CONSUMED, co może powodować niezamierzone nakładanie się elementów wizualnych.
Aby potwierdzić, że odcięcia są wysyłane do elementów równorzędnych we wszystkich obsługiwanych wersjach Androida
, przed użyciem
odcięć użyj metody ViewGroupCompat#installCompatInsetsDispatch, która jest dostępna w
AndroidX Core i Core-ktx 1.16.0-alpha01
i nowszych.
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 wyświetlać na pełnym ekranie, aby zapewnić użytkownikowi bardziej wciągające wrażenia. Aby ukryć paski systemu w trybie pojemnym, możesz użyć
WindowInsetsController
i
WindowInsetsControllerCompat
bibliotek:
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 implementowaniu tej funkcji znajdziesz w artykule Ukrywanie pasków systemu w trybie pojemnym.
Ikony pasków systemu
Wywołanie funkcji enableEdgeToEdge zapewnia, że kolory ikon pasków systemu będą się aktualizować, gdy zmieni się motyw urządzenia.
Podczas wyświetlania bez ramki może być konieczne ręczne zaktualizowanie kolorów ikon pasków systemu, aby kontrastowały z tłem aplikacji. Aby na przykład utworzyć jasne ikony paska stanu:
Kotlin
WindowCompat.getInsetsController(window, window.decorView) .isAppearanceLightStatusBars = false
Java
WindowCompat.getInsetsController(window, window.getDecorView()) .setAppearanceLightStatusBars(false);
Ochrona pasków systemu
Gdy aplikacja jest kierowana na pakiet SDK 35 lub nowszy, wyświetlanie bez ramki jest wymuszane.
Pasek stanu systemu i paski nawigacji przy użyciu gestów są przezroczyste, ale pasek nawigacji z 3 przyciskami jest półprzezroczysty. Aby zapewnić zgodność wsteczną, wywołaj funkcję enableEdgeToEdge.
Jednak ustawienia domyślne systemu mogą nie działać we wszystkich przypadkach użycia. Aby określić, czy używać przezroczystych czy półprzezroczystych pasków systemu, zapoznaj się ze wskazówkami dotyczącymi projektowania pasków systemu na Androidzie i projektowania bez ramki.
Tworzenie przezroczystych pasków systemu
Aby utworzyć przezroczysty pasek stanu, kieruj aplikację na Androida 15 (SDK 35) lub nowszego albo wywołaj funkcję enableEdgeToEdge() z argumentami domyślnymi w przypadku starszych wersji.
Aby utworzyć przezroczysty pasek nawigacji przy użyciu gestów, kieruj aplikację na Androida 15 lub nowszego albo wywołaj funkcję enableEdgeToEdge() z argumentami domyślnymi w przypadku starszych wersji. W przypadku
paska nawigacji z 3 przyciskami ustaw wartość Window.setNavigationBarContrastEnforced
na false, w przeciwnym razie zostanie zastosowana półprzezroczysta nakładka.
Tworzenie półprzezroczystych pasków systemu
Aby utworzyć półprzezroczysty pasek stanu:
- Zaktualizuj zależność
androidx-coredo wersji 1.16.0-beta01 lub nowszej. - Owiń układ XML w element
androidx.core.view.insets.ProtectionLayouti przypisz mu identyfikator. - Programowo uzyskaj dostęp do elementu
ProtectionLayout, aby ustawić ochronę, określając stronę i elementGradientProtectiondla paska 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 przekazana do elementu GradientProtection jest zgodna z tłem treści. Na przykład układ szczegółowej listy wyświetlany na urządzeniu składanym może mieć różne GradientProtections o 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 nawigacji z 3 przyciskami, wykonaj jedną z tych czynności:
- Jeśli układ jest już owinięty w element
ProtectionView, możesz przekazać dodatkowy elementColorProtectionlubGradientProtectiondo metodysetProtections. Zanim to zrobisz, upewnij się, żewindow.isNavigationBarContrastEnforced = false. - W przeciwnym razie ustaw wartość
window.isNavigationBarContrastEnforced = true.
Inne wskazówki
Dodatkowe wskazówki dotyczące obsługi odcięć.
Wyświetlanie przewijanej treści bez ramki
Sprawdź, czy ostatni element listy nie jest zasłonięty przez paski systemu w elemencie RecyclerView lub NestedScrollView, obsługując odcięcia i ustawiając wartość clipToPadding na false.
Film poniżej przedstawia element RecyclerView z wyłączonym (po lewej) i włączonym (po prawej) wyświetlaniem bez ramki:
Przykładowy kod znajdziesz we fragmentach kodu w sekcji Tworzenie list dynamicznych za pomocą RecyclerView.
Wyświetlanie okien dialogowych na pełnym ekranie bez ramki
Aby wyświetlać okna dialogowe na pełnym ekranie bez ramki, wywołaj funkcję enableEdgeToEdge w oknie dialogowym.
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 bez ramki znajdziesz w tych materiałach.
Blogi
- Wskazówki dotyczące obsługi odcięć w przypadku wymuszania wyświetlania bez ramki na Androidzie 15
- WindowInsets – detektory układów
Design
Inna dokumentacja
Filmy