如何检测 DrawerLayout 开始打开?

Posted

技术标签:

【中文标题】如何检测 DrawerLayout 开始打开?【英文标题】:How to detect that the DrawerLayout started opening? 【发布时间】: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 这样的东西。

【问题讨论】:

【参考方案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);
    使用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 已弃用。更改为addDrawerListener。 developer.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

【讨论】:

以上是关于如何检测 DrawerLayout 开始打开?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用可见句柄实现 DrawerLayout

菜单抽屉仅用两根手指滑动即可打开 DrawerLayout

DrawerLayout 和 NavigationView - 未找到属性“菜单”

带有 DrawerLayout 的 ScrollView 内的 ListView

在 DrawerLayout 上禁用手势监听器

关闭左侧时,防止DrawerLayout打开右侧?