Overlapping shadow effect remains on Navigation Drawer's NavigationView
Asked Answered
E

4

20

I have refined the Navigation Drawer Activity project template of Android Studio, which uses Toolbar, v7.app.ActionBarDrawerToggle and NavigationView instead of the NavigationDrawerFragment (and layout/fragment_navigation_drawer.xml).

Navigation Drawer in immersive-sticky when the drawer is closed

It is perfectly working. Then, I go further. I have my Navigation Drawer project in immersive-sticky (full screen) mode.

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    if (hasFocus) {
        View decorationView = getWindow().getDecorView();
        decorationView.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);
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {

    ...

    toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    ActionBar actionBar = getSupportActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);

    drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawerToggle = new ActionBarDrawerToggle(
            this,
            drawerLayout,
            R.string.navigation_drawer_open,  /* "open drawer" description for accessibility */
            R.string.navigation_drawer_close  /* "close drawer" description for accessibility */
    ) {
        @Override
        public void onDrawerClosed(View drawerView) {
            super.onDrawerClosed(drawerView);

            invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
        }

        @Override
        public void onDrawerOpened(View drawerView) {
            super.onDrawerOpened(drawerView);

            invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
        }
    };
    drawerLayout.setDrawerListener(drawerToggle);

    navigationView = (NavigationView) findViewById(R.id.navigation_view);
    navigationView.setNavigationItemSelectedListener(this);
}

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

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

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (drawerToggle.onOptionsItemSelected(item)) {
        return true;
    }

    ...

}

A problem has risen. The bands of overlapped shadow effect on the NavigationView which are derived from status bar (on the top side) and navigation bar (on the bottom side) remain still.

When Navigation Drawer is opened

When Navigation Drawer is opened and sticky status and navigation bars are showed

How can I get rid of them?

I reviewed sources of v7.app.ActionBarDrawerToggle or NavigationView of Android, but in vain.


Updated:

Thanks for @lcw_gg's advice, I have gotten rid of the status bar's shadow completely (while the navigation bar's shadow remains). That is to set android:windowFullscreen attribute true in layout xml.

But I want to do this in Java code. I found a way and probably it is equivalent to the xml way:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

And with doing this, you don't need any more to set these two flags -- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and View.SYSTEM_UI_FLAG_FULLSCREEN -- to the decorationView.

shadow of the status bar has disappeared while that of navigation bar remains

Still, I can't find the way to get rid of the navigation bar's shadow. I'm waiting for a solution.

Egyptology answered 26/9, 2015 at 15:27 Comment(8)
What API levels does this issue occur on? Is it all of them or only post-Lollipop?Chopine
@Chopine My device is 5.1.1 (API-22 Lollipop; Nexus7). I haven't examined other devices yet.Egyptology
May be worth experimenting with android:elevation attributes in your layout xml files in that case. I've experienced similar woes in the past when I've forgotten about elevation.Chopine
@Chopine Thank you for your suggestion, but it is sorry, even if I set android:elevation="1000dp" or "0dp" on the NavigationView, nothing change.Egyptology
If it can help someone, I am working on an app with a navigation drawer and met this problem on a Sony Xperia 4.4.3, Nexus6 6.0 and Moto G 5.0.2. And I noticed that the shadow is always there, even though the app is in fullscreen mode. If you put a color on the background of your activity/fragment, the navigation bar shadow will appear in white.Tranquil
If you want to get rid of the status bar on top, putting this line in the style file is working fine : <item name="android:windowFullscreen">true</item>. I have absolutely no problem with the status bar shadow.Tranquil
@Tranquil Thank you for the suggestion. It is indeed very useful. The status bar's shadow has disappeared completely! While the navigation bar's shadow cannot be removed.Egyptology
Would you be so kind and publish also your layout file? I am having similar problems as you do. Besides sticky immersive I also don't want a toolbar at the top, but only a floating action button that would open navigation drawer.Alten
E
27

At last, I made it.

The solution is using FLAG_LAYOUT_NO_LIMITS together with FLAG_FULLSCREEN to the android.view.Window object.

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
        | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);

This has gotten rid of both of the shadows perfectly.

enter image description here

lcw_gg's comment was very useful clue to manipulating android.view.Window. Special thanks to him.


Update for Android 11

