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

У меня возникли проблемы с возвратом из режима погружения в ActionBarActivity. Я создал простое приложение, чтобы проиллюстрировать эту проблему. Существует макет с одной кнопкой для переключения режима погружения. При «возвращении» из режима погружения панель действий смещается вниз от своего исходного положения, примерно на то же расстояние, на которое она обычно смещается от верхней части экрана.

Я пробовал это на Nexus 4 с Lollipop. Такого поведения не было до Lollipop.

Скриншоты до, погружено, после.

Простой ActionBarActivity, который иллюстрирует эту проблему:

public class MainActivity extends ActionBarActivity {
    private boolean immersed;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private void enterImmersiveMode() {
        if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().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
                    );
            immersed = true;
        }
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private void leaveImmersiveMode() {
        if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            immersed = false;
//          recreate();
        }
    }

    public void toggleImmersive(View v) {
        if (immersed) {
            leaveImmersiveMode();
        } else {
            enterImmersiveMode();
        }
    }
}

Ничего особенного в манифесте:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.immersivetest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="ImmersiveTest"
        android:theme="@style/Theme.AppCompat" >
        <activity
            android:name=".MainActivity"
            android:label="ImmersiveTest" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Тривиальный макет:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.immersivetest.MainActivity" >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="toggle immersive"
        android:onClick="toggleImmersive"
        />

</RelativeLayout>

Обходной путь, который я использую на данный момент, — это вызвать recreate() после выхода из режима погружения, но он выглядит немного «глючным».

Если я поднимаю minSdkVersion и использую Activity вместо ActionBarActivity, т.е. не использую библиотеку поддержки, то такого поведения не наблюдается.

Я понимаю, что иммерсивный режим доступен только в KitKat+ и мне не нужно использовать ActionBarActivity из библиотеки поддержки, но готовый продукт должен будет работать на API версии 8+, а иммерсивный режим является необязательным дополнением.

Некоторые другие обходные пути, о которых я подумал и отклонил на данный момент:

  1. Иметь средство запуска оболочки Activity, которое немедленно программно перенаправляется на ActionBarActivity для более ранних версий API.
  2. Наличие нескольких приложений по версии API.

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

Есть ли что-нибудь еще, что я мог бы попробовать? Есть ли в приведенном выше коде явные ошибки в отношении возврата из режима погружения?

Обновить

С тех пор я обновил Nexus 4 до версии 5.1 и библиотеку поддержки до версии 22.1.1, и поведение осталось прежним. Я также обновил код, чтобы использовать новый AppCompatActivity, так как ActionBarActivity теперь устарел. Поведение, опять же, то же самое.

public class MainActivity extends AppCompatActivity {
    // no changes here
}

Обновить

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

Скриншоты горизонтальной ориентации до, погружено, после.


comment
Вы пробовали использовать панель инструментов вместо панели действий?   -  person florent champigny    schedule 01.05.2015
comment
Я пробовал именно то, что в приведенном выше коде, я даже ничего конкретно не делаю с панелью действий, она просто есть по умолчанию.   -  person ci_    schedule 01.05.2015
comment
Вы нашли ответ? у меня такая же проблема   -  person Vijay    schedule 01.07.2017
comment
У меня такая же проблема. Кто-нибудь нашел решение?   -  person Jacob R    schedule 12.07.2017
comment
Прошло 4 года, а проблема осталась. Ни один из ответов не решил это для меня, как и ответ на этот аналогичный вопрос: stackoverflow.com/questions/32394135. Запуск баунти с надеждой получить реальное решение.   -  person Georgios    schedule 03.04.2019
comment
Я не могу воспроизвести проблему на Android P. Единственное отличие от кода, который вы разместили здесь, это minSdk=14 и targetSdk=28.   -  person Rodrigo Bagni    schedule 04.04.2019
comment
Проблема, кажется, с View.SYSTEM_UI_FLAG_LAYOUT_STABLE. Похоже, без этой проблемы невозможно восстановить исходные флаги. Глядя на скриншот после, опубликованный выше (imgur.com/aaQu11i), кажется, что верхнее пространство равно в статусбар, а правое пространство равно высоте раскладки софт-кнопок. Это как раз и мой случай. Использование minSdk=19, targetSdk=28, протестировано на Android 8.1.0.   -  person Georgios    schedule 05.04.2019


Ответы (3)


Была та же проблема, исправленная следующим образом: вместо этого ваша функция leaveImmersiveMode() должна использовать эти флаги:

getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_FULLSCREEN);
person ricardoquesada    schedule 31.05.2017

Используйте Toolbar... У меня была такая же проблема, и переход на Toolbar вместо customContentView решил ее.

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

И установите тему вашей активности на @style/Theme.AppCompat.NoActionBar

person ShellDude    schedule 10.01.2016

Несмотря на то, что ActionBarActivity устарел в библиотеке поддержки из V26, изменение вашего метода leaveImmersiveMode() для указания следующих флагов устраняет проблему для меня с тестовым приложением, использующим ваш код (который изначально показал дополнительный пробел, о котором вы упомянули).

@TargetApi(Build.VERSION_CODES.KITKAT)
private void leaveImmersiveMode() {
    if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
        immersed = false;
//          recreate();
    }
}

Эти флаги рекомендуются в документации Android по иммерсивному режиму.

person AGDownie    schedule 16.09.2019