Accelerate the speed of the "Navigation Box" animation when closing?

It is implemented and works as expected, so there really is no code that is worth publishing here, just looking to find out if anyone has the experience of speeding up the time it takes for the box to open and close? For example, the YouTube app is much faster!

+16
android
Oct 18 '13 at 23:49 on
source share
6 answers

You can fine-tune the duration of the animation, but you will need to copy the classes from the support library, and then edit them accordingly.

ViewDragHelper

Duration is defined here in ViewDragHelper

Then applied to DrawerLayout when ViewDragHelper.smoothSlideViewTo is called

You will need to create a modified version of ViewDragHelper.forceSettleCapturedViewAt , which will take place with a duration parameter.

 forceSettleCapturedViewAt(... int duration) 

Then create your version of ViewDragHelper.smoothSlideViewTo .

 public boolean smoothSlideViewTo(... int duration) { ... return forceSettleCapturedViewAt(... int duration); } 

DrawerLayout

Then you will need to change DrawerLayout.closeDrawer and DrawerLayout.closeDrawers to suit your new ViewDragHelper .

ActionBarDrawerToggle

You will also have to copy ActionBarDrawerToggle and ActionBarDrawerToggleHoneycomb . However, these files do not require editing.

+23
Oct 19 '13 at 1:09 on
source share

An alternative to speeding up the animation and waiting for it to complete is a simple exception to the animation: just call startActivity() without calling closeDrawer() . Although you don’t see that the box is closed, the transition animation still provides a pretty nice effect, and this happens immediately, without having to wait until the animation closes the box to complete the setup first, without interruption and a shorter delay in perception.




More details

(You can skip this explanation if you just want to see the code.)

To do this, you will need a way to close the drawer without any close animation when you go to activity using the back button. (Without calling closeDrawer() , it will leave the box open in this instance of activity, with a relatively wasteful workaround, it will simply force the recreate() action when navigating backwards, but this can be solved without doing this.) You also need to make sure that you only close the drawer if you return after navigation, and not after changing orientation, but this is easy.

Although calling closeDrawer() from onCreate() will cause the box to close without any animation, the same does not apply to onResume() . Calling closeDrawer() from onResume() will close the box with the animation that instantly displays to the user. DrawerLayout provides no way to close a drawer without this animation, but you can add it.

As @syesilova notes, closing a drawer actually just shifts it off the screen. Thus, you can effectively skip the animation by moving the box directly to the "closed" position. The broadcast direction will change depending on gravity (whether it is the left or right drawer), and the exact position depends on the size of the drawer when it is laid out by all his children.

However, simply moving it is not enough, because DrawerLayout saves some internal state in the advanced LayoutParams , which it uses to find out if the box is open. If you simply move the box from the screen, it will not know that it is closed, and this will cause other problems. (For example, the box will reappear the next time the orientation changes.)

Since you are compiling the support library into your application, you can create a class in the android.support.v4.widget package to access its default parts (batch-private) or extend DrawerLayout without copying through any of the other classes in which it needs. It will also reduce the burden of updating code with future changes to the support library. (It is always best to isolate the code from the implementation details as much as possible.) You can use moveDrawerToOffset() to move the box and set LayoutParams so that it knows that the box is closed.




the code

This is the code that will 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 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 simply call moveDrawerToOffset() without changing LayoutParams , the box will return to the open position the next time you change the orientation.




Option 1 (use an existing DrawerLayout)

This approach adds a utility class to the support.v4 package to access the private parts of the package that we need inside DrawerLayout.

Put this class in / src / android / support / v 4 / 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 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 closed // and invalidates the view for us. drawerLayout.closeDrawer(drawerView); /**/ } } 

Set a boolean in your activity when you move, indicating that the box 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 close the box in onResume() without animation:

 @Overrid6e protected void onResume() { super.onResume(); if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) { Support4Widget.setDrawerClosed(mDrawerLayout, GravityCompat.START); mCloseNavDrawer = false; } } 



Option 2 (extends from DrawerLayout)

This approach extends DrawerLayout to add the setDrawerClosed () method.

Put this class in / src / android / support / v 4 / 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 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 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 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 closed // and invalidates the view for us. closeDrawer(drawerView); /**/ } } 

Use CustomDrawerLayout instead of DrawerLayout in your action 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 logical value in your activity when you move, indicating that the box 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 close the box in onResume() without animation:

 @Overrid6e protected void onResume() { super.onResume(); if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) { mDrawerLayout.setDrawerClosed(GravityCompat.START); mCloseNavDrawer = false; } } 

I found this to be the best way to avoid shaking without any long delayed delays.

You could use a similar technique to simulate closing a mailbox after activity has been reached, passing a value in the intention of telling new activity to open your mailbox without animation from onCreate() , and then revive it after the activity layout is complete, however, in my experiments, the activity transition destroyed the modeling effect , so you also need to disable it.

+7
Jul 15 '15 at 1:22
source share

First, the bottom download links sourcode files

DrawerLayout.java

AND

ViewDrawerHelper.java

and insert the above two files in your applications use the package (or wherever you want) and refer to this box layout in your activity, and not to android.support.v4.widget.DrawerLayout change your box layout pointer in the work layout file,

Now adjust

 private static final int MAX_SETTLE_DURATION = 600; // ms 

of ViewDrawerHelper, to speed it up just increase the value and down decrease the value.

If you want to add an action to the action bar toggle button, the links below load the source files

ActionBarDrawerToggle.java

ActionBarDrawerToggleJellybeanMR2.java

ActionBarDrawerToggleHoneycomb.java

and paste the above files into your applications using the package (or wherever you want). Note. - Make sure that the imported packages of each newly added file refer to the file that is in your application project, and not to android.support.v4.widget * ;.

If the links above do not work, add http: //

+1
Oct 03 '14 at 8:22
source share

If you want to forcibly immediately remove the left panel without any animation, you can simply set its value to x. When the box layout is opened, its x value of the left left panel becomes 0, and when closed it becomes -1 * (its width). Therefore, if you set x to -2 * width when you open it, the left panel disappears immediately. And of course, don't forget to set x to -1 * width after closing it. For example:

 DisplayMetrics metrics = new DisplayMetrics(); this.getWindowManager().getDefaultDisplay().getMetrics(metrics); //obtain left panel 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); } ...... }; 
+1
Feb 15 '15 at 13:05
source share

This does not allow you to change the animation speed, but if you only want to instantly close the drawer, you can use the new DrawerLayout.closeDrawer(int/View, bool) methods in the v24 support library:

 drawerLayout.closeDrawer(Gravity.LEFT, false); 
0
Jun 28 '16 at 1:36
source share

I believe the real point of your question is how can I make the animation smoother after I click on the menu in drawerlayout, which will start a new activity.
If this is the meaning of your question, here 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); }; 
0
Aug 03 '16 at 20:33
source share



All Articles