当导航抽屉滑动时隐藏ActionBar菜单项

29

我正在尝试实现一个导航抽屉,每当打开抽屉时,它会隐藏ActionBar中的菜单项。

我正在遵循谷歌的文档,但他们的代码并没有产生预期的行为。

http://developer.android.com/training/implementing-navigation/nav-drawer.html

使用这段代码,当抽屉完全打开时,菜单项会被隐藏,并在抽屉完全关闭时显示。

然而,Gmail应用程序的行为不同。只要抽屉以任何量打开,菜单项就会被隐藏。这是我想要的行为。有人知道如何实现吗?

谢谢!

7个回答

48

你尝试过这个吗:

  1. 在每次切换导航抽屉时,通过测量滑动偏移量,使用invalidateOptionsMenu()
  2. onPrepareOptionsMenu(Menu menu)中遍历每个菜单项并将其隐藏。

@Override

public boolean onPrepareOptionsMenu(Menu menu) {

    // If the nav drawer is open, hide action items related to the content view
    boolean drawerOpen = shouldGoInvisible;
    hideMenuItems(menu, !drawerOpen);
    return super.onPrepareOptionsMenu(menu);
}

private void hideMenuItems(Menu menu, boolean visible)
{

    for(int i = 0; i < menu.size(); i++){

        menu.getItem(i).setVisible(visible);

    }
}
检测导航抽屉菜单滑动的距离:

     mDrawerLayout.setDrawerListener(new DrawerListener(){
                    float mPreviousOffset = 0f;

        @Override
        public void onDrawerClosed(View arg0) {
                         super.onDrawerClosed(arg0);
                         shouldGoInvisible = false;
                         invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
        }

        @Override
        public void onDrawerOpened(View arg0) {
                         super.onDrawerOpened(arg0);
                         shouldGoInvisible = true;
                         invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
        }

        @Override
        public void onDrawerSlide(View arg0, float slideOffset) {
             super.onDrawerSlide(arg0, slideOffset);
             if(slideOffset > mPreviousOffset && !shouldGoInvisible){
                shouldGoInvisible = true;
                invalidateOptionsMenu();
            }else if(mPreviousOffset > slideOffset && slideOffset < 0.5f && shouldGoInvisible){
                shouldGoInvisible = false;
                invalidateOptionsMenu();
            }
            mPreviousOffset = slideOffset;


        }

        @Override
        public void onDrawerStateChanged(int arg0) {
            // or use states of the drawer to hide/show the items

        }});

注意:shouldGoInvisible 是一个类字段。


是的,这基本上就是我正在做的事情。问题在于,该代码直到抽屉完全打开后才被调用,而不是在抽屉打开时立即被调用,即使只是一点点,就像Gmail中那样。我在ActionBarDrawerToggle对象的onDrawerOpen()和onDrawerClose()中调用supportInvalidateOptionsMenu()。 - Synergy807
@Synergy807 我已经测试过了,这个功能完美地实现了你的需求。无论抽屉向哪个方向滑动多少距离,它都会被隐藏起来。 - Nikola Despotoski
1
感谢您的帮助!onDrawerSlide和onDrawerStateChanged方法正是我所需要的。 - Synergy807
"shouldGoInvisible是一个类字段。你能展示一下它是如何定义的吗?" - Axel
如果主活动没有选项菜单,但子片段有自己的选项菜单,该如何实现? - User3
显示剩余2条评论

5

如果你想在抽屉菜单进入屏幕时立即覆盖操作栏,并在抽屉菜单不再可见时恢复操作栏(就像Gmail在2014年3月20日之后的行为一样),可以使用以下代码:

mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
    R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {

  @Override
  public void onDrawerStateChanged(int newState) {
    super.onDrawerStateChanged(newState);

    boolean isOpened = mDrawerLayout.isDrawerOpen(mDrawerList);
    boolean isVisible = mDrawerLayout.isDrawerVisible(mDrawerList);

    if (!isOpened && !isVisible) {
      if (newState == DrawerLayout.STATE_IDLE) {
        // drawer just hid completely
        restoreActionBar();
      } else {
        // } else if (newState == DrawerLayout.STATE_SETTLING) {
        // drawer just entered screen
        overrideActionBar();
      }
    }
  }

  private void restoreActionBar() {
    getSupportActionBar().setTitle(mTitle);
    supportInvalidateOptionsMenu();
  }

  private void overrideActionBar() {
    getSupportActionBar().setTitle(mDrawerTitle);
    supportInvalidateOptionsMenu();
  }
};

// Set the drawer toggle as the DrawerListener
mDrawerLayout.setDrawerListener(mDrawerToggle);

根据您的需求修改restoreActionBar()overrideActionBar()方法。

无需区分滑动和主页按钮,也无需测量滑动长度。

变化

如果您不想引用抽屉列表视图,请改用以下代码:

    boolean isOpened = mDrawerLayout.isDrawerOpen(GravityCompat.START);
    boolean isVisible = mDrawerLayout.isDrawerVisible(GravityCompat.START);

