Immersive Mode (режим погружения) в Android 4.4 KitKat

31 октября 2013 года Google представили новую версию ОС Android 4.4 KitKat, в которой появилось очень много интересных функций для разработчиков. Одна из этих функций — Immersive Mode (режим погружения). Режим погружения — это режим, в котором ваша программа показывается пользователю на весь экран, при этом не видны любые системные панели, в том числе и панель навигации (та, что с кнопкой Back). До Android 4.4 была также возможность скрывать системные панели (панель навигации и панель статуса). Но в существующем варианте был один недостаток — пользователь не мог полностью погрузится в контент, так как любое нажатие на контент снова вызывало отображение системных панелей. Новый режим погружения добавляет еще один способ взаимодействия с отображением системных панелей. В этом режиме, чтобы отобразить системные панели пользователю достаточно сделать свайп от верхнего или нижнего края экрана по направлению к центру экрана, при этом отображение панелей по нажатию на контент делать необязательно, тем самым можно создавать приложения и игры, в которых пользователь может полноценно взаимодействовать с приложением или игрой любыми жестами. Чтобы пользователь понял, как вызвать системные панели из полноэкранного режима, при первом запуске приложения ему автоматически покажется системное сообщение о том, как снова отобразить эти панели на экране.

image image

Также вы могли подумать, что есть другая проблема использования этого режима. Например, пользователь запустил программу для рисования и хочет нарисовать линию от края экрана и как же так, неужели ему в этом случае отобразится системная панель. Да, но при этом приложение также будет получать сообщения от нажатий на экран и будет рисовать линию. Кстати, теперь можно просто сделать системные панели полупрозрачными с помощью двух новых тем — Theme.Holo.NoActionBar.TranslucentDecor и Theme.Holo.Light.NoActionBar.TranslucentDecor. Но стоит помнить, что в этом случае ваш контент будет занимать всю область на экране и будет видим даже за полупрозрачными системными панелями. Если вам необходимо, чтобы какая-та часть интерфейса не заезжала за пределы системных панелей (например, какие-то дополнительные панели), вам необходимо указать для родительского layout этого контента атрибут fitsSystemWindows равным true. Не очень приятная вещь с этими темами в том, что тут используется неполноценная полупрозрачность для панелей, а градиентная (от черного цвета к полупрозрачному), что не всегда выглядит красиво. Если вы создаете свою кастомную тему, вы можете отнаследоваться от этих новых тем, либо добавить вручную два новых свойства windowTranslucentNavigation (прозрачна ли системная панель навигации) и windowTranslucentStatus (прозрачна ли системная панель статуса с часиками).

А как же снова попасть в полноэкранный режим, когда панели уже отображаются? У пользователя есть несколько вариантов — нажать на любой контент вне системных панелей, подождать некоторое время, чтобы панели скрылись автоматически, или сделать свайп от центра экрана к краю. Все эти варианты программист может предусмотреть в своем приложении. Для этого команда Android сделала так, чтобы эти способы взаимодействия с Immersive Mode можно было реализовать очень просто. Есть два типа режима погружения — обычный и sticky («липкий»). В обычном режиме программисту надо самому решать, когда скрывать панели, а в sticky-режиме системные панели будут скрываться автоматически. Для установки обоих типов режима погружения необходимо воспользоваться методом setSystemUiVisibility(), только передать в параметры разные флаги. Вызывать этот метод необходимо у так называемого Decor View, который представляет собой внешний вид окна Activity со всем его оформлением и содержимым. Ссылку на этот View получить очень просто.

mDecorView = getWindow().getDecorView();

Давайте рассмотрим оба типа режима погружения поподробнее.

Обычный Immersive Mode


Для установки обычного режима используется флаг SYSTEM_UI_FLAG_IMMERSIVE в качестве параметра метода setSystemUiVisibility().

Лучше всего для работы с обычным immersive-режимом сделать два простых метода, которые будут показывать и скрывать системные панели соответственно.

    private void hideSystemUI() {
        mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LOW_PROFILE
                | View.SYSTEM_UI_FLAG_IMMERSIVE);
    }

    private void showSystemUI() {
        mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    }

Для тех случаев, когда показывать и скрывать системные панели вам нужно по нажатию на контент (contentView), необходимо использовать следующий код. Это может пригодится, например, при просмотре видео, как в YouTube, где вам не нужно взаимодействовать с контентом во время его просмотра.

        contentView.setClickable(true);
        final GestureDetector clickDetector = new GestureDetector(this,
                new GestureDetector.SimpleOnGestureListener() {
                    @Override
                    public boolean onSingleTapUp(MotionEvent e) {
                        boolean visible = (mDecorView.getSystemUiVisibility()
                                & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
                        if (visible) {
                            hideSystemUI();
                        } else {
                            showSystemUI();
                        }
                        return true;
                    }
                });
        contentView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                return clickDetector.onTouchEvent(motionEvent);
            }
        });

