How to detect that the DrawerLayout started opening?

AndroidNavigation DrawerDrawerlayoutDrawertoggle

Android Problem Overview


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.

Android Solutions


Solution 1 - Android

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

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

Solution 2 - Android

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!

Solution 3 - Android

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

Solution 4 - Android

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

Solution 5 - Android

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

Solution 6 - Android

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.

Solution 7 - Android

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

Solution 8 - Android

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
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionOleksiyView Question on Stackoverflow
Solution 1 - AndroidPavel DudkaView Answer on Stackoverflow
Solution 2 - AndroidKirill KarmazinView Answer on Stackoverflow
Solution 3 - AndroidfookwoodView Answer on Stackoverflow
Solution 4 - AndroidNoHarmDanView Answer on Stackoverflow
Solution 5 - AndroidKishan SolankiView Answer on Stackoverflow
Solution 6 - AndroidStefano CristoniView Answer on Stackoverflow
Solution 7 - AndroidRenetikView Answer on Stackoverflow
Solution 8 - Androidbastami82View Answer on Stackoverflow