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

У меня возникают проблемы с возвратом из режима Immersive в 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, т.е. не использую библиотеку поддержки, то я не испытываю этого поведения.

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

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

  • У вас есть пусковая установка Activity, которая сразу же перейдет программно в ActionBarActivity для более низких версий api.
  • Есть несколько apks по версии api.

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

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

Обновление

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

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

Обновление

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

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

Ответ 1

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

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

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

Ответ 2

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

getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_FULLSCREEN);