Перейти к содержимому

Fitssystemwindows android что это

  • автор:

WindowInsets и fitsSystemWindows

Любая активность размещается в специальном контейнере и пользователь видит её вместе с системными элементами — строкой состояния, панелью навигации. Всё вместе это контролируется классом 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); 

android:fitsSystemWindows=»true»

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

Данный флаг не будет иметь значения для многих видов макетов типа LinearLayout, FrameLayout. Но есть несколько видов макетов, к которым флаг применить можно. К таким макетам относятся DrawerLayout, CoordinatorLayout, AppBarLayout, CollapsingToolbarLayout, NavigationView.

Лучше вызывать метод onApplyWindowInsets().

Что делать не надо — вручную прописывать размеры строки состояния в ресурсах. Каждое устройство и каждая версия могут иметь свои размеры системных компонентов и ваша попытка видоизменить размеры приведёт к некрасивым последствиям в виде наслоений разных частей компонентов.

Для решения проблемы используйте методы getSystemWindowInsetLeft(), getSystemWindowInsetTop(), getSystemWindowInsetRight(), getSystemWindowInsetBottom() класса WindowInsets/WindowInsetsCompat.

При разработке собственного компонента можете использовать код:

 // Kotlin myView.setOnApplyWindowInsetListener // Ваш код для обработки отступов . val statusBarSize = insets.systemWindowInsetTop return insets.consumeSystemWindowInsets() > 

Как уже упоминалось выше, отступы не работают с некоторыми видами макетов. Вы можете переопределить поведение макета, наследуясь от основного макета и переопределяя метод.

 // Kotlin class CustomLayout : LinearLayout < override fun onApplyWindowInsets( insets: WindowInsets): WindowInsets < // Ваш код для обработки отступов . return insets.consumeSystemWindowInsets() >> 

BottomNavigationView

Как работать с нижней частью экрана рассказано в статье WindowInsets — Listeners to layouts (12 Apr 2019).

Возможно ли программно узнать значение параметра FitsSystemWindows?

Контент залезает под прозрачный статус бар. Есть ли способ программно знать какое было состояние до того, как я сделал его false?

Отслеживать

задан 29 июл 2022 в 10:05

Igor Novikov Igor Novikov

13 5 5 бронзовых знаков

можно узнать размеры любого View через соотв. методы после вызова onWindowsFocusChanged

29 июл 2022 в 12:13

Больше контекста. Работаю над SDK которая представляет собой фрагмент. Мне необходимо раскрыть его на весь экран. Использую val window = activity?.window ?: return и WindowCompat.setDecorFitsSystemWindows(window, false). Т.е мне нужно узнать какое значение для fitSystemWindow используется у активити мастер приложения .

Шаблон Navigation Drawer Activity

Статья устарела! Она редактировалась последний раз в 2015 году. С тех пор появился Koltin, а сам код модифицировался под использование Jetpack Navigation. Оставлена на память, потом будет удалена.

Рассмотрим шаблон Navigation Drawer Activity. Создадим новый проект и выберем нужный шаблон.

Navigation Drawer Activity

Для беглого знакомства можете сразу запустить проект и посмотреть его в действии. При запуске приложение выглядит как обычная программа «Hello World». Но есть и отличия. Нажмите на значок в виде трёх горизонтальных полосок в заголовке. Значок в документации называется «гамбургером» (Hamburger menu). Это официальная позиция Гугла. Но в реальности значок символизирует полосатых котов (никому не рассказывайте). При нажатии слева вылезет навигационная шторка. Шторка работает как обычная шторка в ванной. По высоте она занимает весь экран, включая системную область. Можете подвигать шторку вперёд-назад, чтобы увидеть, что верхняя кромка шторки в системной области полупрозрачна и не закрывает системные значки. Подобное поведение доступно на устройствах под Android 5 и выше. На старых устройствах шторка находится под системной панелью. Недавно стал проверять работу под Android 8.0 и увидел, что шторка теперь не закрывает системную панель. Ниже для сравнения я привёл два варианта.

Navigation Drawer Navigation Drawer

