Implemented and working as expect, as such there really is no code that is worth posting here, simply looking to find out if anyone has experience with speeding up the time it takes the drawer to open and close? The YouTube app for instance is much faster!
You can definitely adjust the duration of the animation, but it will require you to copy over the classes from the support library, then edit them accordingly.
ViewDragHelper
The duration is determined here in ViewDragHelper
Then is applied to the DrawerLayout
when ViewDragHelper.smoothSlideViewTo
is called
You'll need to create a modified version of ViewDragHelper.forceSettleCapturedViewAt
that passes in a duration param.
forceSettleCapturedViewAt(... int duration)
Then create your version of ViewDragHelper.smoothSlideViewTo
.
public boolean smoothSlideViewTo(... int duration) {
...
return forceSettleCapturedViewAt(... int duration);
}
DrawerLayout
Next you'll need to modify DrawerLayout.closeDrawer
and DrawerLayout.closeDrawers
to match your new ViewDragHelper
modifications.
ActionBarDrawerToggle
You'll also have to copy over ActionBarDrawerToggle
and ActionBarDrawerToggleHoneycomb
. These files won't require any editing though.
An alternative to speeding up the animation and waiting for it to finish is to simply avoid the animation in the first place: just call startActivity()
without calling closeDrawer()
. Although you don't see the drawer close, the activity transition animation still provides a pretty nice effect, and it occurs immediately, with no need to wait for the drawer close animation to finish settling first, no choppiness, and a much shorter perceptual delay.
Details
(You can skip past this explanation if you just want to see the code.)
To make this work you need a way to close the drawer without any close animation when navigating back to the activity with the back button. (Not calling closeDrawer()
will leave the drawer open in that activity instance; a relatively wasteful workaround would be to just force the activity to recreate()
when navigating back, but it's possible to solve this without doing that.) You also need to make sure you only close the drawer if you're returning after navigating, and not after an orientation change, but that's easy.
Although calling closeDrawer()
from onCreate()
will make the drawer start out closed without any animation, the same is not true from onResume()
. Calling closeDrawer()
from onResume()
will close the drawer with an animation that is momentarily visible to the user. DrawerLayout
doesn't provide any method to close the drawer without that animation, but it's possible to add one.
As @syesilova points out, closing the drawer actually just slides it off the screen. So you can effectively skip the animation by moving the drawer directly to its "closed" position. The translation direction will vary according to the gravity (whether it's a left or right drawer), and the exact position depends on the size of the drawer once it's laid out with all its children.
However, simply moving it isn't quite enough, as DrawerLayout
keeps some internal state in extended LayoutParams
that it uses to know whether the drawer is open. If you just move the drawer off screen, it won't know that it's closed, and that will cause other problems. (For example, the drawer will reappear on the next orientation change.)
Since you're compiling the support library into your app, you can create a class in the android.support.v4.widget
package to gain access to its default (package-private) parts, or extend DrawerLayout
without copying over any of the other classes it needs. This will also reduce the burden of updating your code with future changes to the support library. (It's always best to insulate your code from implementation details as much as possible.) You can use moveDrawerToOffset()
to move the drawer, and set the LayoutParams
so it will know that the drawer is closed.
Code
This is the code that'll skip the animation:
// move drawer directly to the closed position
moveDrawerToOffset(drawerView, 0.f);
/* EDIT: as of v23.2.1 this direct approach no longer works
because the LayoutParam fields have been made private...
// set internal state so DrawerLayout knows it's closed
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
lp.onScreen = 0.f;
lp.knownOpen = false;
invalidate();
/*/
// ...however, calling closeDrawer will set those LayoutParams
// and invalidate the view.
closeDrawer(drawerView);
/**/
Note: if you just call moveDrawerToOffset()
without changing the LayoutParams
, the drawer will move back to its open position on the next orientation change.
Option 1 (use existing DrawerLayout)
This approach adds a utility class to the support.v4 package to gain access to the package-private parts we need inside DrawerLayout.
Place this class into /src/android/support/v4/widget/:
package android.support.v4.widget;
import android.support.annotation.IntDef;
import android.support.v4.view.GravityCompat;
import android.view.Gravity;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class Support4Widget {
/** @hide */
@IntDef({Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END})
@Retention(RetentionPolicy.SOURCE)
private @interface EdgeGravity {}
public static void setDrawerClosed(DrawerLayout drawerLayout, @EdgeGravity int gravity) {
final View drawerView = drawerLayout.findDrawerWithGravity(gravity);
if (drawerView == null) {
throw new IllegalArgumentException("No drawer view found with gravity " +
DrawerLayout.gravityToString(gravity));
}
// move drawer directly to the closed position
drawerLayout.moveDrawerToOffset(drawerView, 0.f);
/* EDIT: as of v23.2.1 this no longer works because the
LayoutParam fields have been made private, but
calling closeDrawer will achieve the same result.
// set internal state so DrawerLayout knows it's closed
final DrawerLayout.LayoutParams lp = (DrawerLayout.LayoutParams) drawerView.getLayoutParams();
lp.onScreen = 0.f;
lp.knownOpen = false;
drawerLayout.invalidate();
/*/
// Calling closeDrawer updates the internal state so DrawerLayout knows it's closed
// and invalidates the view for us.
drawerLayout.closeDrawer(drawerView);
/**/
}
}
Set a boolean in your activity when you navigate away, indicating the drawer should be closed:
public static final String CLOSE_NAV_DRAWER = "CLOSE_NAV_DRAWER";
private boolean mCloseNavDrawer;
@Override
public void onCreate(Bundle savedInstanceState) {
// ...
if (savedInstanceState != null) {
mCloseNavDrawer = savedInstanceState.getBoolean(CLOSE_NAV_DRAWER);
}
}
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
// ...
startActivity(intent);
mCloseNavDrawer = true;
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean(CLOSE_NAV_DRAWER, mCloseNavDrawer);
super.onSaveInstanceState(savedInstanceState);
}
...and use the setDrawerClosed()
method to shut the drawer in onResume()
with no animation:
@Overrid6e
protected void onResume() {
super.onResume();
if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
Support4Widget.setDrawerClosed(mDrawerLayout, GravityCompat.START);
mCloseNavDrawer = false;
}
}
Option 2 (extend from DrawerLayout)
This approach extends DrawerLayout to add a setDrawerClosed() method.
Place this class into /src/android/support/v4/widget/:
package android.support.v4.widget;
import android.content.Context;
import android.support.annotation.IntDef;
import android.support.v4.view.GravityCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class CustomDrawerLayout extends DrawerLayout {
/** @hide */
@IntDef({Gravity.LEFT, Gravity.RIGHT, GravityCompat.START, GravityCompat.END})
@Retention(RetentionPolicy.SOURCE)
private @interface EdgeGravity {}
public CustomDrawerLayout(Context context) {
super(context);
}
public CustomDrawerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setDrawerClosed(View drawerView) {
if (!isDrawerView(drawerView)) {
throw new IllegalArgumentException("View " + drawerView + " is not a sliding drawer");
}
// move drawer directly to the closed position
moveDrawerToOffset(drawerView, 0.f);
/* EDIT: as of v23.2.1 this no longer works because the
LayoutParam fields have been made private, but
calling closeDrawer will achieve the same result.
// set internal state so DrawerLayout knows it's closed
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
lp.onScreen = 0.f;
lp.knownOpen = false;
invalidate();
/*/
// Calling closeDrawer updates the internal state so DrawerLayout knows it's closed
// and invalidates the view for us.
closeDrawer(drawerView);
/**/
}
public void setDrawerClosed(@EdgeGravity int gravity) {
final View drawerView = findDrawerWithGravity(gravity);
if (drawerView == null) {
throw new IllegalArgumentException("No drawer view found with gravity " +
gravityToString(gravity));
}
// move drawer directly to the closed position
moveDrawerToOffset(drawerView, 0.f);
/* EDIT: as of v23.2.1 this no longer works because the
LayoutParam fields have been made private, but
calling closeDrawer will achieve the same result.
// set internal state so DrawerLayout knows it's closed
final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
lp.onScreen = 0.f;
lp.knownOpen = false;
invalidate();
/*/
// Calling closeDrawer updates the internal state so DrawerLayout knows it's closed
// and invalidates the view for us.
closeDrawer(drawerView);
/**/
}
}
Use CustomDrawerLayout
instead of DrawerLayout
in your activity layouts:
<android.support.v4.widget.CustomDrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
>
...and set a boolean in your activity when you navigate away, indicating the drawer should be closed:
public static final String CLOSE_NAV_DRAWER = "CLOSE_NAV_DRAWER";
private boolean mCloseNavDrawer;
@Override
public void onCreate(Bundle savedInstanceState) {
// ...
if (savedInstanceState != null) {
mCloseNavDrawer = savedInstanceState.getBoolean(CLOSE_NAV_DRAWER);
}
}
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
// ...
startActivity(intent);
mCloseNavDrawer = true;
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean(CLOSE_NAV_DRAWER, mCloseNavDrawer);
super.onSaveInstanceState(savedInstanceState);
}
...and use the setDrawerClosed()
method to shut the drawer in onResume()
with no animation:
@Overrid6e
protected void onResume() {
super.onResume();
if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.setDrawerClosed(GravityCompat.START);
mCloseNavDrawer = false;
}
}
I found this the best way to avoid the choppiness without any long perceptual delays.
You could almost use a similar technique to simulate closing the drawer after arriving at an activity, by passing a value in the intent to tell the new activity to open its drawer with no animation from onCreate()
and then animate it closed after the activity's layout is finished, however in my experiments the activity transition ruined the effect of the simulation, so you'd also need to disable that.
First from below links download the sourcode files
And
and paste the above two files in your applications util package (or where you want) and take reference of this drawer layout in your activity not of android.support.v4.widget.DrawerLayout change your drawer layout reference in activity's layout file,
Now adjust
private static final int MAX_SETTLE_DURATION = 600; // ms
of ViewDrawerHelper, to speed up just increase the value and to down just decrease the value.
If you want to add action on action bar toggle button then from below links download the source files of
ActionBarDrawerToggleJellybeanMR2.java
ActionBarDrawerToggleHoneycomb.java
and paste the above files in your applications util package (or where you want) Note:- Make sure the imported packages of each newly added file refers to the file which is in your application project, not of android.support.v4.widget.*;
if the above links are not working please add http://
This does not allow you to change the animation speed, but if all you want is to instantly close a drawer you can use the new DrawerLayout.closeDrawer(int/View, bool)
methods in v24 of the support library:
drawerLayout.closeDrawer(Gravity.LEFT, false);
If you want to force immediatelly disappear the left panel without any animation, you can simply set its x value. When drawer layout opened, its left panel's x value becomes 0, and when closed becomes -1*(its width). So if you set x value -2*width while it is opened, left panel immediatelly disappears. And of course, don't forget to set x to -1*width after it is closed. For Example:
DisplayMetrics metrics = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(metrics);
//obtain left panel's width in px
private float mToggleStartX=-260*metrics.density; //260 is the width of left panel in dpi
//while drawer layout is opened, to disappear left panel
ll_drawerLayoutMenuPanel.setX(mToggleStartX*2); //ll_drawerLayoutMenuPanel is the left panel layout
mDrawerLayout.closeDrawers();
//don't forget reset x value in the onDrawerClosed method.
mDrawerToggle = new ActionBarDrawerToggle(this,mDrawerLayout,mainToolBar,R.string.drawer_open,R.string.drawer_close) {
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
ll_drawerLayoutMenuPanel.setX(mToggleStartX);
}
......
};
I believe that the real meaning of your question is how can I make animation run more smoothly after I click menu in drawerlayout that starts the new activity.
If this is the meaning of your question, this is how I do it.
mLeftDrawer.ItemClick += delegate (object sender, Android.Widget.AdapterView.ItemClickEventArgs e)
{
// Mark that item is selected and ask redraw
e.View.Selected = true;
adapter.NotifyDataSetChanged();
var handler = new Handler();
handler.PostDelayed(new Java.Lang.Runnable(() =>
{
_mLeftDrawerLayout.CloseDrawers();
// Here you should call your activity
}), 100);
};
Close drawer after some delay as per following
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
yourFunction();
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
drawer.closeDrawer(GravityCompat.START);
}
}, 100);
return true;
}
© 2022 - 2024 — McMap. All rights reserved.