Отображение контента от края до края в представлениях

Попробуйте способ Compose
Jetpack Compose — рекомендуемый набор инструментов пользовательского интерфейса для Android. Узнайте, как работать с Edge-to-Edge в Compose.

После того, как вы нацелитесь на SDK 35 или выше на устройстве под управлением Android 15 или выше, ваше приложение будет отображаться от края до края. Окно охватывает всю ширину и высоту дисплея, рисуясь за системными панелями. Системные панели включают строку состояния, строку заголовка и панель навигации.

У многих приложений есть верхняя панель приложений. Верхняя панель приложений должна растягиваться до верхнего края экрана и отображаться за строкой состояния. При желании верхняя панель приложений может сжиматься до высоты строки состояния при прокрутке содержимого.

Во многих приложениях также есть нижняя панель приложений или нижняя панель навигации. Эти панели также должны растягиваться до нижнего края экрана и отображаться за панелью навигации. В противном случае приложения должны показывать прокручиваемый контент за панелью навигации.

Рисунок 1. Системные панели в компоновке «от края до края».

При реализации макета «от края до края» в вашем приложении помните следующее:

  1. Включить дисплей от края до края
  2. Устраните любые визуальные наложения.
  3. Рассмотрите возможность показа сеток за системными решетками.
пример изображения за строкой состояния
Рисунок 2. Пример изображения за строкой состояния.

Включить отображение от края до края

Если ваше приложение ориентировано на SDK 35 или более позднюю версию, Edge-to-Edge автоматически включается для устройств Android 15 или более поздней версии.

Чтобы включить функцию Edge-to-Edge на предыдущих версиях Android, выполните следующие действия:

  1. Добавьте зависимость к библиотеке androidx.activity в файле build.gradle вашего приложения или модуля:

    Котлин

    dependencies {
        val activity_version = activity_version
        // Java language implementation
        implementation("androidx.activity:activity:$activity_version")
        // Kotlin
        implementation("androidx.activity:activity-ktx:$activity_version")
    }

    Круто

    dependencies {
        def activity_version = activity_version
        // Java language implementation
        implementation 'androidx.activity:activity:$activity_version'
        // Kotlin
        implementation 'androidx.activity:activity-ktx:$activity_version'
    }
  2. Импортируйте функцию расширения enableEdgeToEdge в свое приложение:

Вручную включите edge-to-edge, вызвав enableEdgeToEdge в onCreate вашего Activity . Его следует вызывать до setContentView .

Котлин

     override fun onCreate(savedInstanceState: Bundle?) {
       enableEdgeToEdge()
       super.onCreate(savedInstanceState)
       ...
     }
   

Ява

     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
       EdgeToEdge.enable(this);
       super.onCreate(savedInstanceState);
       ...
     }
   

По умолчанию enableEdgeToEdge() делает системные панели прозрачными, за исключением режима навигации с 3 кнопками, где строка состояния становится полупрозрачной сеткой. Цвета системных значков и сетки настраиваются на основе системной светлой или темной темы.

Функция enableEdgeToEdge() автоматически объявляет, что приложение должно быть размещено от края до края, и настраивает цвета системных панелей.

Чтобы включить отображение от края до края в вашем приложении без использования функции enableEdgeToEdge() , см . раздел Ручная настройка отображения от края до края .

Обработка перекрытий с помощью вставок

Некоторые из представлений вашего приложения могут отображаться за системными панелями, как показано на рисунке 3.

Вы можете устранить перекрытия, реагируя на вставки, которые указывают, какие части экрана пересекаются с системным пользовательским интерфейсом, таким как панель навигации или строка состояния. Пересечение может означать отображение над содержимым, но оно также может информировать ваше приложение о системных жестах.

Типы вставок, которые применяются для отображения вашего приложения от края до края:

  • Вставки системных панелей: лучше всего подходят для видов, на которые можно нажимать и которые не должны визуально закрываться системными панелями.

  • Вырезы на дисплее: для областей, где из-за формы устройства может быть вырез на экране.

  • Вставки системных жестов: для областей навигации с помощью жестов, используемых системой, которые имеют приоритет над вашим приложением.

Вставки системных панелей

Вставки системной панели являются наиболее часто используемым типом вставок. Они представляют собой область, в которой системный пользовательский интерфейс отображается на оси Z над вашим приложением. Их лучше всего использовать для перемещения или заполнения представлений в вашем приложении, которые можно нажимать и которые не должны визуально закрываться системными панелями.

Например, плавающая кнопка действия (FAB) на рисунке 3 частично закрыта панелью навигации:

