2014-02-23 202 views
3

所以我實現了導航抽屜,它工作正常。我有用於下一個活動的操作欄項目。通過單擊該操作欄圖標輸入第二個活動時,如果導航抽屜已打開,則即使在用戶返回到第一個活動之後它仍保持打開狀態。我嘗試使用打開另一個活動後關閉導航抽屜

drawerLayout.closeDrawer(drawerListView); 

意圖被調用後,但會發生什麼是第二個活動開始動畫關閉第一個活動完成後。這會產生糟糕的用戶體驗,即使我不喜歡它。

因此,有什麼辦法可以在創建第二個活動後關閉抽屜?我的意思是從第二次活動的onCreate或某個地方?

+1

您可以在開始其他活動時關閉它。嘗試設置加速或在關閉動畫時忽略動畫。看到這個:http://stackoverflow.com/questions/19460683/speed-up-navigation-drawer-animation-speed-on-closing – Devrim

+0

謝謝,但這是否意味着沒有辦法關閉抽屜從另一個活動? –

+0

你可以,但你不應該。從另一個活動交互活動的組件並不是一個好的決定。在內存不足的情況下,如果您嘗試使用其組件執行某些操作,您的後臺活動將會被破壞,並且您將擁有NPE。因此,在相關活動中應該做些什麼。 – Devrim

回答

0

在調用意圖之前關閉抽屜。

+0

沒有。我試過了。在調用intent之後它比關閉更快一些,但仍然有一點遲鈍(?)。我認爲這會更好,爲什麼如果關閉不顯示給用戶。 –

0

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

後通過DrawerLayout.java源太久的樣子,我已經找到一種方法。運行此當用戶返回到第一個活動,關閉抽屜,而無需運行動畫:

View view = drawerLayout.getChildAt(drawerLayout.getChildCount() - 1); 
ViewTreeObserver vto = view.getViewTreeObserver(); 

vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 
       @Override 
       public boolean onPreDraw() { 
        final DrawerLayout.LayoutParams lp = new DrawerLayout.LayoutParams(view.getWidth(), view.getHeight()); 
        lp.gravity = Gravity.LEFT; 
        view.setLayoutParams(lp); 
        view.setLeft(-view.getMeasuredWidth()); 
        view.getViewTreeObserver().removeOnPreDrawListener(this); 
        return true; 
       } 
      }); 

說明

首先找到對應於導航視圖來看,這將是最後一個孩子的抽屜佈局。將左側位置設置爲減去其寬度(對於左側抽屜)。

正如Lorne Laliberte所提到的,您還需要將LayoutParams.knownOpen更改爲false才能正常工作,但無法訪問創建appcompat-v4的本地副本並對其進行編輯的這種情況 - 因爲這是私人領域。這是我的詭計進入的地方。在java中,默認布爾值設置爲false。使用舊的寬度和高度創建一個新的LayoutParams將導致一個與knownOpen設置爲false。然後我們可以將其設置爲覆蓋舊版本的LayoutParams的視圖。如果視圖尚未佈置,例如在旋轉屏幕後,這需要放入預渲染偵聽器中。

請問我是否有人無法使這項工作。

+0

好戲!但請注意,您不需要support-v4或appcompat的本地副本,只需在同一個包名稱空間中創建一個類即可。不需要複製文件。您只需要在'/ src/android/support/v4/widget /'中創建一個文件。 –

4

您可以使用在支持庫V24新DrawerLayout.closeDrawer(int/View, bool)方法立即關閉抽屜:

drawerLayout.closeDrawer(Gravity.LEFT, false); 

將在onResume,如果你想在抽屜動畫的項目點擊關閉,但關閉時你從另一個回到活動。