【问题标题】:How to detect that the DrawerLayout started opening?如何检测 DrawerLayout 开始打开?
【发布时间】:2014-06-15 21:33:39
【问题描述】:

所以当导航抽屉开始打开时,我想隐藏标签。我的代码在完成打开时将它们隐藏起来,但这不是我想要的。

mDrawerToggle = new ActionBarDrawerToggle(
        this,                 
        mDrawerLayout,        
        R.drawable.ic_drawer,  
        R.string.drawer_open,  
        R.string.drawer_close  
) {
    @Override
    public void onDrawerClosed(View view) {
        invalidateOptionsMenu(); 
        setActionBarMode(ActionBar.NAVIGATION_MODE_TABS);
    }

    @Override
    public void onDrawerOpened(View drawerView) {
        invalidateOptionsMenu(); 
        setActionBarMode(ActionBar.NAVIGATION_MODE_STANDARD);
    }

};
mDrawerLayout.setDrawerListener(mDrawerToggle);

这是我尝试过的:

  • onClickListener 设置为mDrawerLayoutonClick 永远不会被调用
  • onTouchListener 设置为mDrawerLayoutonTouch 永远不会被调用
  • 研究了ActionBarDrawerToggleDrawerLayout 类。找不到像 onDrawerStartedOpening 这样的东西。