пример реализации Edge-to-Edge, но навигационная панель закрывает FAB
Рисунок 3. Панель навигации, перекрывающая FAB в макете «от края до края».

Чтобы избежать такого визуального наложения в режиме жестов или кнопок, можно увеличить поля представления с помощью getInsets(int) с WindowInsetsCompat.Type.systemBars() .

В следующем примере кода показано, как реализовать вставки системной панели:

Котлин

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
}

Ява

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;
});

Если применить это решение к примеру, показанному на рисунке 3, то в режиме кнопок визуального перекрытия не возникнет, как показано на рисунке 4:

полупрозрачная навигационная панель, не закрывающая FAB
Рисунок 4. Устранение визуального наложения в кнопочном режиме.

То же самое относится и к режиму навигации с помощью жестов, как показано на рисунке 5:

от края до края с навигацией жестами
Рисунок 5. Устранение визуального наложения в режиме навигации с помощью жестов.

Вставки для вырезов на дисплее

На некоторых устройствах есть вырезы на дисплее. Обычно вырез находится в верхней части экрана и включен в строку состояния. Когда экран устройства находится в ландшафтном режиме, вырез может быть на вертикальном краю. В зависимости от содержимого, которое ваше приложение отображает на экране, вам следует реализовать отступы, чтобы избежать вырезов на дисплее, так как по умолчанию приложения будут рисовать в вырезе на дисплее.

Например, многие экраны приложений показывают список элементов. Не закрывайте элементы списка вырезом на дисплее или системными панелями.

Котлин

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
}

Ява

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;
});

Определите значение WindowInsetsCompat , взяв логическое ИЛИ системных панелей и типов вырезов дисплея.

Установите clipToPadding для RecyclerView , чтобы padding прокручивался вместе с элементами списка. Это позволяет элементам заходить за системные панели, когда пользователь прокручивает, как показано в следующем примере.

<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" />

Системные вставки жестов

Вставки системных жестов представляют области окна, где системные жесты имеют приоритет над вашим приложением. Эти области показаны оранжевым цветом на рисунке 6:

Пример вставки системных жестов
Рисунок 6. Вставки системных жестов.

Как и в случае с вставками системной панели, вы можете избежать перекрытия вставок системных жестов, используя getInsets(int) с WindowInsetsCompat.Type.systemGestures() .

Используйте эти вставки для перемещения или отступа прокручиваемых представлений от краев. Обычные варианты использования включают нижние листы , пролистывание в играх и карусели, реализованные с помощью ViewPager2 .

В Android 10 и более поздних версиях системные вставки жестов содержат нижнюю вставку для жеста «Домой», а также левую и правую вставки для жестов «Назад»:

пример измерения вставки жестов системы
Рисунок 7. Измерения вставки жестов системы.

В следующем примере кода показано, как реализовать вставки системных жестов:

Котлин

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
}

Ява

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;
});

Материальные компоненты

Многие компоненты Android Material на основе представлений (com.google.android.material) автоматически обрабатывают вставки, включая BottomAppBar , BottomNavigationView , NavigationRailView и NavigationView

Однако AppBarLayout не обрабатывает вставки автоматически. Добавьте android:fitsSystemWindows="true" для обработки верхних вставок.

Узнайте, как обрабатывать вставки с компонентами материалов в Compose .

Обратная совместимость вставной диспетчеризации

Чтобы остановить отправку вставок в дочерние представления и избежать избыточного заполнения, вы можете использовать вставки с помощью константы WindowInsetsCompat.CONSUMED . Однако на устройствах под управлением Android 10 (уровень API 29 и более ранние) вставки не отправляются в родственные элементы после вызова WindowInsetsCompat.CONSUMED , что может привести к непреднамеренному визуальному наложению.

Пример отправки нерабочей вставки
Рисунок 8. Пример отправки неработающей вставки. Вставки не отправляются в родственные представления после того, как ViewGroup 1 потребляет вставки на Android 10 (уровень API 29) и более ранних версиях, что приводит к перекрытию TextView 2 системной панелью навигации. Однако вставки отправляются в родственные представления на Android 11 (уровень API 30) и более поздних версиях, как и ожидалось.

Чтобы убедиться, что вставки отправляются на родственные устройства для всех поддерживаемых версий Android, используйте ViewGroupCompat#installCompatInsetsDispatch перед использованием вставок, доступных в AndroidX Core и Core-ktx 1.16.0-alpha01 и выше.

Котлин

// 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)

Ява

