17

实现并按预期工作,因此确实没有值得在这里发布的代码,只是想看看是否有人有加快抽屉打开和关闭时间的经验?例如,YouTube 应用程序要快得多!

4

7 回答 7

24

你当然可以调整动画的持续时间,但它需要你从支持库中复制类,然后相应地编辑它们。

查看DragHelper

持续时间在此处确定ViewDragHelper

然后应用于DrawerLayout何时ViewDragHelper.smoothSlideViewTo被调用

您需要ViewDragHelper.forceSettleCapturedViewAt在持续时间参数中创建该传递的修改版本。

forceSettleCapturedViewAt(... int duration)

然后创建您的ViewDragHelper.smoothSlideViewTo.

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

抽屉布局

接下来,您需要修改DrawerLayout.closeDrawerDrawerLayout.closeDrawers匹配您的新ViewDragHelper修改。

ActionBarDrawer 切换

您还必须复制ActionBarDrawerToggleActionBarDrawerToggleHoneycomb。这些文件不需要任何编辑。

于 2013-10-19T01:09:18.560 回答
7

加速动画并等待它完成的另一种方法是首先简单地避免动画:只调用startActivity()而不调用closeDrawer(). 虽然你没有看到抽屉关闭,但活动过渡动画仍然提供了相当不错的效果,并且它立即发生,无需等待抽屉关闭动画先完成,没有波动,并且感知延迟更短.


细节

(如果您只想查看代码,可以跳过此说明。)

要完成这项工作,您需要一种在使用后退按钮导航回活动时关闭抽屉而不使用任何关闭动画的方法。(不调用closeDrawer()将使该活动实例中的抽屉保持打开状态;一个相对浪费的解决方法是在recreate()返回时强制活动,但不这样做也可以解决这个问题。)您还需要确保只关闭如果您在导航后返回抽屉,而不是在方向更改后返回,但这很容易。

虽然调用closeDrawer()fromonCreate()会使抽屉在没有任何动画的情况下开始关闭,但 from 并非如此onResume()。调用closeDrawer()fromonResume()将使用用户暂时可见的动画关闭抽屉。DrawerLayout没有提供任何方法来关闭没有该动画的抽屉,但可以添加一个。

正如@syesilova 指出的那样,关闭抽屉实际上只是将其滑出屏幕。因此,您可以通过将抽屉直接移动到其“关闭”位置来有效地跳过动画。平移方向将根据重力(无论是左抽屉还是右抽屉)而变化,确切位置取决于抽屉与所有子项一起布局后的大小。

然而,仅仅移动它是不够的,因为它DrawerLayout会保留一些内部状态,LayoutParams用于知道抽屉是否打开。如果您只是将抽屉移出屏幕,它不会知道它已关闭,这会导致其他问题。(例如,抽屉将在下一次方向更改时重新出现。)

由于您将支持库编译到您的应用程序中,因此您可以在android.support.v4.widget包中创建一个类以访问其默认(包私有)部分,或者DrawerLayout在不复制它需要的任何其他类的情况下进行扩展。这也将减少未来对支持库的更改来更新代码的负担。(始终最好将您的代码与实现细节尽可能地隔离开。)您可以使用moveDrawerToOffset()移动抽屉,并设置LayoutParams以便它知道抽屉已关闭。


代码

这是将跳过动画的代码:

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

注意:如果您只是调用moveDrawerToOffset()而不更改LayoutParams,则抽屉将在下一次方向更改时移回其打开位置。


选项 1(使用现有的 DrawerLayout)

这种方法向 support.v4 包添加了一个实用程序类,以访问我们在 DrawerLayout 中需要的包私有部分。

将此类放入 /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);
        /**/
    }
}

当您离开时在您的活动中设置一个布尔值,指示抽屉应该关闭:

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

...并使用该setDrawerClosed()方法在onResume()没有动画的情况下关闭抽屉:

@Overrid6e
protected void onResume() {
    super.onResume();

    if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        Support4Widget.setDrawerClosed(mDrawerLayout, GravityCompat.START);
        mCloseNavDrawer = false;
    }
}

选项 2(从 DrawerLayout 扩展)

这种方法扩展了 DrawerLayout 以添加一个 setDrawerClosed() 方法。

将此类放入 /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);
        /**/
    }
}

在您的活动布局中使用CustomDrawerLayout而不是:DrawerLayout

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

...并在您离开时在您的活动中设置一个布尔值,指示抽屉应该关闭:

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

...并使用该setDrawerClosed()方法在onResume()没有动画的情况下关闭抽屉:

@Overrid6e
protected void onResume() {
    super.onResume();

    if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
        mDrawerLayout.setDrawerClosed(GravityCompat.START);
        mCloseNavDrawer = false;
    }
}

我发现这是在没有任何长时间感知延迟的情况下避免波动的最佳方法。

几乎可以使用类似的技术来模拟到达活动后关闭抽屉,通过在意图中传递一个值来告诉新活动打开它的抽屉而不使用动画onCreate(),然后在活动的布局完成后动画它关闭,但是在我的实验中,活动转换破坏了模拟的效果,因此您还需要禁用它。

于 2015-07-15T01:22:27.503 回答
2

首先从下面的链接下载源代码文件

抽屉布局.java

ViewDrawerHelper.java

并将上述两个文件粘贴到您的应用程序 util 包(或您想要的位置)中,并在您的活动中引用此抽屉布局,而不是 android.support.v4.widget.DrawerLayout 在活动的布局文件中更改您的抽屉布局参考,

现在调整

private static final int MAX_SETTLE_DURATION = 600; // ms

ViewDrawerHelper的,加速只是增加值,向下只是减少值。

如果要在操作栏切换按钮上添加操作,请从下面的链接下载源文件

ActionBarDrawerToggle.java

ActionBarDrawerToggleJellybeanMR2.java

ActionBarDrawerToggleHoneycomb.java

并将上述文件粘贴到您的应用程序 util 包中(或您想要的位置) 注意:- 确保每个新添加的文件的导入包引用的是您的应用程序项目中的文件,而不是 android.support.v4.widget 中的文件。 *;

如果以上链接无效,请添加 http://

于 2014-10-03T08:22:22.557 回答
2

这不允许您更改动画速度,但如果您只想立即关闭抽屉,您可以使用DrawerLayout.closeDrawer(int/View, bool)支持库 v24 中的新方法:

drawerLayout.closeDrawer(Gravity.LEFT, false);
于 2016-06-28T01:36:42.090 回答
1

如果你想在没有任何动画的情况下强制立即消失左侧面板,你可以简单地设置它的 x 值。抽屉布局打开时,其左侧面板的 x 值变为 0,关闭时变为 -1*(其宽度)。因此,如果您在打开时设置 x 值 -2*width,左侧面板会立即消失。当然,不要忘记在关闭后将 x 设置为 -1*width。例如:

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);
    }
    ......
};
于 2015-02-15T13:05:59.117 回答
0

我相信您问题的真正含义是,在单击开始新活动的抽屉布局中的菜单后,如何使动画运行更顺畅。
如果这是你问题的意思,我就是这样做的。

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

        };
于 2016-08-03T20:33:24.720 回答
-1

延迟一段时间后关闭抽屉,如下所示

@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;
}
于 2018-02-09T13:47:05.043 回答