Search code examples
androidnavigation-drawerdrawerlayoutandroid-statusbarcontextual-action-bar

Black status bar doesn't disappear when closing Contex Action Mode


To be able to make the drawer layout go under the status bar, I've needed to set its color to transparent and set SystemUIVisibility to fullscreen. When starting Context Action Mode the status bar becomes black.

This can be "solved" by setting the color of the status bar in the OnCreateActionMode and making it transparent again in OnDestroyActionMode (although you can still see the black bar for a moment).

The real problem appears when I'm in Contextual Action Mode and I navigate to another fragment, finishing the contextual mode before navigating. This makes the black view appear over the fragment's layout and it can't be removed.

You can see an image of this behavior:

behavior.

I'm using Xamarin Android, but the implementation in java should be fairly similar. The drawer layout and main content are all created programmatically:

To set the status bar:

if (Build.VERSION.SdkInt > BuildVersionCodes.Lollipop)
{
      //we don't need the translucent flag this is handled by the theme
      StatusBarHelper.SetTranslucentStatusFlag(Activity, false);
      //set the statusbarcolor transparent to remove the black shadow
      StatusBarHelper.SetStatusBarColor(Activity, Color.Transparent);
}
StatusBarHelper.SetLightStatusBar(Activity, false);`

To create the main view (with the drawer):

_drawerLayout = new DrawerLayout(inflater.Context)
{
    LayoutParameters = new DrawerLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent),
};

ViewCompat.SetOnApplyWindowInsetsListener(_drawerLayout, this); // This only works from lollipop 

using (FrameLayout contentContainer = new FrameLayout(inflater.Context))
{
    contentContainer.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent);
    // Sets fullscreen to be able to go under the status bar
    if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop) 
    {
        // Already works since Kitkat, but since SetOnApplyWindowInsets only works from lollipop, for the moment we enable this later
        contentContainer.SystemUiVisibility = (StatusBarVisibility)(SystemUiFlags.LayoutStable | SystemUiFlags.LayoutFullscreen | SystemUiFlags.LayoutHideNavigation);
    }

    // Make the content ViewGroup ignore insets so that it does not use the default padding
    ViewCompat.SetOnApplyWindowInsetsListener(contentContainer, this);


    // Scrim view. This is used to paint under the status bar the color that we want
    using (View statusBarScrim = new View(inflater.Context))
    {
        statusBarScrim.Id = Resource.Id.scrim_view;
        statusBarScrim.LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, 0);

        statusBarScrim.SetBackgroundColor(new Color(ResourcesCompat.GetColor(Resources, Resource.Color.primaryDark, null)));

        ViewCompat.SetOnApplyWindowInsetsListener(statusBarScrim, this);

        contentContainer.AddView(statusBarScrim);
    }

    //Create main content (toolbar and fragment container)
    using (var mainContent = CreateMainContent(inflater))
    {
        contentContainer.AddView(mainContent);
    }

    contentContainer.SetFitsSystemWindows(true);

    _drawerLayout.AddView(contentContainer);
}

Finally the code for the insetListener:

// This is used to handle the scrim view
// The scrim view is used to cover the status bar with a color
// The scrim consumes the top inset by getting its height
// All other views are ignored
public WindowInsetsCompat OnApplyWindowInsets(View v, WindowInsetsCompat insets)
{
    // Check if it's the scrim view
    // If it's not the scrim it will not consume the insets 
    if (v.Id == Resource.Id.scrim_view)
    {
        var topInset = insets.SystemWindowInsetTop;
        if (v.LayoutParameters.Height != topInset)
        {
            v.LayoutParameters.Height = topInset;
            v.RequestLayout();
        }
    }

    return insets;
}

To create the Context Action Menu I use the StartSupportActionMode and in the OnCreateActionMode I'm just setting up the menu. The status bar becomes black (Context Action Mode adds a view like the scrim I'm using, but I haven't found a way to color it).

There's an item in this menu that navigates to the screen you see in the image I attached previously. When this item is pressed i call _actionMode.Finish() and then navigate there. The result is that the Context Action Mode toolbar disappears but this black scrim view doesn't.

If I add a delay before navigating this error doesn't occur, so I'm thinking it has something to do with the animation? Either way, solving things with delay is always bad practice because it will be different in each device.


Solution

  • Okay, so I'm pretty sure this must be a bug with AppCompat's ActionMode. In the end, I was lucky and setting FitSystemWindows on the fragment navigated to seems to fix the issue.

    Also, I found how to set the color of the scrim layout Action Mode adds without ever seeing the black bar.

    I found the answer here https://stackoverflow.com/a/38702953/10054508 so all the credit goes to him. Either way, I'll paste it here:

    <color name="abc_input_method_navigation_guard" tools:override="true">@color/primary_dark</color>