您可以几乎模拟关闭抽屉在活动抵达后,在意图传递一个值来告诉新活动,没有动画从onCreate()
打开它的抽屉,然后动画它关闭后,该活动的布局完成,但是在我的实验中,活动转换破坏了模拟的效果。
另一种方法是避免抽屉动画,只需拨打startActivity()
而不致电closeDrawer()
。活动过渡动画仍然提供了一个很好的效果,它会立即发生,而无需等待抽屉关闭动画完成第一沉淀,无抖动现象,没有长期的感知延迟。
但是,使用后退按钮导航回原始活动时,您将需要一种方法来关闭没有动画的抽屉。
详细
(你可以跳过这个解释,如果你只是想看看代码。)
为了使这项工作,你需要一种方法来关闭抽屉没有任何接近使用后退按钮导航回活动时的动画。 (不调用closeDrawer()
将离开抽屉在活动实例打开;相对浪费的解决办法是导航回来时,只是迫使活动recreate()
,但它可能解决这一点没有这样做。)您也需要让你只确定如果您在导航后返回,而不是在方向更改后关闭抽屉,但这很容易。
尽管从onCreate()
调用closeDrawer()
会使抽屉在没有任何动画的情况下开始关闭,但从onResume()
也不会发生这种情况。从onResume()
调用closeDrawer()
将关闭抽屉,并带有用户瞬间可见的动画。 DrawerLayout
没有提供任何关闭没有该动画的抽屉的方法,但添加一个并不困难。
关闭抽屉实际上只是把它从屏幕上滑下来。因此,您可以通过将抽屉直接移动到其“关闭”位置来有效地跳过动画。翻译方向将根据重力(无论是左或右抽屉)有所不同,一旦它奠定了其所有的孩子的确切位置取决于抽屉的大小。
但是,简单地移动它还不够,因为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目录/安卓/支持/ V4 /空间/:
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目录/安卓/支持/ V4 /空间/:
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;
}
}
您可以在开始其他活动时关闭它。尝试设置加速或在关闭动画时忽略动画。看到这个:http://stackoverflow.com/questions/19460683/speed-up-navigation-drawer-animation-speed-on-closing – Devrim
谢谢,但这是否意味着没有办法关闭抽屉从另一个活动? –
你可以,但你不应该。从另一个活动交互活动的组件并不是一个好的决定。在内存不足的情况下,如果您尝试使用其组件执行某些操作,您的后台活动将会被破坏,并且您将拥有NPE。因此,在相关活动中应该做些什么。 – Devrim