打开另一个活动后关闭导航抽屉

时间:2014-02-23 20:53:33

标签: android navigation-drawer

所以我已经实现了导航抽屉,它工作正常。我有一个动作栏项目,用于下一个活动。通过单击该操作栏图标进入第二个活动时,如果导航抽屉打开,则即使用户返回到第一个活动,它仍然保持打开状态。我尝试使用

drawerLayout.closeDrawer(drawerListView);
在调用intent之后

,但是在关闭第一个活动的动画完成后,第二个活动开始。这会造成糟糕的用户体验,甚至我也不喜欢它。

所以在创建第二个活动后我可以用任何方式关闭抽屉吗?我的意思是从第二个活动的onCreate或某个地方?

4 个答案:

答案 0 :(得分:6)

您可以使用支持库v24中的新drawerLayout.closeDrawer(Gravity.LEFT, false); 方法立即关闭抽屉:

onResume

如果您希望抽屉在项目点击时关闭动画,请放置在 $(function() { // ... var $body = $(document); $body.bind('scroll', function() { if ($body.scrollLeft() !== 0) { $body.scrollLeft(0); } }); // ... }); ,但是当您从另一个活动返回活动时,请关闭。

答案 1 :(得分: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;
                }
            });

<强>解释

首先找到与导航视图对应的视图,这将是drawerLayout的最后一个子节点。将左侧位置设置为减去其宽度(对于左侧抽屉)。

正如Lorne Laliberte所提到的,你还需要将LayoutParams.knownOpen更改为false才能使其工作,但是无法访问创建appcompat-v4的本地副本并对其进行编辑的缺点 - 因为这是私有的领域。这就是我的技巧所在。在java中,默认布尔值设置为false。使用旧的宽度和高度创建新的LayoutParams将导致将knownOpen设置为false。然后我们可以将其设置为覆盖旧LayoutParams的视图。如果视图尚未布局,例如在旋转屏幕之后,则需要将其放在预绘制侦听器内。

请问我是否有人在上班时遇到问题。

答案 2 :(得分:0)

在调用意图之前关闭抽屉。

答案 3 :(得分:0)

你可以几乎模拟在到达活动后关闭抽屉,方法是在意图中传递一个值,告诉新活动打开其抽屉而没有来自onCreate()的动画,然后在活动布局完成后将其关闭动画,但在我的实验中,活动转换破坏了模拟的效果。

另一种方法是避免抽屉动画,只需在不调用startActivity()的情况下拨打closeDrawer()即可。活动过渡动画仍然提供了很好的效果,它立即发生,无需等待抽屉关闭动画首先完成定位,没有紊乱,没有长时间的感知延迟。

但是,当使用后退按钮导航回原始活动时,您需要一种方法来关闭抽屉而不用动画。

详细

(如果您只想查看代码,可以跳过此解释。)

要完成这项工作,您需要一种在使用后退按钮导航回活动时关闭抽屉而无需任何关闭动画的方法。 (不调用closeDrawer()会使抽屉在该活动实例中保持打开状态;相对浪费的解决方法是在导航时强制将活动强制为recreate(),但如果没有,则可以解决此问题这样做。)你还需要确保只有在导航后返回时才关闭抽屉,而不是在方向改变后,但这很容易。

虽然从closeDrawer()调用onCreate()会使抽屉在没有任何动画的情况下关闭,但onResume()也是如此。从closeDrawer()调用onResume()将使用用户暂时可见的动画关闭抽屉。 DrawerLayout没有提供任何方法来关闭抽屉而没有动画,但添加动画并不困难。

关闭抽屉实际上只是将其从屏幕上滑下来。因此,您可以通过将抽屉直接移动到其关闭的&#34;来有效地跳过动画。位置。翻译方向将根据重力(无论是左侧还是右侧抽屉)而有所不同,确切位置取决于抽屉与其所有儿童一起布置后的尺寸。

然而,仅仅移动它是不够的,因为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 / 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 {

    /** @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 / 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 {

    /** @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;
    }
}