27

I have a status bar with the following in the theme set on it:

<!-- Base Theme for all "Material"-esque styles. We use NoActionBar
     so we can use the Toolbar at runtime.
-->
<style name="Material" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:windowTranslucentStatus">true</item>
    ...
</style>

I also have a DrawerLayout for most of my activities, which sets the color of the status bar to my liking using:

    mDrawerLayout.setStatusBarBackgroundColor(getResources().getColor(R.color.myapp_green));

I am using a Toolbar, rather than the default ActionBar, so it exists in my layout (i.e. the navigation drawer draws on top of it).

Everything works fine, except that in one of my activities, I have a multi-select mode with an ActionMode. When this ActionMode is activated (using a long press), it overlays the Toolbar using:

<item name="android:windowActionModeOverlay">true</item>
<item name="windowActionModeOverlay">true</item>
<item name="actionModeStyle">@style/Material.Widget.ActionMode</item>

The Material.Widget.ActionMode style is:

<style name="Material.Widget.ActionMode" parent="@style/Widget.AppCompat.ActionMode">
    <item name="android:background">@color/myapp_green</item>
    <item name="background">@color/myapp_green</item>
</style>

Now, the problem is that, whenever this happens, the status bar turns from the myapp_green color to black. It's almost as if the status bar translucency is turned off (I'm using Android 5.0). I'm wondering how I might be able to get this behavior to not happen, and to keep the status bar color/translucency as-is.

I've tried to add <item name="android:windowTranslucentStatus">true</item> to the action mode's styles, as well as adding <item name="android:statusBarColor">@color/myapp_green</item> in the style for the ActionMode, both without success.

Update:

I wonder if this has something to do with the wonky way that I'm setting the status bar background. All of my Activity classes derive from NavigationDrawerActivity.java:

/**
 * An {@link Activity} that supports a Navigation Drawer, which is a pull-out panel for navigation
 * menus. This drawer is pulled out from the left side of the screen (right side on RTL devices).
 */
public class NavigationDrawerActivity extends ActionBarActivity
  implements AdapterView.OnItemClickListener {

  private static final String LOGTAG = NavigationDrawerActivity.class.getSimpleName();

  private DrawerLayout mDrawerLayout;
  private ListView mDrawerList;
  private LayoutInflater mInflater;
  private NavigationDrawerItemAdapter mAdapter;
  private ActionBarDrawerToggle mDrawerToggle;

  private NavigationDrawerItem[] mNavigationDrawerItems;

  private Toolbar mAppBar;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
// We have to call super.setContentView() here because BaseActivity redefines setContentView(),
// and we don't want to use that.
super.setContentView(R.layout.navigation_drawer);

mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    setupNavigationDrawer();
  }

  @Override
  protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);

    // Sync the toggle state after onRestoreInstanceState has occurred.
    mDrawerToggle.syncState();
  }

  @Override
  public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    mDrawerToggle.onConfigurationChanged(newConfig);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    switch(id) {
      case android.R.id.home:
        return mDrawerToggle.onOptionsItemSelected(item);
    }

    return super.onOptionsItemSelected(item);
  }

  /**
   * Toggles the state of the navigation drawer (i.e. closes it if it's open, and opens it if
   * it's closed).
   */
  public void toggleNavigationDrawer() {
    if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
      closeNavigationDrawer();
    } else {
      openNavigationDrawer();
    }
  }

  /**
   * Opens the navigation drawer.
   */
  public void openNavigationDrawer() {
    mDrawerLayout.openDrawer(GravityCompat.START);
  }

  /**
   * Closes the navigation drawer.
   */
  public void closeNavigationDrawer() {
    mDrawerLayout.closeDrawer(GravityCompat.START);
  }

  /**
   * Initializes items specific to the navigation drawer.
   */
  private void setupNavigationDrawer() {
    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerLayout.setStatusBarBackgroundColor(getResources().getColor(R.color.wiw_green));

        mAppBar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(mAppBar);

        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setHomeButtonEnabled(true);
        actionBar.setDisplayShowHomeEnabled(false);

        mDrawerToggle = new ActionBarDrawerToggle(
          this,                  /* Our context (Activity that hosts this drawer) */
          mDrawerLayout,         /* The DrawerLayout where the nav drawer will be drawn */
          R.string.drawer_open,  /* Description of "open drawer", for accessibility */
          R.string.drawer_close  /* Description of "close drawer", for accessibility */
        ) {

          /**
           * Called when a drawer has settled in a completely closed state.
           */
          public void onDrawerClosed(View view) {
            super.onDrawerClosed(view);
            supportInvalidateOptionsMenu();
          }

          /**
           * Called when a drawer has settled in a completely open state.
           */
          public void onDrawerOpened(View drawerView) {
            super.onDrawerOpened(drawerView);
            supportInvalidateOptionsMenu();
          }
        };

        mDrawerList = (ListView) mDrawerLayout.findViewById(R.id.drawer_list);

        mNavigationDrawerItems = buildNavDrawerItemsList();

        setupAdapter(mNavigationDrawerItems);

        setupNavigationDrawerHeader();

        mDrawerLayout.setDrawerListener(mDrawerToggle);
        mDrawerList.setOnItemClickListener(this);
      }

      @Override
      public void onItemClick(AdapterView<?> parent, View aView, int aPosition, long aId) {
        // Code not relevant
      }

      /**
       * Set the inner content view of this {@link NavigationDrawerActivity} to have a given layout.
       *
       * @param aLayoutId The id of the layout to load into the inner content view of this activity.
       */
      public void setDrawerContent(int aLayoutId) {
        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        ViewGroup root = (ViewGroup)findViewById(R.id.drawer_content);
        inflater.inflate(aLayoutId, root);
      }  
    }

