你可以通过在意图中传递一个值来告诉新活动在 onCreate()
中不使用动画打开抽屉,然后在活动布局完成后将其关闭以模拟到达活动后关闭抽屉的效果,但是在我的实验中,活动转换破坏了模拟效果。
另一种方法是避免抽屉动画,只需调用 startActivity()
而不调用 closeDrawer()
。活动转换动画仍然提供了很好的效果,并且它立即发生,无需等待抽屉关闭动画先完成,没有卡顿,没有长时间的感知延迟。
但是,当使用返回按钮导航回原始活动时,您需要一种关闭抽屉而不使用动画的方法。
详细信息
(如果您只想看代码,请跳过此解释。)
为使此功能正常工作,您需要一种方法在使用返回按钮导航回活动时关闭抽屉而不进行任何关闭动画。(不调用closeDrawer()
将使抽屉在该活动实例中保持打开状态;一个相对低效的解决方法是强制活动在导航回来时重新创建()
,但可以不这样做解决这个问题。)您还需要确保仅在导航后关闭抽屉,而不是在方向更改后关闭抽屉,但这很容易。
尽管从onCreate()
调用closeDrawer()
会使抽屉默认关闭而没有任何动画,但从onResume()
调用它则不然。从onResume()
调用closeDrawer()
会以短暂可见的动画关闭抽屉。DrawerLayout
并未提供任何方法来关闭抽屉而没有该动画,但添加一个不困难。
关闭抽屉实际上只是将其从屏幕上滑出。因此,您可以通过直接将抽屉移动到其“关闭”位置来有效地跳过动画。翻译方向将根据重力而变化(无论是左侧还是右侧抽屉),确切的位置取决于抽屉在布局中与所有子项一起放置后的大小。
然而,仅仅移动它还不够,因为
DrawerLayout
在扩展的
LayoutParams
中保留了一些内部状态,用于知道抽屉是否打开。如果您只是将抽屉移出屏幕,它就不会知道它已关闭,这会导致其他问题。(例如,在下一个方向更改时,抽屉将重新出现。)
由于您正在将支持库编译到应用程序中,因此可以在android.support.v4.widget
包中创建一个类以访问其默认(包私有)部分,或者扩展DrawerLayout
而不复制它需要的任何其他类。这也将减轻更新支持库时代码的负担。(尽可能地隔离代码与实现细节总是最好的。)您可以使用moveDrawerToOffset()
来移动抽屉,并设置LayoutParams
,以便它知道抽屉已关闭。
代码
这是跳过动画的代码:
moveDrawerToOffset(drawerView, 0.f);
closeDrawer(drawerView);
注意:如果您只调用moveDrawerToOffset()
而不更改LayoutParams
,则抽屉将在下一次方向更改时移回其打开位置。
选项1(使用现有的DrawerLayout)
这种方法添加了一个实用类到support.v4包中,以获得访问DrawerLayout内部需要的package-private部分。
将此类放入/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 {
@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));
}
drawerLayout.moveDrawerToOffset(drawerView, 0.f);
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);
}
...并在onResume()
中使用setDrawerClosed()
方法以无动画方式关闭抽屉:
@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 {
@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");
}
moveDrawerToOffset(drawerView, 0.f);
closeDrawer(drawerView);
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);
}
...并在onResume()
中使用setDrawerClosed()
方法关闭抽屉,无需动画:
@Overrid6e
protected void onResume() {
super.onResume();
if(mCloseNavDrawer && mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
mDrawerLayout.setDrawerClosed(GravityCompat.START);
mCloseNavDrawer = false;
}
}