// 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);
Пример отправки исправленной вставки
Рисунок 9. Исправлена ​​диспетчеризация вставок после вызова ViewGroupCompat#installCompatInsetsDispatch.

Режим погружения

Некоторый контент лучше всего воспринимается в полноэкранном режиме, что дает пользователю более захватывающий опыт. Вы можете скрыть системные панели для иммерсивного режима с помощью библиотек WindowInsetsController и WindowInsetsControllerCompat :

Котлин

val windowInsetsController =
      WindowCompat.getInsetsController(window, window.decorView)

// Hide the system bars.
windowInsetsController.hide(Type.systemBars())

// Show the system bars.
windowInsetsController.show(Type.systemBars())

Ява

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());

Дополнительную информацию о реализации этой функции см. в разделе Скрытие системных панелей для режима погружения .

Значки системной панели

Вызов enableEdgeToEdge обеспечивает обновление цветов значков системной панели при изменении темы устройства.

При переходе от края к краю вам может потребоваться вручную обновить цвета значков системной панели, чтобы они контрастировали с фоном вашего приложения. Например, чтобы создать светлые значки строки состояния:

Котлин

WindowCompat.getInsetsController(window, window.decorView)
    .isAppearanceLightStatusBars = false

Ява

WindowCompat.getInsetsController(window, window.getDecorView())
    .setAppearanceLightStatusBars(false);

Защита системной панели

Как только ваше приложение станет ориентировано на SDK 35 или выше, будет применен edge-to-edge . Строка состояния системы и панели навигации жестов прозрачны, но панель навигации с тремя кнопками полупрозрачна. Вызовите enableEdgeToEdge , чтобы сделать это обратно совместимым.

Однако системные настройки по умолчанию могут не работать во всех случаях использования. Ознакомьтесь с руководством по дизайну системных панелей Android и дизайном Edge-to-edge, чтобы определить, следует ли использовать прозрачные или полупрозрачные системные панели.

Создать прозрачные системные панели

Создайте прозрачную строку состояния, указав версию Android 15 (SDK 35) или выше или вызвав enableEdgeToEdge() с аргументами по умолчанию для более ранних версий.

Создайте прозрачную панель навигации жестов, нацелившись на Android 15 или выше или вызвав enableEdgeToEdge() с аргументами по умолчанию для более ранних версий. Для трехкнопочной панели навигации установите Window.setNavigationBarContrastEnforced на false , иначе будет применена полупрозрачная сетка.

Создание полупрозрачных системных панелей

Чтобы создать полупрозрачную строку состояния, выполните следующие действия:

  1. Обновите зависимость androidx-core до версии 1.16.0-beta01 или выше.
  2. Оберните свой XML-макет в androidx.core.view.insets.ProtectionLayout и назначьте идентификатор.
  3. Программно обращайтесь к ProtectionLayout , чтобы установить защиту, указав сторону и GradientProtection для строки состояния.

<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>

val red = 52
val green = 168
val blue = 83
findViewById<ProtectionLayout>(R.id.list_protection)
    .setProtections(
        listOf(
            GradientProtection(
                WindowInsetsCompat.Side.TOP,
                // Ideally, this is the pane's background color
                // alpha = 204 for an 80% gradient
                Color.argb(204, red, green, blue)
            )
        )
    )

Убедитесь, что ColorInt , переданный в GradientProtection соответствует фону контента. Например, макет списка-детали, отображаемый на складном устройстве, может иметь разные GradientProtections разных цветов для панели списка и панели деталей.

Рисунок 1. Градиентная защита разных цветов.

Не создавайте полупрозрачную панель навигации жестов. Чтобы создать полупрозрачную панель навигации с тремя кнопками, выполните одно из следующих действий:

  • Если ваш макет уже обернут в ProtectionView , вы можете передать дополнительный ColorProtection или GradientProtection в метод setProtections . Перед этим убедитесь, что window.isNavigationBarContrastEnforced = false .
  • В противном случае установите window.isNavigationBarContrastEnforced = true . Если ваше приложение вызывает enableEdgeToEdge, window.isNavigationBarContrastEnforced = true является значением по умолчанию.

Другие советы

Убедитесь, что последний элемент списка не закрыт системными панелями в RecyclerView или NestedScrollView , обработав вставки и установив clipToPadding в false .

На следующем видео показан RecyclerView с отключенным (слева) и включенным (справа) дисплеем от края до края:

Пример кода см. в разделе Создание динамических списков с помощью RecyclerView .

Дополнительные ресурсы

Дополнительную информацию о переходе от края к краю можно найти в следующих ссылках.

Блоги

Дизайн

Другая документация

Видео