Unfortunately, the problem returns when I update my phone to Android 11 (API-30). The above solution isn't effective any more. An alternate solution is @AllanVeloso's answer using app:insetForeground="@null"

And I also got a new solution. Just getting rid of SYSTEM_UI_FLAG_LAYOUT_STABLE is it. This SYSTEM_UI_FLAG_LAYOUT_STABLE should be the root cause of shadow effects.

For my humble research on Google Developer or StackOverflow, the flag is always explained to use with SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and/or SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION, but there is no exact explanation about SYSTEM_UI_FLAG_LAYOUT_STABLE itself other than:

This means that the insets seen there will always represent the worst case that the application can expect as a continuous state.

So I used it with other two automatically. But that was the root of this problem. Just removing SYSTEM_UI_FLAG_LAYOUT_STABLE alone resolved this problem (even on Android 10 or earlier).

Deprecation for those immersive FLAGs

As you know those View.SYSTEM_UI_FLAG_* are deprecated from Android 11 (API-30).

You will use like below:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    WindowInsetsController windowInsetsController = decorView.getWindowInsetsController();
    windowInsetsController.setSystemBarsBehavior(
            WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
    );
    windowInsetsController.hide(
            WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars()
    );

    window.setDecorFitsSystemWindows(false);
} else {
    (...)
}

As google people (like Chris Banes) explained Window.setDecorFitsSystemWindows(false) is almost equivalent to SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION combine with SYSTEM_UI_FLAG_LAYOUT_STABLE. There is no problem at least about shadow effects.

Egyptology answered 6/10, 2015 at 13:44 Comment(3)
THANK YOU SO MUCH! - I was struggling for maybe 20 full hours trying to get this working, research view attributes, trying to figure out why this navigation bar black transparency wouldn't go away on Lollipop+ devices.Rabelais
At the end, I found amazing solutionLamartine
You are a hero, my friend! ;-)Caw
K
21

The accept answer has a problem. Setting flags FLAG_FULL_SCREEN and FLAG_LAYOUT_NO_LIMITS will make the insets of the window to be 0 and content may lay behind the system UIs.

It will work because NavigationView, which is a subclass of ScrimInsetsFrameLayout, listen to onApplyWindowInsets and View.setWillNotDraw to true when window insets are all 0.

You just need to add to your NavigationView this:

app:insetForeground="@null"
Kulak answered 23/8, 2018 at 0:56 Comment(3)
Is there an equivalent Java code way rather than layout.xml? I don't know why but setScrimInsetForeground method couldn't be used.Egyptology
@Egyptology The developers restricted those apis to the library. Reading the code if you set NavigationView.setWillNotDraw(true) no inset foreground will be drawed, but I think everything else will be drawed correctly. You may try this.Kulak
Hmm, though app:insetForeground='@null' worked well, I couldn't work setWillNotDraw(true) as intended. But thank you Allan.Egyptology
C
1

For those with the same issue as above, but want to keep the system status bar always showing (thus not using WindowManager.LayoutParams.FLAG_FULLSCREEN), there is some additional work you have to do to prevent a weird content creep underneath the status bar on lollipop. I didn't notice the issue on kitkat when I tested it.

I was using two navigation drawers (left and right) and weirdly had only the LEFT one casting the shadow on the bottom. Once applying WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS as shown in the accepted answer, the shadow went away but then had the content creep under the status bar as well as the navigation drawer drawing on top of the status bar; all the way to the device bezel.

To fix that you must overwrite your theme in v21 styles.xml to include:

<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:statusBarColor">@color/colorPrimaryDark</item>

This will move the content back below the status bar and also prevent the navigation drawer from drawing on top of the status bar.

In addition to the above entries in my v21 styles, my activity code looked like this:

// Fixes bottom navbar shadows from appearing on side navigation drawers
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    WindowManager.LayoutParams attributes = getWindow().getAttributes();
    attributes.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
    getWindow().setAttributes(attributes);
}
Cirilo answered 30/9, 2016 at 19:23 Comment(0)
H
1

Thanks to hata i have succeed to completely hide system UI, by merging his original code:

@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);

  if (hasFocus) {
    View decorationView = getWindow().getDecorView();
    decorationView.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);
  }
}

with the solution code added in onCreate of my Activity before calling inflate:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);

Apparently, the flag WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS was the missing piece to make the full screen work as expected on my device (API 21)

Hellion answered 27/8, 2020 at 6:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.