I actually have to run DrawerLayout.setStatusBarBackgroundColor() for it to have an effect. Just changing colorPrimaryDark in values-v21/styles.xml has no effect on the status bar. I feel like this could be the root of the problem... These classes are being converted from a non-Material theme to a new, Material-like theme, so I'm wondering if I missed something when I did the conversion to the colorPrimaryDark to be recognized correctly.

jwir3
  • 6,019
  • 5
  • 47
  • 92
  • Maybe your color `@color/wiw_green` is different from `R.color.myapp_green`, is it? – Eduardo May 18 '15 at 16:13
  • Oops, sorry about that. Yeah, the two colors are the same. – jwir3 May 18 '15 at 16:30
  • Also, I'll restart the bounty when it expires, because I still haven't really solved this problem. Thanks to all who have replied so far! – jwir3 May 26 '15 at 15:30
  • This is a bug, https://code.google.com/p/android/issues/detail?id=183887 https://code.google.com/p/android/issues/detail?id=184047 Related http://stackoverflow.com/questions/32318563/appcompat-v7-v23-0-0-statusbar-color-black-when-in-actionmode – sojin Dec 09 '15 at 13:53
  • This problem has been fixed with This has been fixed with `com.android.support:design:28.0.0-rc01`. – Soren Stoutner Aug 16 '18 at 20:13

9 Answers9

6

I'm posting an answer here because, while the other solutions were helpful, none of them answered the question exactly. I was able to find out that the call to DrawerLayout.setStatusBarBackgroundColor() was causing the trouble. My solution is the following:

  1. Enable windowTranslucentStatus and windowDrawsSystemBarBackgrounds in the main theme.

  2. Remove the call to DrawerLayout.setStatusBarBackgroundColor() inside of my NavigationDrawerActivity, from which all Activity classes derive.

  3. Set the following styles in my values-v21/styles.xml base theme:

    <item name="android:colorPrimary">@color/app_green</item>
    <item name="colorPrimary">@color/app_green</item>
    <item name="android:colorPrimaryDark">@color/app_green_dark</item>
    <item name="colorPrimaryDark">@color/app_green_dark</item>
    <item name="android:colorPrimaryDark">?attr/colorPrimaryDark</item>
    <item name="android:statusBarColor">?attr/colorPrimaryDark</item>
    
  4. Inside of the NavigationDrawerActivity class, in its onCreate() method, I execute the following (thanks @Tinadm for this part of the answer): getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

  5. Inside of my class that presents the ActionMode, I added the following methods:

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        // This is to highlight the status bar and distinguish it from the action bar,
        // as the action bar while in the action mode is colored app_green_dark
        getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.app_green_darker));
      }
    
      // Other stuff...
      return true;
    }
    
    @Override
    public void onDestroyActionMode(ActionMode mode) {
      mActionMode = null;
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.app_green_dark));
      }
    }
    

