Reputation: 8774
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:
Activity
which immediately forwards programmatically to an ActionBarActivity
for lower API versions.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.
Upvotes: 31
Views: 1955
Reputation: 638
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.
Upvotes: 0
Reputation: 606
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
Upvotes: 0
Reputation: 88
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);
Upvotes: 1