【问题讨论】:

    标签: android navigation-drawer drawerlayout drawertoggle


    【解决方案1】:

    已弃用:查看其他答案以获得更合适的解决方案

    有两种可能的方法来做到这一点:

    1. 使用onDrawerSlide(View drawerView, float slideOffset)回调

    slideOffset 从 0 变为 1。1 表示完全打开,0 - 关闭。

    一旦偏移量从0 更改为!0 - 这意味着它开始打开过程。比如:

    mDrawerToggle = new ActionBarDrawerToggle(
            this,                 
            mDrawerLayout,        
            R.drawable.ic_drawer,  
            R.string.drawer_open,  
            R.string.drawer_close  
    ) {
    
        @Override
        public void onDrawerSlide(View drawerView, float slideOffset) {
            if (slideOffset == 0
                    && getActionBar().getNavigationMode() == ActionBar.NAVIGATION_MODE_STANDARD) {
                // drawer closed
                getActionBar()
                        .setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
                invalidateOptionsMenu();
            } else if (slideOffset != 0
                    && getActionBar().getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) {
                // started opening
                getActionBar()
                        .setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
                invalidateOptionsMenu();
            }
            super.onDrawerSlide(drawerView, slideOffset);
        }
    };
    mDrawerLayout.setDrawerListener(mDrawerToggle);
    
    1. 使用onDrawerStateChanged(int newState)回调

    您需要收听STATE_SETTLING 状态 - 每当抽屉开始移动(打开或关闭)时都会报告此状态。所以一旦你看到这个状态 - 检查抽屉现在是否打开并采取相应的行动:

    mDrawerToggle = new ActionBarDrawerToggle(
            this,                 
            mDrawerLayout,        
            R.drawable.ic_drawer,  
            R.string.drawer_open,  
            R.string.drawer_close  
    ) {
        @Override
        public void onDrawerStateChanged(int newState) {
            if (newState == DrawerLayout.STATE_SETTLING) {
                if (!isDrawerOpen()) {
                    // starts opening
                    getActionBar()
                            .setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
                } else {
                    // closing drawer
                    getActionBar()
                            .setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
                }
                invalidateOptionsMenu();
            }
        }
    };
    mDrawerLayout.setDrawerListener(mDrawerToggle);
    

    【讨论】:

    • 第一种方法对我有用,但是有一个问题。当我试图滑动抽屉不是一直并将其释放到原来的位置时,标签没有恢复。原来对invalidateOptionsMenu(); 的调用必须在setNavigationMode 之前。其他一切都很好。谢谢!
    • 如何在我实现了导航抽屉的 MainActivity 中使用 onDrawerStateChanged 隐藏某些东西(例如:FAB 按钮)?
    • 在使用onDrawerSlide 时收到大量已弃用的代码通知和“无法解决 isDrawerOpen()”错误... 有更新的答案吗?
    • @第一种方法超级棒
    • setDrawerListener 已弃用。更改为addDrawerListenerdeveloper.android.com/reference/android/support/v4/widget/…
    【解决方案2】:

    Pavel Dudka 目前接受的答案已被弃用。 请改用mDrawerLayout.addDrawerListener() 方法设置监听器。

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
    
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                //Called when a drawer's position changes.
            }
    
            @Override
            public void onDrawerOpened(View drawerView) {
                //Called when a drawer has settled in a completely open state.
                //The drawer is interactive at this point.
                // If you have 2 drawers (left and right) you can distinguish 
                // them by using id of the drawerView. int id = drawerView.getId(); 
                // id will be your layout's id: for example R.id.left_drawer            
            }
    
            @Override
            public void onDrawerClosed(View drawerView) {
                // Called when a drawer has settled in a completely closed state.
            }
    
            @Override
            public void onDrawerStateChanged(int newState) {
                // Called when the drawer motion state changes. The new state will be one of STATE_IDLE, STATE_DRAGGING or STATE_SETTLING.
            }
        });
    

    完美运行。干杯!

    【讨论】:

    • 最新答案!谢谢!
    • DrawerLayout.SimpmleDrawerListener 也可用,并为您不需要的东西提供无操作。
    【解决方案3】:

    尝试重写 DrawerLayout.DrawerListener 的方法

    @Override
    public void onDrawerStateChanged(int newState) {
        if( newState == DrawerLayout.STATE_DRAGGING && isDrawerOpen() == false ) {
            // this where Drawer start opening
        }
    }
    

    【讨论】:

    • isDrawerOpen() 未定义。哪里来的?
    • @PrimožKralj 使用 DrawerLayout.isDrawerOpen(int drawerGravity)
    【解决方案4】:

    最新解决方案:

    正如其他人所建议的那样,当前答案已过时,建议使用mDrawerLayout.addDrawerListener()。一个可行的解决方案是:

    mDrawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
            @Override
            public void onDrawerStateChanged(int newState) {
                if (newState == DrawerLayout.STATE_SETTLING && !mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
                    // Drawer started opening
                }
            }
        });
    

    当然,将 GravityCompat.START 替换为任何标识您的抽屉(布局 ID 或其重力 ~ 位置)。

    另外,如果您想检测抽屉何时开始关闭,您可以简单地执行以下操作:

    mDrawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
            @Override
            public void onDrawerStateChanged(int newState) {
                if (newState == DrawerLayout.STATE_SETTLING) {
                    if (!mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
                        // Drawer started opening
                    } else {
                        // Drawer started closing
                    }
                }
            }
        });
    

    【讨论】:

      【解决方案5】:

      对于 Kotlin

      var toggle = object : ActionBarDrawerToggle(this,
                      drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) {
      
                  override fun onDrawerOpened(drawerView: View) {
                      super.onDrawerOpened(drawerView)
                  }
              }
      drawer_layout.addDrawerListener(toggle)
      toggle.syncState()
      

      【讨论】:

        【解决方案6】:
        drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close){
              @Override
              public void onDrawerOpened(View drawerView) {
                   super.onDrawerOpened(drawerView);
                   app.sendScreenView("Menu");
              }
        };
        drawer.setDrawerListener(toggle);
        toggle.syncState();
        

        这是最好的方法。

        【讨论】:

          【解决方案7】:

          我不明白为什么这里几乎每个人都建议使用沉降,而当抽屉沉降时调用它,而开始打开时则没有?这个问题不是很清楚吗?

          如何检测 DrawerLayout 开始打开?

          然后我不明白人们如何建议在使用带有 slideOffset 的简单回调时重复调用未知次数直到打开完成的代码,这可能会导致一些未知问题,具体取决于您的实现,它只会浪费性能。你只想知道抽屉什么时候开始打开并且知道一次,而不是 100 次......

          我不认为是超级简单的解决方案,但我是这样实现的:

          val DrawerLayout.isDrawerOpen get() = isDrawerOpen(START) || isDrawerOpen(END)
          
          fun DrawerLayout.onDrawerOpening(function: (DrawerLayout) -> Unit) {
              val drawerLayout = this
              var onDrawerOpeningCalled = false
              addDrawerListener(object : SimpleDrawerListener() {
                  override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
                      super.onDrawerSlide(drawerView, slideOffset)
                      if (!onDrawerOpeningCalled && slideOffset > 0f && !isDrawerOpen) {
                          function(drawerLayout)
                          onDrawerOpeningCalled = true
                      }
                  }
          
                  override fun onDrawerStateChanged(newState: Int) {
                      if (newState == STATE_IDLE) onDrawerOpeningCalled = false
                  }
              })
          }
          

          用法:

          drawer.onDrawerOpening {
              panelView.showingInPager(true) //Just my use case...
          }
          

          【讨论】:

            【解决方案8】:

            福克伍德的答案对我不起作用,但对 if 语句进行了轻微修改就可以了)

            ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, navigationDrawerLayout, topToolbar,
                            R.string.open_drawer, R.string.close_drawer) {
                        @Override public void onDrawerStateChanged(int newState) {
                            if (newState == DrawerLayout.STATE_SETTLING && !navigationDrawerLayout.isDrawerOpen(navigationDrawerView)) {
                                // this where Drawer start opening
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-05-24
              • 1970-01-01
              • 1970-01-01
              • 2013-03-23
              • 2016-01-11
              • 1970-01-01
              • 2011-09-23
              相关资源
              最近更新 更多