Re-enabling the translucent status bar when the ActionMode is destroyed enables my navigation drawer to be drawn underneath the status bar. It's also worth noting that the ActionMode overlays the action bar, so it will overlay the navigation drawer if it's pulled out when the ActionMode is enabled (via swiping left-to-right). I am going to disable this functionality when the ActionMode is enabled, so it doesn't confuse users.

aax
  • 394
  • 5
  • 10
jwir3
  • 6,019
  • 5
  • 47
  • 92
4

well your temporary solution is call this code when you most need it or when the problem starts

if(Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP){
    getWindow().setStatusBarColor(Color.BLUE); 
    // or Color.TRANSPARENT or your preferred color
}

EDIT

have you tried this

// actionMode.getCustomView().setBackgroundColor.(Color.TRANSPARENT);

@Override
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {    
    //but this time you let the actionmode's view change the statusbar's
    // visibility
    actionMode.getCustomView().setSystemUiVisibility(
        View.SYSTEM_UI_FLAG_LOW_PROFILE); //or preferred one
    return true;
}

when your actionMode is destroyed, the status bar will be restored, so override that too and re-set your statusBar color.

hope it helps

rekaszeru
  • 19,130
  • 7
  • 59
  • 73
Elltz
  • 10,730
  • 4
  • 31
  • 59
  • 1
    Hm, actually this doesn't help, either. I think this might be because setStatusBarColor is ignored when the translucent status bar is enabled. – jwir3 May 19 '15 at 03:46
  • wow! i see can you please create like a scenario demo i can hit up when im on a pc? +1 for the question though@jwir3 – Elltz May 19 '15 at 12:12
  • Yeah, I'll see if I can get a more complete compilable solution up today. – jwir3 May 19 '15 at 15:12
  • So, if I unset the `windowTranslucentStatus` within `onPrepareActionMode()`, (and don't reset the flag when I destroy the action mode), it leaves it as unset, indicating to me that the flag is taking effect. However, if I also add a call to `setStatusBarColor()` and `DrawerLayout.setStatusBarBackgroundColor()`, these have no effect (i.e. it's still black). My thinking is that something is setting it explicitly to black somewhere else further down the style chain. Thoughts on how I might be able to fix this issue? – jwir3 May 26 '15 at 17:41
3

I ran into the same problem a while ago and my resolution was to in onCreateActionMode & onDestroyActionMode set and restore the statusbar color.

//onCreateActionMode
mStartColor =  activity.getWindow().getStatusBarColor();
getWindow().setStatusBarColor(color);

//onDestroyActionMode
activity.getWindow().setStatusBarColor(mStartColor);

Of course youll need to check that its on lolipop before using the setStatusBarColor method as its not available pre L.

Extra: If you use a DrawerLayout youll need to set the statusBarColor on it too.

mDrawerLayout.setStatusBarBackground(new ColorDrawable(color));

Hope that helps!

zoltish
  • 2,122
  • 19
  • 37
2

I was having the exactly same issue as you. I managed to solve it using a workaround.

  1. The first thing I did was to remove windowTranslucentStatus=true from my theme;
  2. Then I used getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); to make the content fill the area under the status bar.
  3. I set <item name="android:colorPrimaryDark">@color/primary_dark</item> where primary_dark is "#51000000". Note the alpha value to achieve the same trasparency that windowTranslucent provides. Since my Toolbar is blue, my status bar is now dark blue.
  4. This alone did not solve the problem. When action mode is active, the status bar was still black in my case. But now, not using translucentStatusBar, I'm able to apply colors to my status bar, so I used:

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        getActivity().getWindow().setStatusBarColor(Color.parseColor("#026592"));
        return true;
    }
    