你可能需要使用GravityCompat.END,具体取决于在XML布局中指定的内容。

编辑-关于操作

上述示例未隐藏与导航抽屉下方内容相关的操作栏项。要这样做或在抽屉可见时显示不同的图标集,您必须手动跟踪抽屉是打开还是关闭。

除了上面的代码之外,还要声明private boolean mDrawerVisible = false并进行适当的保存/恢复状态处理。 然后按以下方式修改mDrawerToggle内部方法:

  private void restoreActionBar() {
    getSupportActionBar().setTitle(mTitle);
    mDrawerVisible = false;
    supportInvalidateOptionsMenu();
  }

  private void overrideActionBar() {
    getSupportActionBar().setTitle(mDrawerTitle);
    mDrawerVisible = true;
    supportInvalidateOptionsMenu();
  }

最后,在 onCreateOptionsMenu 中填充不同的菜单资源或在 onPrepareOptionsMenu 中根据 mDrawerVisible 的值显示/隐藏不同的操作。


2

我有些同意尼古拉的说法,但只要在抽屉状态改变时更新图标就足够了。

创建一个全局变量来跟踪抽屉的状态:

private int mDrawerState;

设置一个新的 DrawerListener:

mDrawerLayout.setDrawerListener(new DrawerListener() {

  @Override
  public void onDrawerStateChanged(int state) {
    mDrawerState = state;
    invalidateOptionsMenu();
  }

  @Override
  public void onDrawerSlide(View view, float slide) {
    // TODO Auto-generated method stub
  }

  @Override
  public void onDrawerOpened(View view) {
    // TODO Auto-generated method stub
  }

  @Override
  public void onDrawerClosed(View view) {
    // TODO Auto-generated method stub
  }
});

更新菜单可见性:

boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawer);
for(int i=0;i<menu.size();i++){
  // If the drawer is moving / settling or open do not draw the icons
  menu.getItem(i).setVisible(mDrawerState!=DrawerLayout.STATE_DRAGGING &&
      mDrawerState!=DrawerLayout.STATE_SETTLING && !drawerOpen);
}

谢谢,这非常类似于我最终所做的事情! - Synergy807
1
实际上,经过进一步的调查,我发现使用状态并不完全有效,至少对于我的实现来说是这样。如果你只是把手指放在屏幕边缘,导航抽屉的一个小片段就会显示出来(就像 Gmail 等应用程序一样)。然而,它仍然会显示操作栏菜单项。你的也是这样吗? - Synergy807
不,使用状态在我的设备上似乎已经足够了。 - Ljdawson

1
我有一个更好的解决方案来回答这个问题:

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    if (navigationDrawerFragment.isDrawerOpen()) {
        menu.clear();
    }
    return super.onPrepareOptionsMenu(menu);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    if (!navigationDrawerFragment.isDrawerOpen()) {
        // Only show items in the action bar relevant to this screen
        // if the drawer is not showing. Otherwise, let the drawer
        // decide what to show in the action bar.
        showLocalContextActionBar();
        return false;
    }
    return super.onCreateOptionsMenu(menu);
}

0
我采用了@Laurence Dawson的答案并进行了简化。这个解决方案不需要使用任何类成员。
onCreate()期间执行以下代码:
    DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);

    drawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() {

        @Override
        public void onDrawerSlide(View view, float v) {
            invalidateOptionsMenu();
        }

        @Override
        public void onDrawerClosed(View view) {
            invalidateOptionsMenu();
        }

        @Override
        public void onDrawerOpened(View view) {}

        @Override
        public void onDrawerStateChanged(int state) {}
    });

并重写此方法:

@Override
public boolean onPrepareOptionsMenu(Menu menu) {

    DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    boolean actionsVisibility = !drawerLayout.isDrawerVisible(Gravity.START);

    for(int i=0;i<menu.size();i++){
        menu.getItem(i).setVisible(actionsVisibility);
    }

    return super.onPrepareOptionsMenu(menu);
}

一些注意事项:

  • 上述实现假定与NavigationDrawer相关联的视图在XML中设置了layout_gravitystart
  • 与OP的问题无关,但很烦人:似乎存在某种导致抽屉沿途卡住的错误。如果您确实观察到这种行为,这里是解决方案:使用示例的Android Navigation Drawer bug

0

我有不同的代码,但是相同的解决方案:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    hideMenuItems(menu, !mShouldGoInvisible);
    return super.onCreateOptionsMenu(menu);
}
private void hideMenuItems(Menu menu, boolean visible){
    for(int i = 0; i < menu.size(); i++){

        menu.getItem(i).setVisible(visible);
    }
}

并且

 @Override
public void onNavigationDrawerListener(boolean opened, int position) {

    if (opened){
        mShouldGoInvisible = true;
        invalidateOptionsMenu();

    } else {
        mShouldGoInvisible = false;
        invalidateOptionsMenu();
    }
}

0

如果您想要隐藏所有菜单项,只需使用:

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);

    return showActionBarMenu; // boolean value, set it in drawer listeners as class variable
}

那么你就不需要每个菜单项都可见。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接