2014-02-23 204 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,如果你想在抽屉动画的项目点击关闭,但关闭时你从另一个回到活动。