29

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

我正在关注谷歌的文档,但是他们的代码没有产生预期的行为。

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

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

但是,Gmail 应用程序的行为有所不同。只要抽屉打开任意数量,菜单项就会被隐藏。这是我想要的行为。有谁知道如何实现这一目标?

谢谢!

4

7 回答 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是类字段。

于 2013-08-08T20:22:15.557 回答
5

如果您想在抽屉进入屏幕后立即覆盖操作栏并在抽屉不再可见时恢复操作栏(与 2014 年 3 月 20 日的 Gmail 行为完全相同),您可以使用以下代码:

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.

于 2014-03-20T23:57:44.997 回答
2

我有一半同意 Nikola,但只要在抽屉状态有更新时更新图标就足够了

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

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);
}
于 2013-08-08T22:24:26.520 回答
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);
}
于 2015-06-25T20:59:06.767 回答
0

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

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

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

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

于 2013-10-14T06:06:36.800 回答
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_gravity设置为。start
  • 与 OP 的问题无关,但很烦人:似乎有某种错误导致抽屉沿途卡住。如果您确实观察到这种行为,这里是解决方案:Android Navigation Drawer bug using the sample
于 2015-02-08T22:22:22.980 回答
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();
    }
}
于 2015-12-23T15:21:23.363 回答