Сама шторка состоит из двух основных частей — в верхней части находится картинка и текст, а в нижней — меню со значками. Меню в свою очередь разделено на две группы. В верхней части значки можно выбрать и выбранный пункт останется выделенным. В нижней части меню пункты не выделяются. Уберите шторку обратно и вызовите теперь её не нажатием на значок гамбургера, а движением пальца от края экран в центр. Получилось? Отлично, а теперь выдвигайте шторку медленно и наблюдайте за значком гамбургера. Вы увидите, что во время движения значок трансформируется. К сожалению, шторка закрывает значок и непонятно, во что превращаются три полоски. А превращаются они в три кота ж! стрелку. Позже я покажу, как увидеть её. А может не покажу, я ещё не решил.

Возвращаемся в студию и начинаем изучать код проекта.

Если открыть файл activity_main.xml в режиме Design, то можно увидеть, как будет выглядеть приложение с открытой шторкой.

Небольшие расхождения имеются, но в целом совпадает с реальным приложением.

Посмотрим на его содержание.

Сейчас важно запомнить, что за выдвигающую шторку отвечает элемент NavigationView, который входит последним в контейнере DrawerLayout и представляет собой навигационное меню. А перед меню находится вставка include, указывающая на разметку app_bar_main.xml.

Атрибут tools:openDrawer позволяет указать студии, что навигационное меню нужно отобразить в раскрытом виде в режиме просмотра разметки.

NavigationView

В 2014 году Google показал новый дизайн и различные новые примеры по навигации. Но вначале они использовали подручные средства, которые были под рукой — фрагменты.

Спустя год компания разработала на основе предка FrameLayout новый компонент NavigationView, который стал частью библиотеки Android Design Support Library.

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

Необходимые рекомендации по созданию навигационной выдвижной шторки можно найти на странице Navigation drawer — Patterns.

Перейдём к деталям.

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

Тег NavigationView содержит ссылку на собственную разметку в атрибуте app:headerLayout, который указывает на файл nav_header_main.xml (верхняя часть шторки), а также на меню в атрибуте app:menu, который ссылается на ресурс меню menu/activity_main_drawer.xml.

Откроем файл nav_header_main.xml и посмотрим на разметку шторки.

Разметка состоит из ImageView и двух TextView, размещённых в контейнере LinearLayout. Фон контейнера определён в ресурсе drawable/side_nav_bar.xml и представляет собой градиент.

Остальные атрибуты понятны и не требуют пояснений.

Можно (но не нужно) настроить верхнюю часть шторки не через XML, а программно.

 NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); // Наполняем шапку элементами View headerLayout = navigationView.inflateHeaderView(R.layout.nav_header_main); ImageView headerImageView = headerLayout.findViewById(R.id.imageView); 

После обновления одной из версий библиотеки Design Support, доступ к шапке осуществляется теперь через другой код.

 View headerLayout = navigationView.getHeaderView(0); 

Теперь рассмотрим ресурс навигационного меню res/menu/activity_main_drawer.xml.

Принцип создания элементов меню остался стандартным. Каждый пункт меню представляет собой тег item с указанием значка и текста. Для группировки используется элемент group. Поведение элементов меню в группе регулируется атрибутом android:checkableBehavior. В примере используется значение single — при нажатии на пункт меню, он останется выделенным (принцип переключателя RadioButton). Всего доступно три варианта.

  • single – можно выбрать один элемент группы (переключатель)
  • all — можно выбрать все элементы группы (флажок)
  • none – элементы не выбираются

В библиотеке Android Support Design версии 23 вариант all не работает и будет действовать, как со значением single.

Также следует обратить внимание, что теперь проект ссылается на векторные рисунки, которые находятся в папке drawable-21.

Осталось рассмотреть тег include, который ссылается на файл ресурса res/layout/app_bar_main.xml. Он вам будет знаком по шаблону Blank Activity, который мы изучали в статье Библиотека Android Support Design. Только там он находился в файле activity_main.xml, а здесь его перенесли в файл app_bar_main.xml. Всё остальное осталось без изменений. Повторяться не будем.

Теперь изучим код активности для работы со шторкой.

В классе активности реализуется интерфейс OnNavigationItemSelectedListener с его методом onNavigationItemSelected():

 public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener < @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) < // Handle navigation view item clicks here. int if (id == R.id.nav_camara) < // Handle the camera action >else if (id == R.id.nav_gallery) < >else if (id == R.id.nav_slideshow) < >else if (id == R.id.nav_manage) < >else if (id == R.id.nav_share) < >else if (id == R.id.nav_send) < >DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; > > 

