Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Action bar displayed incorrectly when returning from immersive mode

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.

like image 765
ci_ Avatar asked Mar 05 '15 09:03

ci_


People also ask

How do I replace my action bar toolbar?

To replace an app's default action bar with a Toolbar : Create a new custom theme and modify the app's properties so that it uses this new theme. Disable the windowActionBar attribute in the custom theme and enable the windowNoTitle attribute. Define a layout for the Toolbar .

How do you add action items to the action bar in Android?

All action buttons and other items available in the action overflow are defined in an XML menu resource. To add actions to the action bar, create a new XML file in your project's res/menu/ directory. The app:showAsAction attribute specifies whether the action should be shown as a button on the app bar.


1 Answers

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); 
like image 65
ricardoquesada Avatar answered Sep 26 '22 06:09

ricardoquesada