How to detect that the DrawerLayout started opening?
Asked Answered
G

8

65

So I have tabs that I want to hide when the Navigation Drawer starts opening. The code I have hides them when it finished opening, but it's not what I want.

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

Here's what I tried:

  • Setting an onClickListener to mDrawerLayout. onClick never gets called
  • Setting an onTouchListener to mDrawerLayout. onTouch never gets called
  • Researched ActionBarDrawerToggle and DrawerLayout classes. Could not find anything like onDrawerStartedOpening.
Gunfire answered 29/4, 2014 at 19:8 Comment(0)
F
87

DEPRECATED: See other answers for a more suitable solution

There are 2 possible ways to do that:

  1. Use onDrawerSlide(View drawerView, float slideOffset) callback

slideOffset changes from 0 to 1. 1 means it is completely open, 0 - closed.

Once offset changes from 0 to !0 - it means it started opening process. Something like:

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. Use onDrawerStateChanged(int newState) callback

You need to listen to STATE_SETTLING states - this state is reported whenever drawer starts moving (either opens or closes). So once you see this state - check whether drawer is opened now and act accordingly:

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);
Francie answered 29/4, 2014 at 19:26 Comment(8)
The first approach worked for me, however there was one issue. When I tried to slide the drawer not all the way and release it to go to its original position, the tabs didn't restore. It turned out that the call to invalidateOptionsMenu(); has to be before setNavigationMode. Everything else worked fine. Thanks!Gunfire
How i can hide something(Like: FAB button) using onDrawerStateChanged in my MainActivity where i have implement my navigation drawer.. ?Oller
Getting a lot deprecated code notifications and "cannot resolve isDrawerOpen()" errors when using onDrawerSlide... Is there an updated answer?Coakley
@ first approach is super awesomeDigitoxin
setDrawerListener is deprecated. Changed to addDrawerListener. developer.android.com/reference/android/support/v4/widget/…Eavesdrop
This statement is wrong!!! "STATE_SETTLING states - this state is reported whenever drawer starts moving (either opens or closes)" Obviously it is what is written "Indicates that a drawer is in the process of settling to a final position" so you get it late not in the start so unusable for op question. You should remove this from your answer. I just wasted some time because I was testing that because I liked idea at first sight.Grassofparnassus
@Renetik, well, I would say you should be careful when using a SO answer from 2014 :) I bet APIs have changed since thenFrancie
@PavelDudka well sure it changes all the time in some places but here I didn't expect it and answer should be then updated ... I wrote here my answer too after I found issues so maybe this is stack overflow logic that something accepted as answer is true answer forever should be somehow improved.Grassofparnassus
A
47

Currently accepted answer by Pavel Dudka is already deprecated. Please use mDrawerLayout.addDrawerListener() method instead to set a listener.

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

Works perfectly. Cheers!

Ambagious answered 3/4, 2017 at 12:20 Comment(2)
Most up to date answer! Thanks!Conch
DrawerLayout.SimpmleDrawerListener is also available and provides no-ops for stuff you don't need.Humanism
W
12

try to override a method of DrawerLayout.DrawerListener

@Override
public void onDrawerStateChanged(int newState) {
    if( newState == DrawerLayout.STATE_DRAGGING && isDrawerOpen() == false ) {
        // this where Drawer start opening
    }
}
Weigand answered 29/4, 2014 at 19:28 Comment(2)
isDrawerOpen() is not defined. Where is it from?Frontogenesis
@PrimožKralj Use DrawerLayout.isDrawerOpen(int drawerGravity)Mojave
O
11

Up-to-date solution:

As others have suggested, the current answer is outdated and it's advised to use mDrawerLayout.addDrawerListener(). A working solution would then be:

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

Naturally, replace GravityCompat.START with whatever identifies your drawer (layout ID or its gravity ~ location).

Also, if you want to detect when the drawer starts closing, you can simply do:

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
                }
            }
        }
    });
Outsole answered 29/6, 2018 at 12:22 Comment(0)
C
10

For 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()
Coltun answered 12/11, 2018 at 10:17 Comment(0)
L
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();

It's the best way.

Logogram answered 23/5, 2017 at 15:52 Comment(0)
G
1

I don't get why almost everybody here suggested to use settling, while that is called when drawer is settling so no when started opening ? Isn't this question clearly about that ?

How to detect that the DrawerLayout started opening?

Then I don't get how people can propose that code that will be called repeatedly unknown times until opening is finished when using simple callback with slideOffset, this can cause some unknown issues depending on you implementation an it just waste performance. You just want to know when drawer is starting to open and to know it once, not 100 times ...

I don't see as super simple solution but I am implementing it like this:

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

Usage:

drawer.onDrawerOpening {
    panelView.showingInPager(true) //Just my use case...
}
Grassofparnassus answered 17/12, 2021 at 20:29 Comment(0)
E
0

fookwood answer did not work for me but slight modification in the if statment did the trick)

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
}
Earp answered 6/7, 2017 at 19:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.