Логика кода ничем не отличается о работы с обычным меню. Определяется идентификатор выбранного пункта и далее вам нужно написать свой код. Затем вызывается метод closeDrawer() для закрытия шторки.

Добавим код для первого пункта меню.

 if (id == R.id.nav_camera) < // Handle the camera action Toast.makeText(getApplicationContext(), "Вы выбрали камеру", Toast.LENGTH_SHORT).show(); >

При нажатии кнопки «Назад» проверяется состояние шторки. Если шторка открыта (isDrawerOpen()), то её закрываем с помощью метода closeDrawer().

 @Override public void onBackPressed() < DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) < drawer.closeDrawer(GravityCompat.START); >else < super.onBackPressed(); >> 

В методе onCreate() происходит инициализация шторки.

 @Override protected void onCreate(Bundle savedInstanceState)

Теперь поговорим об изменениях, которые можно внести в проект.

Хотите выдвигать шторку справа? Установите значение end у атрибута layout_gravity. Обычно используется для стран с обратным порядком букв.

NavigationView end

На самом деле смысла в этом не оказалось. Да, шторка выдвигается вручную. Но если нажать на значок гамбургера, то приложение валится с ошибкой. Любое нажатие в меню шторки также приводит к ошибке. Теоретически можно написать код, который исправит проблему, но он будет сложным. Забудьте об этом совете.

Тем не менее, можно реализовать забавный эффект — добавить вторую шторку на экран. Первая будет работать главной и реагировать на нажатие значка, а вторая будет дополнительной для вывода какой-то информации. Достаточно в разметку добавить второй NavigationView с атрибутом android:layout_gravity=»end»

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

NavigationView

Для изменения цвета значков и текста в навигационном меню используйте атрибуты app:itemIconTint и app:itemTextColor.

Данным атрибутам соответствуют методы setItemIconTintList() и setItemTextColor() компонента NavigationView.

NavigationView tint

Так как наличие шторки не совсем очевидно, можно при первом запуске показать шторку в раскрытом состоянии. Далее можно запомнить состояние шторки при первом запуске в настройках, чтобы во второй раз выводить её уже в закрытом состоянии. Можете самостоятельно реализовать эту возможность.

 // показываем в открытом состоянии в onCreate() drawer.openDrawer(GravityCompat.START); 

Напоследок покажу превращение значка гамбургера в стрелку в явном виде, как и обещал в начале статьи. Напомню, что по рекомендации Material Design шторка должна закрывать всю область экрана. Но если вы хотите поместить шторку под заголовком, то следует немного поправить разметку. Откроем файл app_bar_main.xml и вырежем из него небольшой кусок. Затем в файле activity_main.xml добавим LinearLayout в качестве корневого контейнера и вставим скопированный ранее кусок кода.

            

Смотрим на значок.

Сама анимация значка зависит от переменной toggle (объект класса ActionBarDrawerToggle). Если вы её уберёте, то никакого значка в заголовке приложения не будет.

Можно поменять цвет значка гамбургера. Откроем файл стилей res/values/styles.xml и добавим:

     

Элемент spinBars со значением true позволяет использовать анимацию. В противном случае значок будет статичным.

Навигация

В шаблоне присутствует метод onNavigationItemSelected() с аннотацией @SuppressWarnings(«StatementWithEmptyBody») (Оператор с пустым телом). Нам нужно добавить свой код для навигации, который должен реагировать на нажатия в меню шторки. Нам понадобятся фрагменты. Для примера создадим первый фрагмент.

 package ru.alexanderklimov.navigationdrawerdemo; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class FirstFragment extends Fragment < @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) < return inflater.inflate(R.layout.fragment_first, container, false); >> 

Разметка для первой активности.

По такому же образу создайте второй фрагмент SecondFragment и т.д.

Определим RelativeLayout в файле content_main.xml в качестве контейнера.

Теперь можем написать недостающий код для навигации по фрагментам в MainActivity.

 @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) < // Создадим новый фрагмент Fragment fragment = null; Class fragmentClass = null; // Handle navigation view item clicks here. int if (id == R.id.nav_camera) < // Handle the camera action fragmentClass = FirstFragment.class; >else if (id == R.id.nav_gallery) < fragmentClass = SecondFragment.class; >else if (id == R.id.nav_slideshow) < >else if (id == R.id.nav_manage) < >else if (id == R.id.nav_share) < >else if (id == R.id.nav_send) < >try < fragment = (Fragment) fragmentClass.newInstance(); >catch (Exception e) < e.printStackTrace(); >// Вставляем фрагмент, заменяя текущий фрагмент FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction().replace(R.id.container, fragment).commit(); // Выделяем выбранный пункт меню в шторке item.setChecked(true); // Выводим выбранный пункт в заголовке setTitle(item.getTitle()); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; > 