(Note that the color I used does not have alpha. This is the RGB color the exactly matches my dark blue semi transparent status bar).

Tinadm
  • 359
  • 3
  • 16
  • So, I have a navigation drawer with an image in the header, which shows up right now when the nav bar slides underneath the status bar. Disabling translucency will disable this, I think. (Although it's probably worth a shot to see if it works - I'll try this later today). I know that this behavior is possible, because the GMail app does exactly what I want, and I just need to figure out _how_. – jwir3 May 21 '15 at 15:28
  • Yes, I have a nav drawer as wall. Using the flags for full screen allows the content of the drawer to show under the status bar, even if it's not translucent. – Tinadm May 21 '15 at 15:36
  • Oh, awesome. I'll check this out then and get back to you as soon as I can. – jwir3 May 21 '15 at 15:46
  • Hm, so I feel like I'm missing something with this solution. I did exactly as you recommended, and removed both `windowTranslucentStatus` and `windowDrawsSystemBarBackgrounds` from my `values-v21/styles.xml` main theme, then I added the system ui visibility call to `onCreate` in my base class (`NavigationDrawerActivity`), and set the colors appropriately as recommended, and added the `onPrepareActionMode()` call in the fragment that uses the action mode. Now, my status bar is black 100% of the time, which is odd. Did I perhaps miss something? – jwir3 May 23 '15 at 14:15
  • I've noticed that `colorPrimaryDark` doesn't, for some reason, control the status bar color. If all I change is this (and disable the other flags for `windowDrawsSystemBarBackgrounds` and `windowTranslucentStatus`), shouldn't it change the color of the system bar (it does on other apps I've written). Could it maybe have something to do with the fact that I'm supporting more than just 5.0 (down to API 14)? – jwir3 May 23 '15 at 14:17
  • Yeah, I think colorPrimaryDark should change the status bar color as long as you don't use windowTranslucentStatus. This is odd indeed, since I have exactly the same design as you, a nav drawer with some fragments, and one in special that needs action mode. If you want, give me your email address so I can provide you more detailed instructions and some code examples; – Tinadm May 24 '15 at 22:51
  • Any chance you could post the code examples to gist.github.com so that future readers of this post would also benefit from it? – jwir3 May 25 '15 at 15:23
1

Looking through the Android documentation on Translucent system bars

If you're creating a custom theme, set one of these themes ( Theme.Holo.NoActionBar.TranslucentDecor and Theme.Holo.Light.NoActionBar.TranslucentDecor) as the parent theme or include the windowTranslucentNavigation and windowTranslucentStatus style properties in your theme.

So if you are using the Support Libraries, your style must extend some of the AppCompat styles and use both windowTranslucentNavigation and windowTranslucentStatus on your ActionBarStyle.

Eduardo
  • 133
  • 3
  • 8
  • Thanks for the response. I edited the above question to include a bit more context in terms of my styles. I'm only setting `windowTranslucentStatus` in `values-v21/styles.xml` (i.e. Lollipop and greater). The theme does inherit from an `AppCompat` style. I tried to add `windowTranslucentNavigation`, but without any luck. The status bar still turns black when I enter into an `ActionMode`. – jwir3 May 18 '15 at 17:50
  • but did you use both `windowTranslucentNavigation` and `windowTranslucentStatus` at the same time? – Eduardo May 18 '15 at 18:19
0

The problem might be here.

<item name="android:background">@color/myapp_green</item>
<item name="background">@color/myapp_green</item>

"android:background" is a proper syntax, I don't think "background" is. Why do you need to specify the background color twice anyway? Remove the second one and it should be fine. If not, then I hope my 2 cents below on the subject of setBackgroundColor methods help.

$0.02

