The DrawerLayout take noticeable time to close
here is my code:
#Override
public void onDrawerOpened(View drawerView) {
if (items.size() == 0)
view.setVisibility(View.GONE);
else view.setVisibility(View.VISIBLE);}
super.onDrawerOpened(drawerView);
invalidateOptionsMenu();
}
is there a solution for that?
Your problem is that the onDrawerOpened() method doesn't fire until the drawer is completely open. The perceived delay is due to the View being visible during the opening.
One solution would be to disable dragging the drawer, and only allow the toggle to open and close it. You could then do your size check before opening it programmatically. This, though, would require locking the drawer to the appropriate state in each of the onDrawerOpened() and onDrawerClosed() methods, and, of course, you'd lose that standard mode of interaction.
The preferred option is probably to do the check and visibility setting as soon as possible as the drawer is starting to open. We can do this in the onDrawerSlide() method instead, keeping a boolean flag to determine if the drawer is sliding after having been closed. For example:
actionBarDrawerToggle = new ActionBarDrawerToggle(...) {
private boolean opened;
#Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, slideOffset);
if (slideOffset == 0) {
opened = false;
}
else {
if (!opened) {
opened = true;
if (items.size() == 0) {
view.setVisibility(View.GONE);
}
else {
view.setVisibility(View.VISIBLE);
}
}
}
}
...
}
have you include
actionBarDrawerToggle.syncState()
What does syncState() exactly do?
Well, ActionBarDrawerToggle.syncState() will synchronize the changed icon's state, which deponds on actions of DrawerLayout. If you ever tried to remove the syncState(), you will realize that the icon of arrow won't rotate any more.
Related
I have created an ActionDrawer for my Wear OS App and want to change the icon and text on runtime. This code is working and my app continues to run without errors, but I am not able to slide up the ActionDrawer anymore. Also, when the ActionDrawerMenu is already opened and I press the button there, it disappears and my UI of the app seems frozen.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Top navigation drawer
WearableNavigationDrawerView wearableNavigationDrawer = (WearableNavigationDrawerView) findViewById(R.id.top_navigation_drawer);
wearableNavigationDrawer.setAdapter(new NavigationDrawerAdapter());
wearableNavigationDrawer.getController().peekDrawer();
//Bottom action drawer
WearableActionDrawerView wearableActionDrawer = (WearableActionDrawerView) findViewById(R.id.bottom_action_drawer);
wearableActionDrawer.getController().peekDrawer();
wearableActionDrawer.setOnMenuItemClickListener(this);
}
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
final int itemId = menuItem.getItemId();
switch(itemId) {
case R.id.menuItem_pause:
if(intent == null) {
menuItem.setIcon(getDrawable(R.drawable.ic_pause));
menuItem.setTitle(R.string.actionDrawerMenuPause);
} else {
menuItem.setIcon(getDrawable(R.drawable.ic_play));
menuItem.setTitle(R.string.actionDrawerMenuStart);
}
invalidateOptionsMenu();
return true;
}
return false;
}
I have tried some solutions with invalidateOptionsMenu(), onPrepareOptionsMenu() and onCreateOptionsMenu() but nothing worked for me. I think this just works with standard Android mobile apps but not with Wear OS. So how can I change the text and icon of my WearableActionDrawer-MenuItems when pressing a MenuItem?
I found the solution by accident. It's just this one line of code that programmatically implements the menu.
//Bottom action drawer
WearableActionDrawerView wearableActionDrawer = findViewById(R.id.bottom_action_drawer);
wearableActionDrawer.getController().peekDrawer();
wearableActionDrawer.setOnMenuItemClickListener(this);
getMenuInflater().inflate(R.menu.action_drawer_menu, wearableActionDrawer.getMenu());
No, its not a re asked question
I've searched so well. but nowhere found how to do this. I want to change the hamburger icon to back icon by calling a method from my activity. I can do it with Drawer.. but I wanna do it without opening drawer..
for example, when a fragment is shown.. the hamburger will turn into back arrow(with animation)
then clicking the back arrow will hide fragment and turn the arrow back into hamburger.
I want the animation while doing this.. is there any way to do that? or its only possible while opening/closing drawer
Thanks in advance
I did it by Calling
onDrawerSlide(View v, float f);
where float value from 0.0f to 1.0f defines the progress of transforming from hamburger to back arrow.
Then I used object animator to call this method 100 times each time float value 0.1 increased
and the magic happened.
Thank you #gauravsarma for showing me the way.
Do the following
ActionBarDrawerToggle toolbarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
toolbar, R.string.drawer_open, R.string.drawer_close) {
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
invalidateOptionsMenu();
}
public void onDrawerOpened(View view) {
super.onDrawerOpened(view);
invalidateOptionsMenu();
}
};
drawerLayout.setDrawerListener(toolbarDrawerToggle);
toolbarDrawerToggle.syncState();
I am having an issue with the toolbar and the back button. Here is the setup I have:
When I add a detail fragment, I animate the toolbar hamburger as outlined here. and this causes the hamburger to animate to an arrow.
Even in the comments section, a user mentions:
This works perfectly. Just set start=0 and end=1 to go from hamburger
to arrow, and start=1 and end=0 for arrow to hamburger. One thing
you'll have to keep track of is when the drawer is closed when the
arrow is shown. At this point, the hamburger ends up being shown
(because of the drawer's slide), which you'll have to correct.
But I cannot figure out how to get the back arrow to function properly. When I press the back arrow, the drawer opens and the detail fragment does not pop. How should I go about implementing this?
Questions
How should I animate hamburger to back arrow when adding a detail fragment? assuming the linked solution is not good enough.
How do I override the back arrow to perform only specific functions I wish? like animate to hamburger, pop back stack and NOT open the drawer.
After several hours of searching and playing around, I was able to build a solution that delivered on each requirement. Sources: 1,2
detailFragmentActive = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
setSupportActionBar(mToolbar);
...
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(detailFragmentActive) {
onBackPressed();
//if(displayBackAgain)
//return; //return after so you don't call syncState();
}else if (mDrawerLayou.isDrawerOpen(GravityCompat.START))
mDrawerLayout.closeDrawer(GravityCompat.START);
else
mDrawerLayout.openDrawer(GravityCompat.START);
mDrawerToggle.syncState();
}
});
}
private void animateHamburger(boolean isArrow){
int start = 0, end = 1;
if(isArrow){
detailFragmentActive = false;
start = 1; end = 0;
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
}else{
detailFragmentActive = true;
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
ValueAnimator anim = ValueAnimator.ofFloat(start, end);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float slideOffset = (Float) valueAnimator.getAnimatedValue();
mDrawerToggle.onDrawerSlide(mDrawerLayout, slideOffset);
}
});
anim.setInterpolator(new DecelerateInterpolator());
anim.setDuration(500);
anim.start();
}
#Override
public void onBackPressed() {
super.onBackPressed();
animateHamburger(true);
}
public void onFragmentChange(){
...
animateHamburger(false);
}
You can set listener for this button:
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (flagDeterminingAction) {
drawerLayout.openDrawer(drawerListView);
} else {
onBackPressed();
//or popbackstack or whatever you are using to going back in navigation
}
}
I've put following code into onDrawerSlide method of navigation drawer in order to move content when drawer is opening.
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, slideOffset);
container.setTranslationX(slideOffset * drawerView.getWidth());
mDrawerLayout.bringChildToFront(drawerView);
mDrawerLayout.requestLayout();
}
It works perfectly almost always, except of first app run - I open drawer programmatically to teach user using it.
So, when I'm calling
if (!mUserLearnedDrawer && !mFromSavedInstanceState) {
mDrawerLayout.openDrawer(mFragmentContainerView);
}
drawer is opening, but onDrawerSlide is not called.
I suppose, reason is that something does not have enough time to load, but I don't know what exactly and how escape this.
It works when I'm calling openDrawer in a few seconds after run (via Handler), but it looks terrible on UI.
PS. Tried to call openDrawer in onCreate, onResume, in calling Activity - still no result.
PPS. Also I'm unable to call onDrawerSlide directly - because I don't know slideOffset value.
I think when you open drawer programmatically to teach user using it, you need to call DrawerLayout.DrawerListener.onDrawerSlide(View drawerView, float slideOffset) with a Handler or an AsyncTask which will change in time the value of slideOffset which is in [0, 1].
Just like:
private Handler mUIHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case UPDATE_SLIDING_INDEX:
double slideOffSet= (double)msg.obj;
if (slideOffSet> 0.0 && slideOffSet<= 1.0) {
drawerListener.onDrawerSlide(drawer, slideOffSet);
Message message = mUIHandler.obtainMessage();
message.what = UPDATE_SLIDING_INDEX;
message.obj = slideOffSet- 0.1;
mUIHandler.sendMessageDelayed(message, 100);
} else {
//To do something here.
}
break;
default:
break;
}
}
};
Then you call like this:
Message message = mUIHandler.obtainMessage();
message.what = UPDATE_SLIDING_INDEX;
message.obj = 0.8;
mUIHandler.sendMessage(message);
when mDrawerLayout.setDrawerListener(....); is called before or after of the below if condition.
if (!mUserLearnedDrawer && !mFromSavedInstanceState) {
mDrawerLayout.openDrawer(mFragmentContainerView);
}
If it is after, obviously the onDrawerSlide won't be called.
If it is before then you should debug.
I have an activity with many fragments that uses action bar and navigation drawer. It has "home as up" enabled. I have implemented proper logic that only top level fragments show action bar drawer toggle icon, other fragments show up arrow. I achieved this by:
mDrawerToggle.setDrawerIndicatorEnabled(false);
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerList);
Now old v4 support library ActionBarDrawerToggle became deprecated. I've switched to v7 version together with new Toolbar to get Material Design look. After that when drawer is open "up" arrow is correctly displayed, but when the above-mentioned code is executed it disappears completely.
Is it a bug in support library or I have to do something different to show "up" arrow instead of drawer indicator?
Answer/comments of Nikola Despotoski and Andrey Novikov are perfectly correct but I want to mention that after toolbar was replaced with following code:
drawerToggle.setDrawerIndicatorEnabled(false);
drawerToggle.setHomeAsUpIndicator(getV7DrawerToggleDelegate().getThemeUpIndicator());
setSupportActionBar(toolbar);
your activity will receive every onOptionsItemsSelected events even if you enable your drawer toogle again drawerToggle.setDrawerIndicatorEnabled(true);
So you need to handle this, I've ended with
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (drawerToggle.isDrawerIndicatorEnabled()) {
return drawerToggle.onOptionsItemSelected(item);
} else {
onBackPressed();
return true;
}
default:
return super.onOptionsItemSelected(item);
}
}
Have you tried to get themed up indicator using getV7DrawerToggleDelegate().getThemeUpIndicator () and set it after you disable the indicator?
Because when the indicator is disabled ActionBarDrawerToggle tries to set the previous indicator.
From ActionBarDrawerToggle source:
public void setDrawerIndicatorEnabled(boolean enable) {
if (enable != mDrawerIndicatorEnabled) {
if (enable) {
setActionBarUpIndicator((Drawable) mSlider,
mDrawerLayout.isDrawerOpen(GravityCompat.START) ?
mCloseDrawerContentDescRes : mOpenDrawerContentDescRes);
} else {
setActionBarUpIndicator(mHomeAsUpIndicator, 0);
}
mDrawerIndicatorEnabled = enable;
}
}
Edit:
As of deprecation of ActionBarActivity, you should use getDrawerToggleDelegate().getThemeUpIndicator ()
If you use AppCompatActivity, you can get the right drawer icon and back icon by
if(homeUp)
{
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
mDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
}
else
{
mDrawerToggle.setDrawerIndicatorEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mDrawerToggle.syncState();
}
Without need for getV7DrawerToggleDelegate :D