重叠阴影效果保留在 Navigation Drawer 的 NavigationView 上

Overlapping shadow effect remains on Navigation Drawer's NavigationView

我改进了Android Studio 的Navigation Drawer Activity 项目模板,它使用了Toolbarv7.app.ActionBarDrawerToggle NavigationView 而不是 NavigationDrawerFragment(和 layout/fragment_navigation_drawer.xml)。

它运行良好。然后,我更进一步。我的导航抽屉项目处于 immersive-sticky(全屏)模式。

@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;
    }

    ...

}

出现了一个问题。来自状态栏(顶部)和导航栏(底部)的 NavigationView 重叠阴影效果带保持不变。

我怎样才能摆脱它们?

我查看了 v7.app.ActionBarDrawerToggle 的来源或 Android 的 NavigationView,但没有成功。


已更新:

感谢@lcw_gg的指点,状态栏的阴影我已经完全去掉了(导航栏的阴影还在)。即在布局xml.

中设置android:windowFullscreen属性true

但我想在 Java 代码中执行此操作。我找到了一种方法,它可能等同于 xml 方法:

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

通过这样做,您不再需要将这两个标志 - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREENView.SYSTEM_UI_FLAG_FULLSCREEN - 设置为 decorationView.

仍然,我找不到摆脱导航栏阴影的方法。我正在等待解决方案。

我终于做到了

解决方案是将 FLAG_LAYOUT_NO_LIMITSFLAG_FULLSCREEN 一起用于 android.view.Window 对象。

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

这两个阴影都被完美去除了。

lcw_gg 的评论对于操纵 android.view.Window 非常有用。特别感谢他


更新 Android 11

不幸的是,当我将 phone 更新为 Android 11 (API-30) 时出现问题 returns。上述解决方案不再有效。另一种解决方案是 使用 app:insetForeground="@null"

而且我还得到了一个新的解决方案。 刚去掉SYSTEM_UI_FLAG_LAYOUT_STABLE就是了。这个SYSTEM_UI_FLAG_LAYOUT_STABLE应该是阴影效果的根本原因

对于我对 Google Developer 或 Whosebug 的拙劣研究,该标志总是被解释为与 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN and/or SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 一起使用,但是 那里关于 SYSTEM_UI_FLAG_LAYOUT_STABLE 本身 没有确切的解释,除了:

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

所以我自动将它与其他两个一起使用。但这是这个问题的根源。仅删除 SYSTEM_UI_FLAG_LAYOUT_STABLE 即可解决此问题(即使在 Android 10 或更早版本上)。

弃用那些身临其境的 FLAG

如您所知,View.SYSTEM_UI_FLAG_* 已从 Android 11 (API-30) 中弃用。

您将像下面这样使用:

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 {
    (...)
}

正如 google 人(如 Chris Banes)所解释的那样,Window.setDecorFitsSystemWindows(false) 几乎等同于 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,并且 SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATIONSYSTEM_UI_FLAG_LAYOUT_STABLE 相结合。至少阴影效果没有问题

对于那些遇到与上述问题相同但希望始终显示系统状态栏(因此不使用 WindowManager.LayoutParams.FLAG_FULLSCREEN)的用户,您需要做一些额外的工作来防止奇怪的内容蔓延在棒棒糖的状态栏下方。我在kitkat上测试的时候没注意到这个问题

我当时使用了两个导航抽屉(左侧和右侧),奇怪的是只有左侧的抽屉在底部投射阴影。如已接受的答案所示应用 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 后,阴影消失了,但随后内容在状态栏下方蔓延,导航抽屉在状态栏顶部绘制;一直到设备边框。

要解决这个问题,您必须覆盖 v21 中的主题 styles.xml 以包括:

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

这会将内容移回状态栏下方,同时防止抽屉式导航栏绘制在状态栏上方。

除了我的 v21 样式中的上述条目外,我的 activity 代码如下所示:

// 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);
}

采纳答案有问题。设置标志 FLAG_FULL_SCREENFLAG_LAYOUT_NO_LIMITS 将使 window 的插图为 0,并且内容可能位于系统 UI 后面。

它会起作用,因为 NavigationViewScrimInsetsFrameLayout 的子类,当 window 插入全部时,听 onApplyWindowInsetsView.setWillNotDraw 为真0.

您只需要添加到您的 NavigationView 中:

app:insetForeground="@null"

感谢 hata 我已经成功地通过合并他的原始代码完全隐藏系统 UI:

@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);
  }
}

在调用inflate之前在我的Activity的onCreate中添加了解决方案代码:

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

显然,标志 WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 是使全屏在我的设备上按预期工作的缺失部分 (API 21)