In my experience, I have found that from time to time setBackgroundColor() method in Android behave strangely. When that happens, I use setBackground() instead, and specify the color in a drawable form: getResources().getDrawable(R.color.xxxx). I never found out why this resolved similar problems I've had in the past, but I've used this workaround so far without any obvious side effects. It might not be the best solution (especially if you're already setting a background drawable), but it comes in handy when nothing else seems to work.

user3829751
  • 712
  • 7
  • 20
  • I added "background" just to try it as a combination. I noticed that sometimes the android system responded to one with the "android" namespace and sometimes the one without. I've tried it with the namespace, without, and with both, and none seem to work. If I were to try your suggestion about setting it in code, what would I set the background drawable on? The action bar? – jwir3 May 19 '15 at 03:49
  • No, the status bar you mention in OP. `mDrawerLayout` has acces to a few `setBackground` methods. Try `mDrawerLayout.setBackground(getResources().getDrawable(R.color.myapp_green));` – user3829751 May 19 '15 at 13:17
0

I got the same issue and I resolved it in the following way

The issue was with overriding the same color for all views.But I's not able to go with R.color.mycolor and Color.parseColor("#412848") saved me.

Karthika PB
  • 1,373
  • 9
  • 16
  • So, I'm not sure where `setActivityBackgroundColor()` is defined. It doesn't appear to be defined on `Toolbar` or `ActionBar`... – jwir3 May 26 '15 at 15:50
  • just try using the direct color code like Color.parseColor("#412848") instead of using R.color.yourcolor because in some os the color overriding do not works. – Karthika PB May 27 '15 at 04:09
0
    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        super.onPrepareActionMode(mode, menu);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window w = getActivity().getWindow();
            w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            w.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        return true;
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        super.onDestroyActionMode(mode);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window w = getActivity().getWindow();
            w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
    }
0

While I used the previous solutions earlier, there are more issues when adding a navigation drawer, if you want the drawer to draw behind the system bar, and so many glitches related to setting and clearing window flags or the status bar color (even the official Gmail app has these glitches!)

Here's the solution I use now:

@Override
public void onSupportActionModeStarted(@NonNull ActionMode mode) {
    super.onSupportActionModeStarted(mode);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // Post this so it ideally happens after the window decor callbacks.
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                try {
                    final AppCompatDelegate delegate = getDelegate();
                    final Class clazz = Class.forName("android.support.v7.app.AppCompatDelegateImplV7");
                    final Field mStatusGuardField = clazz.getDeclaredField("mStatusGuard");
                    mStatusGuardField.setAccessible(true);
                    final View mStatusGuard = (View) mStatusGuardField.get(delegate);
                    if (mStatusGuard != null) {
                        mStatusGuard.setBackgroundColor(/* Should match your desired status bar color when the action mode is showing, or alternatively you could use Color.TRANSPARENT here and just update the colors through getWindow().setStatusBarColor() below. The key is to avoid the black status guard from appearing. */);
                    }
                } catch (Exception e) {
                    /* Log or handle as desired. */
                }
            }
        });
        // Also set the status bar color. This is to avoid a momentary frame or 
        // two where the black will still be visible.
       getWindow().setStatusBarColor(/* Should match your desired status bar color when the action mode is showing. */);
    }
}

@Override
public void onSupportActionModeFinished(@NonNull ActionMode mode) {
    super.onSupportActionModeFinished(mode);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // Reset to the theme's colorPrimaryDark. This would normally result in 
        // the black status guard being seen momentarily, but because we set it 
        // to the same color as the action mode background, this intermediate 
        // state is not visible.
        getWindow().setStatusBarColor(Color.TRANSPARENT);
    }
}

This is obviously a big hack, and it doesn't animate exactly as the contextual action bar does, but it does the trick for me. Props to https://stackoverflow.com/a/38702953/1317564 for coming up with the idea.

Note that this is only needed if you're using a navigation drawer or otherwise have these attributes set in your values-v21/theme.xml:

<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>

Otherwise, it will probably work to just call setStatusBarColor(...) in onSupportActionModeStarted() and onSupportActionModeFinished(), and this is what I was doing before adding a navigation drawer.

Community
  • 1
  • 1
Learn OpenGL ES
  • 4,759
  • 1
  • 36
  • 38