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:
- Have a wrapper launcher
Activity
which immediately forwards programmatically to anActionBarActivity
for lower API versions. - 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.
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