Action bar displayed incorrectly when returning from immersive mode
Asked Answered
M

3

31

I'm having some issues returning from Immersive Mode in an ActionBarActivity. I have created a simple app to illustrate this issue. There is a layout with a single button to toggle immersive mode. When "returning" from immersive mode, the action bar is offset downwards from its original position, roughly the same distance again that it is usually offset from the top of the screen.

I've tried this on a Nexus 4 running Lollipop. This behavior did not happen pre-Lollipop.

Screenshots before, immersed, after.

A simple ActionBarActivity that illustrates this issue:

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();
        }
    }
}

Nothing fancy in the manifest:

<?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>

Trivial layout:

<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>

The workaround I use at the moment is to call recreate() after leaving immersive mode, but it looks a bit "glitchy".

If I up the minSdkVersion and use Activity instead of ActionBarActivity, i.e. don't use the support library, then I do not experience this behavior.

I understand that immersive mode is only available in KitKat+ and that I don't need to use the ActionBarActivity from the support library, but the finished product will have to run on API versions 8+ and the immersive mode is an optional extra.

Some other workarounds I have thought about and dismissed for now:

  1. Have a wrapper launcher Activity which immediately forwards programmatically to an ActionBarActivity for lower API versions.
  2. Have multiple apps by API version.

Either of those options sounds like a lot of added maintenance.

Is there anything else I could try? Are there any glaring mistakes in the code above, with respect to returning from immersive mode?

Update

I have since updated the Nexus 4 to 5.1 and the support library to rev 22.1.1 and the behavior is still the same. I have also updated the code to use the new AppCompatActivity since ActionBarActivity is now deprecated. The behavior is, once more, the same.

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

Update

This weird behavior also extends to the landscape mode. In addition to the offset at the top of the screen, there also is an offset to the right of the screen between the end of the action bar and the navigation buttons. Interestingly this offset again seems to be the size of the navigation button "bar", i.e. larger than the offset at the top.

Landscape screenshots before, immersed, after.

Merbromin answered 5/3, 2015 at 9:20 Comment(7)
did you try with toolbar instead actionbar ?Hognut
I have tried exactly what's in the code above, I'm not even doing anything specifically with the actionbar, it's just there by default.Merbromin
Did you find the answer? I am having the same issueGivens
I am also having the same problem. Did anyone find a solution?Strasbourg
It's 4 years later and this problem still remains. None of the answers has solved it for me nor does the answer in this similar question: stackoverflow.com/questions/32394135. Starting a bounty with the hope to get a real solution.Ambient
I can't reproduce the issue on Android P. The only difference from the code that you posted here is the minSdk=14 and targetSdk=28.Coycoyle
The problem seems to be with View.SYSTEM_UI_FLAG_LAYOUT_STABLE. It looks like it's impossible to restore the original flags without this issue. Looking at the "after" screenshot that's posted above (imgur.com/aaQu11i), it seems like the top space is equal to statusBar while the right space is equal to the height of the soft buttons' layout. That's exactly my case, too. Using minSdk=19, targetSdk=28, tested on Android 8.1.0.Ambient
V
1

Had the same problem, fixed with this: your leaveImmersiveMode() function should use these flags instead:

getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_FULLSCREEN);
Verboten answered 31/5, 2017 at 17:21 Comment(0)
W
0

Use a Toolbar ... I had the same problem and switching to a Toolbar instead of a customContentView solved it.

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

And set your Activity's theme to @style/Theme.AppCompat.NoActionBar

Woden answered 10/1, 2016 at 2:43 Comment(0)
A
0

Notwithstanding the fact that ActionBarActivity was deprecated in the support library from V26, changing your leaveImmersiveMode() method to specify the following flags fixes the problem for me with the test app using your code (which originally showed the extra gap you mentioned).

@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();
    }
}

These are the flags recommended in the Android documentation on immersive mode.

Affectional answered 16/9, 2019 at 0:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.