首先从下面的链接下载源代码文件
抽屉布局。Java语言
和
ViewDrawerHelper。Javahtml" target="_blank">语言
然后将以上两个文件粘贴到你的应用程序util包中(或你想要的地方),并在你的活动中参考这个抽屉布局,而不是android。支持v4.widget。抽屉布局更改活动布局文件中的抽屉布局参考,
现在调整一下
private static final int MAX_SETTLE_DURATION = 600; // ms
对于ViewDrawerHelper,加速只需增加值,而下降只需减少值。
如果您想在操作栏切换按钮上添加操作,请从下面的链接下载以下源文件
ActionBarDrawerToggle。Java语言
JellyBeanMR2.java
切换oneycomb.java
并将上述文件粘贴到您的应用程序util包(或您想要的地方)注意:-确保每个新添加文件的导入包都指向应用程序项目中的文件,而不是android的文件。支持控件。*;
如果以上链接不起作用,请添加http://
加速动画并等待它完成的另一种选择是首先简单地避免动画:只需调用start Active()
而不调用CloseDrawer()
。虽然您没有看到抽屉关闭,但活动转换动画仍然提供了相当不错的效果,并且它立即发生,无需等待抽屉关闭动画先完成沉淀,没有波动性,并且感知延迟要短得多。
(如果您只想查看代码,可以跳过此解释。)
要实现这一点,您需要一种方法,在使用“后退”按钮导航回活动时,在没有任何关闭动画的情况下关闭抽屉。(不调用closeDrawer()将使该活动实例中的抽屉保持打开状态;一种相对浪费的解决方法是在返回时强制活动重新创建(),但不这样做也可以解决这个问题。)您还需要确保只有在导航后返回时才关闭抽屉,而不是在方向更改后,但这很容易。
虽然从onCreate()
调用closeDrawer()
将使抽屉在没有任何动画的情况下开始关闭,但从onResume()
调用时情况并非如此。从onResume()调用closeDrawer()将以用户瞬间可见的动画关闭抽屉<代码>抽屉布局(code>DrawerLayout)不提供任何方法来关闭没有该动画的抽屉,但可以添加一个。
正如@syesilova所指出的,关闭抽屉实际上只是把它从屏幕上滑下来。因此,通过将抽屉直接移动到其“关闭”位置,可以有效跳过动画。平移方向将根据重力而变化(无论是左抽屉还是右抽屉),准确位置取决于抽屉及其所有子抽屉的大小。
然而,仅仅移动它是不够的,因为DrawerLayout
在扩展的LayoutParams
中保留了一些内部状态,用于了解抽屉是否打开。如果您只是将抽屉移出屏幕,它将不知道它已关闭,这将导致其他问题。(例如,抽屉将在下一次方向更改时重新出现。)
由于您正在将支持库编译到应用程序中,因此可以在android中创建一个类。支持小部件包,以访问其默认(包私有)部分,或扩展抽屉布局,而无需复制其所需的任何其他类。这还将减少在将来更改支持库时更新代码的负担。(最好尽可能将代码与实现细节隔离开来。)您可以使用moveDrawerToOffset()移动抽屉,并设置布局参数,使其知道抽屉已关闭。
这是跳过动画的代码:
// 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
,则抽屉将在下一次方向更改时移回其打开位置。
这种方法向支持中添加了一个实用程序类。v4软件包,以获取我们在抽屉布局内需要的软件包私有部分。
将此类放入/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()方法在无动画的情况下关闭抽屉:
@Overrid6e
protected void onResume() {
super.onResume();
if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
Support4Widget.setDrawerClosed(mDrawerLayout, GravityCompat.START);
mCloseNavDrawer = false;
}
}
这种方法扩展了DrawerLayout以添加setDrawerCloked()方法。
将此类放入/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);
/**/
}
}
在活动布局中使用自定义抽屉布局,而不是使用抽屉布局:
<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()方法在无动画的情况下关闭抽屉:
@Overrid6e
protected void onResume() {
super.onResume();
if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.setDrawerClosed(GravityCompat.START);
mCloseNavDrawer = false;
}
}
我发现这是避免起伏的最好方法,没有任何长时间的感知延迟。
您几乎可以使用类似的技术来模拟在到达活动后关闭抽屉,方法是在意图中传递一个值,告诉新活动在没有动画的情况下从onCreate()
打开抽屉,然后在活动布局完成后为其关闭设置动画,但是在我的实验中,活动转换破坏了模拟的效果,所以你也需要禁用它。
您当然可以调整动画的持续时间,但这需要您从支持库中复制类,然后相应地编辑它们。
ViewDragHelper视图
持续时间在ViewDragHelper
中确定
然后,当ViewDragHelper时,应用于抽屉布局。调用SmoothSlideViewWTO
您需要创建ViewDragHelper的修改版本。forceSettleCapturedViewAt,在持续时间参数中传递。
forceSettleCapturedViewAt(... int duration)
然后创建您的ViewDragHelper版本。SmoothSlideViewWTO。
public boolean smoothSlideViewTo(... int duration) {
...
return forceSettleCapturedViewAt(... int duration);
}
抽屉布局
接下来,您需要修改抽屉布局。关闭抽屉和抽屉布局。衣柜以匹配新的ViewDragHelper修改。
ActionBarDrawerToggle
您还必须复制ActionBarDrawertogle
和ActionBarDrawertogleHoneycomb
。不过,这些文件不需要任何编辑。
我尝试在我的应用程序中实现导航抽屉(材料设计)。我的活动包含带有片段的FrameLayout。当用户在导航抽屉FrameLayout中选择项目时,重新加载新片段: 当我点击项目时,一切正常。导航抽屉关闭不顺利,但冻结(抽搐,抽搐),因为片段在后台重新加载。 如何顺利关闭导航抽屉?
我是android新手。我有一个关于Android导航抽屉的问题。我在我的应用程序中加入了导航抽屉,一切都很顺利,但我想知道是否有人能帮我在导航抽屉列表中获得惰性动画。 图像的来源。 非常感谢。
在我的片段中,我通过编程将片段添加到导航抽屉中,然后从右侧打开抽屉。注意,我无法直接访问抽屉布局的xml文件,因此需要一个编程解决方案。抽屉从右侧打开并正确关闭,我保留抽屉的打开/关闭状态,并在配置根据该状态更改后打开它。 问题是,在配置更改时,抽屉关闭(我可以看到抽屉动画到关闭状态),即使我根据保存的InstanceState布尔值恢复其打开/关闭状态。我在中的所有方法中放置了调试器,但它们没有
我的应用程序有一个导航抽屉。从抽屉选项,我正在打开不同的活动。此时,在新活动之前显示一个空白屏幕。
二等 三等
每当用户按下< code >底部导航栏中的一个按钮时,我都想关闭我的< code >抽屉小部件,但是我不太明白这一点。我现在设置BNB的方式是通过应用程序记住所有屏幕的当前状态(使用< code>IndexedStack),但我想在按下BNB按钮之前关闭任何屏幕中打开的抽屉。我的每个屏幕都有自己的抽屉和应用程序栏,所以我不能在BNB里面放一个抽屉(或者我可以,我可以用一个开关盒动态地改变它们,当一