Добавляем счётчик в меню шторки

Откройте файл res/menu/activity_main_drawer.xml и добавьте атрибут app:actionViewClass=»android.widget.TextView» ко второму и третьему элементу меню из шаблона. Теперь эти элементы будут связаны с текстовыми метками.

Объявим текстовые метки и инициализируем их в методе onCreate(). В отдельном методе будем управлять их свойствами.

 private TextView mSlideshowTextView; private TextView mGalleryTextView; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); . mGalleryTextView = (TextView) MenuItemCompat.getActionView(navigationView.getMenu(). findItem(R.id.nav_gallery)); mSlideshowTextView = (TextView) MenuItemCompat.getActionView(navigationView.getMenu(). findItem(R.id.nav_slideshow)); // метод для счетчиков initializeCountDrawer(); >private void initializeCountDrawer()

Счетчик в NavigationView

Вы можете переделать метод под себя, чтобы динамически изменять показания счётчика.

Сдвигаем содержимое экрана

При выдвижении шторки можно сдвинуть основное содержание. Потребуется небольшая модификация кода. Для начала нужно присвоить идентификатор контейнеру ConstraintLayout в content_main.xml.

В MainActivity добавим экземпляру ActionBarDrawerToggle метод onDrawerSlide() и сдвинем содержимое на определённую величину. При желании можно также изменить размер, используя второй параметр метода slideOffset.

 final ConstraintLayout content = findViewById(R.id.content); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string .navigation_drawer_close) < @Override public void onDrawerSlide(View drawerView, float slideOffset) < super.onDrawerSlide(drawerView, slideOffset); float slideX = drawerView.getWidth() * slideOffset; content.setTranslationX(slideX); // а также меняем размер content.setScaleX(1 - slideOffset); content.setScaleY(1 - slideOffset); >>; drawer.addDrawerListener(toggle); toggle.syncState(); 

Дополнительное чтение

Библиотека mxn21/FlowingDrawer с прикольным эффектом.

Android Studio Tutorial – Bottom Sheet

[:ru]В этом уроке вы узнаете, как реализовать в андроид-приложении элемент bottom sheet из библиотеки материального дизайна. Bottom sheet переводится как “нижний слой” или “нижний лист”. Это информационная панель. заголовок которой появляется внизу экрана. Основное содержимое панели можно увидеть, потянув bottom sheet вверх за заголовок.

import android.support.design.widget.BottomSheetBehavior; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity implements View.OnClickListener < private BottomSheetBehavior mBottomSheetBehavior; boolean isClick=false; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); View bottomSheet = findViewById(R.id.bottom_sheet); mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet); Button button1 = (Button)findViewById(R.id.button_1); Button button2 = (Button)findViewById(R.id.button_2); Button button3 = (Button)findViewById(R.id.button_3); button1.setOnClickListener(this); button2.setOnClickListener(this); button3.setOnClickListener(this); >@Override public void onClick(View view) < switch (view.getId())< case R.id.button_1: if(isClick==false) mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); else mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); isClick=!isClick; break; default: break; >> >

[:en]In this lesson you will learn how to implement in Android application element bottom sheet from material design library. This is an informational panel. a title which appears at the bottom of the screen. The main toolbar contents can be seen by pulling the bottom sheet up over the title.

import android.support.design.widget.BottomSheetBehavior; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity implements View.OnClickListener < private BottomSheetBehavior mBottomSheetBehavior; boolean isClick=false; @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); View bottomSheet = findViewById(R.id.bottom_sheet); mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet); Button button1 = (Button)findViewById(R.id.button_1); Button button2 = (Button)findViewById(R.id.button_2); Button button3 = (Button)findViewById(R.id.button_3); button1.setOnClickListener(this); button2.setOnClickListener(this); button3.setOnClickListener(this); >@Override public void onClick(View view) < switch (view.getId())< case R.id.button_1: if(isClick==false) mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); else mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); isClick=!isClick; break; default: break; >> >

Вам також може сподобатися

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *