Pull to refresh

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

Reading time6 min
Views65K
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 по этому режиму. Постараюсь дополнить статью в ближайшее время с учетом и ее.
Tags:
Hubs:
Total votes 37: ↑36 and ↓1+35
Comments35

Articles