当导航抽屉滑动任意量时隐藏操作栏菜单项

Posted

技术标签:

【中文标题】当导航抽屉滑动任意量时隐藏操作栏菜单项【英文标题】:Hide ActionBar MenuItems when Navigation Drawer slides for any amount 【发布时间】:2013-08-10 16:59:59 【问题描述】:

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

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

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

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

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

谢谢!

【问题讨论】:

【参考方案1】:

你试过了吗:

    通过测量滑动偏移量,在切换导航抽屉时使用invalidateOptionsMenu()

    遍历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 我已经测试过了,它可以完美地工作,就像你想要的那样。每当它向任何方向滑动任意量时,它都会隐藏抽屉。 感谢您的帮助!我需要的 onDrawerSlide 和 onDrawerStateChanged 方法。 shouldGoInvisible 是一个类字段你能说明它是如何定义的吗? private boolean shouldGoInvisible = false;【参考方案2】:

如果您想在抽屉进入屏幕后立即覆盖操作栏并在抽屉不再可见时恢复操作栏(与 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();
  

最后在onCreateOptionsMenuinflate不同的菜单资源或在onPrepareOptionsMenu根据mDrawerVisible的值显示/隐藏不同的动作。

【讨论】:

【参考方案3】:

我有一半同意 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);

【讨论】:

谢谢,这与我最终所做的非常相似! 其实经过进一步调查,我发现使用状态不太好用,至少我的实现是这样。如果您只是将手指放在屏幕边缘,则会显示一小部分导航抽屉(就像 gmail 等一样)。但是,它仍会显示操作栏菜单项。你的也一样吗? 不,在我的设备上使用状态似乎就足够了。【参考方案4】:

我对这个问题有更好的解决方案:

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

【讨论】:

【参考方案5】:

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

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

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

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

【讨论】:

【参考方案6】:

我接受了@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

【讨论】:

【参考方案7】:

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

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

【讨论】:

以上是关于当导航抽屉滑动任意量时隐藏操作栏菜单项的主要内容,如果未能解决你的问题,请参考以下文章

在窗口调整大小时一一隐藏菜单项

尝试使用菜单下拉菜单创建导航栏,但我的菜单项似乎不想出现,或者它们隐藏在我的父容器中

更改导航抽屉中菜单项的文本颜色

Android中的导航抽屉菜单项标题颜色

Android - 导航抽屉 - 与动态菜单项重叠的片段

如何创建带有菜单项的滑动抽屉?