19

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

当抽屉关闭时,导航抽屉处于沉浸式粘性状态

这是完美的工作。然后,我走得更远。我的导航抽屉项目处于沉浸式粘性(全屏)模式。

@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 上从状态栏(顶部)和导航栏(底部)派生的重叠阴影效果带保持静止。

导航抽屉打开时

当导航抽屉打开并显示粘性状态和导航栏时

我怎样才能摆脱它们?

我查看了 Android 的 v7.app.ActionBarDrawerToggle 或 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.

状态栏的阴影消失了,而导航栏的阴影还在

尽管如此,我还是找不到摆脱导航栏阴影的方法。我正在等待解决方案。

4

4 回答 4

27

最后,我做到了。

解决方案是与对象FLAG_LAYOUT_NO_LIMITS一起使用。FLAG_FULLSCREENandroid.view.Window

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

这已经完美地摆脱了这两个阴影。

在此处输入图像描述

lcw_gg 的评论是非常有用的线索来操纵android.view.Window. 特别感谢他。


Android 11 更新

不幸的是,当我将手机更新到 Android 11 (API-30) 时,问题又出现了。上述解决方案不再有效。另一种解决方案是@AllanVeloso's answer usingapp:insetForeground="@null"

我也得到了一个新的解决方案。摆脱SYSTEM_UI_FLAG_LAYOUT_STABLE就是了。这SYSTEM_UI_FLAG_LAYOUT_STABLE应该是阴影效果的根本原因。

对于我对 Google Developer 或 StackOverflow 的谦虚研究,该标志总是被解释为与SYSTEM_UI_FLAG_LAYOUT_FULLSCREENand/or一起使用SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,但除了以下内容之外没有关于SYSTEM_UI_FLAG_LAYOUT_STABLE其自身的确切解释:

这意味着在那里看到的插图将始终代表应用程序可以预期的最坏情况作为连续状态。

所以我自动将它与其他两个一起使用。但这就是这个问题的根源。只需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 {
    (...)
}

正如谷歌人(如Chris Banes)所解释Window.setDecorFitsSystemWindows(false)的几乎等同于SYSTEM_UI_FLAG_LAYOUT_FULLSCREENSYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION结合SYSTEM_UI_FLAG_LAYOUT_STABLE. 至少在阴影效果方面没有问题。

于 2015-10-06T13:44:24.397 回答
18

接受答案有问题。设置标志FLAG_FULL_SCREEN并将FLAG_LAYOUT_NO_LIMITS使窗口的 insets 为 0,并且内容可能位于系统 UI 后面。

它会起作用,因为当窗口插入全部为 0 时NavigationView,它是 , 的子类ScrimInsetsFrameLayout,监听onApplyWindowInsets和为真。View.setWillNotDraw

你只需要添加到你的NavigationView这个:

app:insetForeground="@null"
于 2018-08-23T00:56:52.820 回答
1

对于那些与上述相同的问题,但希望保持系统状态栏始终显示(因此不使用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 样式中的上述条目之外,我的活动代码如下所示:

// 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);
}
于 2016-09-30T19:23:27.110 回答
1

感谢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);
  }
}

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

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

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

于 2020-08-27T06:43:48.977 回答