如何检测DrawerLayout开始打开?

65

我有一些选项卡,当导航抽屉开始打开时,我想要隐藏它们。我现在的代码是在导航抽屉完成打开后才隐藏选项卡,但这不是我想要的。

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

这是我尝试过的方法:

  • mDrawerLayout设置一个onClickListener,但onClick从未被调用
  • mDrawerLayout设置一个onTouchListener,但onTouch从未被调用
  • 研究了ActionBarDrawerToggleDrawerLayout类,并没有找到类似于onDrawerStartedOpening的东西。
8个回答

87

已弃用: 请参考其他答案以获得更合适的解决方案

有两种可能的方法:

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

1
第一种方法对我有效,但是有一个问题。当我试图将抽屉滑动“不完全”并释放它回到原始位置时,选项卡没有恢复。事实证明,在setNavigationMode之前调用invalidateOptionsMenu();。其他所有都正常工作。谢谢! - Oleksiy
在我实现了导航抽屉的MainActivity中,如何使用onDrawerStateChanged来隐藏某些东西(例如:FAB按钮)? - Parth Sharma
7
当使用onDrawerSlide时,会收到许多已弃用的代码通知和“无法解析isDrawerOpen()”错误。是否有更新的答案? - Micro
2
setDrawerListener已被弃用。改为使用addDrawerListener。https://developer.android.com/reference/android/support/v4/widget/DrawerLayout.html - Mahendran Sakkarai
这个说法是错误的!!! “STATE_SETTLING states - 表示当抽屉开始移动(无论是打开还是关闭)时,会报告此状态”显然,它所写的是“指示抽屉正在定位到最终位置的过程中”,因此您得到的是迟到的结果,而不是在开始时,所以对于问题来说是无用的。你应该从你的回答中删除这个。我刚刚浪费了一些时间测试它,因为我一开始很喜欢这个想法。 - Renetik
显示剩余3条评论

47

目前 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.
        }
    });

完美运行。干杯!


最新的答案!谢谢! - RamithDR
5
也有可用的DrawerLayout.SimpmleDrawerListener,它为您不需要的内容提供了无操作(no-ops)。 - ElliotM

12

尝试覆盖 DrawerLayout.DrawerListener 的方法

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

3
isDrawerOpen()未定义。它来自哪里? - c0dehunter
1
@PrimožKralj 使用 DrawerLayout.isDrawerOpen(int drawerGravity)。 - Allan Veloso

11

最新解决方案:

正如其他人所建议的那样,当前的答案已过时,建议使用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
                }
            }
        }
    });

10

对于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()

1
我不明白为什么几乎每个人都建议使用settle,因为那是在抽屉 settling 时调用的,而不是在开始打开时调用的?这个问题难道不是很清楚吗?
如何检测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...
}

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

这是最好的方式。


0

fookwood的答案对我没有用,但在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
}

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