Если при этом у вас в приложении есть какие-то свои панели (controlsView), которые тоже надо скрыть вместе с системными, вы это также можете просто сделать, написав следующий код. Тут скрытие и появление происходит с использованием анимации альфа-канала и позиции по Y вашей панели.

        mDecorView.setOnSystemUiVisibilityChangeListener(
                new View.OnSystemUiVisibilityChangeListener() {
                    @Override
                    public void onSystemUiVisibilityChange(int flags) {
                        boolean visible = (flags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
                        controlsView.animate()
                                .alpha(visible ? 1 : 0)
                                .translationY(visible ? 0 : controlsView.getHeight());
                    }
                });

Если при запуске приложения вы хотите показать пользователю, что у приложения есть панели, а потом их скрыть через некоторое время, это тоже довольно просто делается.

    private static final int INITIAL_HIDE_DELAY = 300;
    
    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        delayedHide(INITIAL_HIDE_DELAY);
    }

    private final Handler mHideHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            hideSystemUI();
        }
    };

    private void delayedHide(int delayMillis) {
        mHideHandler.removeMessages(0);
        mHideHandler.sendEmptyMessageDelayed(0, delayMillis);
    }

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

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);

        if (hasFocus) {
            delayedHide(INITIAL_HIDE_DELAY);
        } else {
            mHideHandler.removeMessages(0);
        }
    }

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

Immersive Mode Sticky


Для установки sticky-режима используется флаг SYSTEM_UI_FLAG_IMMERSIVE_STICKY в качестве параметра метода setSystemUiVisibility().

Этот режим чрезвычайно прост. Если вам необходима ситуация, при которой системные панели будут скрываться, когда окно получает фокус или проходит некоторое время после появлении панелей, то этот режим для вас. Для включения этого режима вам необходимо установить для окна, когда оно находится в фокусе, флаг SYSTEM_UI_FLAG_IMMERSIVE_STICKY. В этом режиме, если окно теряет фокус, оно автоматически сбрасывается до состояния, в котором окно было до установки флага SYSTEM_UI_FLAG_IMMERSIVE_STICKY, то есть со всеми панелями. Как только окно получает фокус, панели опять исчезают через некоторое время и вам нет нужды программировать эту ситуацию вручную. Чтобы показать системные панели надо также, как и в обычном режиме, сделать свайп от краев экрана. Включение stick-режима делается простым куском кода.

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    }


P.S. В статьи использовались материалы видео от Roman Nurik. Код полных примеров от Романа по использованию этого режима можно скачать тут.

P.P.S. 20 ноября появилась новая статья в Training Kit по этому режиму. Постараюсь дополнить статью в ближайшее время с учетом и ее.
Поделиться публикацией

