Освой Android играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Любая активность размещается в специальном контейнере и пользователь видит её вместе с системными элементами - строкой состояния, панелью навигации. Всё вместе это контролируется классом Window. Но иногда требуется изменить стандартное поведение. Самый простой пример - игры в полноэкранном режиме, когда мы хотим скрыть все лишние элементы с экрана. Другой вариант - полноэкранный режим, но системные элементы накладываются поверх игры. Ещё один вариант - динамическое изменение, когда пользователь начинает прокручивать список вверх, то системная строка состояния исчезает, а при прокрутке вниз снова появляется.
Любая активность может получить доступ к окну Window через метод Activity#getWindow().
Window window = this.getWindow();
System.out.println(window.getClass().getSimpleName()); // PhoneWindow
Аналогично класс Dialog имеет своё окно Dialog#getWindow().
В свою очередь окно содержит всю необходимую информацию о размерах системных элементов и подстраивает все дочерние элементы (вашу активность), чтобы не было нигде зазоров или наложений друг на друга. Реализуется взаимное расположение при помощи специальных отступов Inset.
Поведение Window в разных версиях Android менялась. До KitKat была упрощённая версия не учитывала системные элементы управления. Доступ к ним осуществлялся через метод setSystemUiVisibility(). Также было множество флагов настроек: SYSTEM_UI_FLAG_VISIBLE, SYSTEM_UI_FLAG_FULLSCREEN и другие.
Начиная с KitKat, Android стала поддерживать полупрозрачные системные панели. Появились атрибуты androd:windowTranslucentStatus, android:windowsTranslucentNavigation.
В Lollipop происходит дальнейшая интеграция, системные элементы входят в состав Window и появилась возможность управлять фоном через android:windowDrawsSystemBarBackgrounds.
Мы можем узнать цвета системных элементов.
ImageView image = findViewById(R.id.imageView);
Window window = this.getWindow();
int navigationColor = window.getNavigationBarColor();
int statusBarColor = window.getStatusBarColor();
image.setBackgroundColor(statusBarColor);
Данный флаг использовался для этой же цели. Сейчас в документации указано, что флаг считается устаревшим с версии API 20.
Данный флаг не будет иметь значения для многих видов макетов типа LinearLayout, FrameLayout. Но есть несколько видов макетов, к которым флаг применить можно. К таким макетам относятся DrawerLayout, CoordinatorLayout, AppBarLayout, CollapsingToolbarLayout, NavigationView.
Лучше вызывать метод onApplyWindowInsets().
Что делать не надо - вручную прописывать размеры строки состояния в ресурсах. Каждое устройство и каждая версия могут иметь свои размеры системных компонентов и ваша попытка видоизменить размеры приведёт к некрасивым последствиям в виде наслоений разных частей компонентов.
Для решения проблемы используйте методы getSystemWindowInsetLeft(), getSystemWindowInsetTop(), getSystemWindowInsetRight(), getSystemWindowInsetBottom() класса WindowInsets/WindowInsetsCompat.
При разработке собственного компонента можете использовать код:
// Kotlin
myView.setOnApplyWindowInsetListener{view, insets ->
// Ваш код для обработки отступов
...
val statusBarSize = insets.systemWindowInsetTop
return insets.consumeSystemWindowInsets()
}
Как уже упоминалось выше, отступы не работают с некоторыми видами макетов. Вы можете переопределить поведение макета, наследуясь от основного макета и переопределяя метод.
// Kotlin
class CustomLayout : LinearLayout {
override fun onApplyWindowInsets(
insets: WindowInsets): WindowInsets {
// Ваш код для обработки отступов
...
return insets.consumeSystemWindowInsets()
}
}
Как работать с нижней частью экрана рассказано в статье WindowInsets — Listeners to layouts (12 Apr 2019).
droidcon NYC 2017 - Becoming a master window fitter🔧 - YouTube