您可以幾乎模擬關閉抽屜在活動抵達後,在意圖傳遞一個值來告訴新活動,沒有動畫從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