Комментарии 35

    –7
    Спасибо за пост. Надо попробовать реализовать в Robird
      +4
      Думаешь twitter-клиент подходит, как use-case для immersive mode? Я правда в твиттере мало пишу, неужели там нужно полное погружение в твиты? :)
        +1
        Как посмотреть. С одной стороны все таки читалка твитов. Может как опцию сделать.
          +6
          Вот как опцию вполне можно, но точно не по умолчанию.
      +1
      В CyanogenMod есть аналогичная опция «расширенный экран». Можно включить её через меню, при зажатии кнопки выключения, и работает она на всю операционную систему, не только на приложение.
      И это 1 из немногих фич которых не хватает в заводской прошивке.
        0
        Ну вы же понимаете, что не у всех есть CM. И при этом неизвестно, как приложение будет реагировать на этот режим, не зная о нем. А как в CM происходит возврат в обычный режим с панелями?
          0
          Возврат происходит аналогично переходу в режим — зажимаем хардварную кнопку включения и выбираем пункт «скрыть расширенный режим».
          Я тестировал этот режим, он работает нормально со всеми приложениями. По факту — он прячет(или выключает) эту панельку для всей ОС, что-то наподобии display:none в css.
          И когда она скрыта — можно провести пальцем от торца(любого) по направлению к середине экрана — всплывут программно продублированные кнопки(назад, домой, список приложений)

          пример кнопки(Expanded desktop)
          cdn.androidpolice.com/wp-content/uploads/2012/10/nexusae0_Screenshot_2012-10-29-11-16-22.png

          пример кнопок(назад, домой...)
          cdn.androidcommunity.com/wp-content/uploads/2013/03/Screen-Shot-2013-03-27-at-2.19.24-PM-540x464.png
            0
            Клева, но все равно CM не у всех стоит :)
        0
        Из описания так и не понял, а чем принципиально отличается это от старого режима «включить опции NoTitleBar + FullScreen»? Методом выхода из него?
          +1
          Насколько я понял, это для телефонов и планшетов, в которых нет хардварных сенсорных кнопок. Вместо этого они постоянно отображаются внизу экрана.
            0
            И для них ещё в 4.2 (или ниже, могу путать) нижнюю панель можно самостоятельно спрятать кнопкой «спрятать панель», вылезает наружу жестом «провести пальцем вдоль стороны, где должна быть панель». Хотя не отрицаю, возможно это фишка китайской прошивки была.
              0
              Скорей всего это было в китайской прошивке :) Такая функция на сколько я помню была только в YouTube и то там использовался режим Loan Back — это когда панелька появляется только при нажатии на контент (проигрываемое видео). Этот режим приносит этот функционал во все приложения, добавляя к этому дополнительный способ отображения системных панелей от края экрана.
              0
              Точно :)
            0
            при первом запуске приложения пользователю автоматически покажется системное сообщение о том, как использовать приложение в полноэкранном режиме.

            А как показать подсказку при втором запуске и т.д.?
              +1
              Если честно, не знаю :) Попробую найти это в доках. Если найдете раньше, пишите.
                0
                Нашел в доках для отладки
                Note: If you want to force the reminder bubble to appear for testing purposes, you can do so by putting the app in immersive mode, turning off the screen with the power button, and then turning the screen back on again within 5 seconds.
                developer.android.com/intl/ru/training/system-ui/immersive.html

                На эмуляторе сработало.
              0
              Эта фича (с таскбаром) неизвестно с каких времён имеется в MIUI. И только сейчас она появляется в основном Android.
                0
                Возможно, команда Android оттуда эту фичю и позаимствовала, но большинство пользователей не умеют ставить кастомные прошивки :)
                  –1
                  Нужно резет сделать устройству ;). Для не очень соблазнительных наверняка будет галочка «больше не показывать»
                0
                На нексусе 4 в андроиде 4.3 прекрасно делается fullscreen режим, когда прячутся панели. Появляются они автоматически при любом тапе.
                Я пока не разобрался, в чем принципиальное отличие, кроме как способа «вернуть» панели назад.
                  –1
                  Как вы скрываете панели в этом случае? Насколько я помню там можно было только затемнять панель навигации и там появлялись 3 полупрозрачные точки.
                  0
                  Изменил статью, в соответствии с вашим замечаниями :) Действительно, immersive mode добавляет просто еще один способ взаимодействия с системными панелями из полноэкранного режима
                  0
                  >команда Android для этого придумала простое решение, пользователю достаточно сделать свайп от верхнего или нижнего края экрана по направлению к центру экрана

                  Чего-чего команда Android сделала? «Придумала»? Надо же, а я эту фичу видел 3 года назад, когда впервые был представлен BlackBerry PlayBook, да и сейчас ежедневно использую на своем BlackBerry Z10 :)
                    0
                    Исправил :)
                    0
                    Свайпы от краёв неудобны при использовании многих чехлов.
                    Например у меня, даже разворачивание шторок превращется в этакое выковыривание из-под края чехла.
                      0
                      Возможно, чехлы не использую, не пробовал.
                      0
                      Наконец-то. Учитывая соотношение сторон у того же Nexus 7, в альбомной ориентации почти невозможно комфортно пользоваться. Верхняя панель + кнопки ОС занимают очень много полезного места.
                        0
                        Да для Nexus 7 это актуально :)
                        0
                        Немного поменял статью в соответствии с теми комментариями, что были написаны. Спасибо вам.
                          0
                          Мешаться эта фича будет, много полезных приложений используют свайпы от краёв — и свайпы с боков, как в популярном Pie, и свайпы снизу, как в этом приложении, например.
                            0
                            Ну если будет мешаться, тогда и не надо использовать. Ее же Google не навязывает, если нужна такая функциональность используй, если нет, делай все как раньше.
                            0
                            P.P.S. 20 ноября появилась новая статья в Training Kit по этому режиму. Постараюсь дополнить статью в ближайшее время с учетом и ее.

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

                            Самое читаемое