web-dev-qa-db-ja.com

ユーザーが戻るボタンを押したときにナビゲーションドロワーを非表示にする

Googleの公式開発者チュートリアル here に従ってナビゲーションドロワーを作成しました。

現時点では、ユーザーがネイティブの戻るボタンを使用する場合を除き、すべて正常に機能しますAndroidは(ホームおよび最近のアプリのボタンとともに)画面の下部に表示されます。このネイティブの戻るボタンを使用して戻ると、ナビゲーションドロワーはまだ開いたままになります。

私のコードは、ユーザーが引き出しでアイテムを選択する方法を除いて、公式チュートリアルとほぼ同じです。

   mDrawerList.setOnItemClickListener(new ListView.OnItemClickListener()
    {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id)
        {
            switch(position)
            {
                case 0:
                {
                    Intent intent = new Intent(MainActivity.this, NextActivity.class);
                    startActivity(intent);
                }
            }
        }
    });

ユーザーがネイティブの戻るボタンを使用して戻るときにナビゲーションドロワーを閉じるにはどうすればよいですか?アドバイスをお願いします。ありがとう!

22
pez

onBackPressed() をオーバーライドする必要があります。ドキュメントから:

ユーザーが戻るキーを押したことをアクティビティが検出したときに呼び出されます。デフォルトの実装は単に現在のアクティビティを終了しますが、これをオーバーライドして任意のことを行うことができます。

したがって、次のようなコードを使用できます。

@Override
public void onBackPressed() {
    if (this.drawerLayout.isDrawerOpen(GravityCompat.START)) {
        this.drawerLayout.closeDrawer(GravityCompat.START);
    } else {
        super.onBackPressed();
    }
}

開いている場合、このメソッドは閉じます。それ以外の場合は、デフォルトの動作に戻ります。

59
mt0s

アクティビティでonBackPressed()をオーバーライドし、ナビゲーションドロワーが開いている状態を確認する必要があります。開いている場合は閉じ、それ以外の場合は通常のバックプレス方式を実行します。ここに、いくつかのコードといくつかの擬似コードを組み合わせて、役立つものを示します。

@Override
public void onBackPressed(){
  if(drawer.isDrawerOpen()){ //replace this with actual function which returns if the drawer is open
   drawer.close();     // replace this with actual function which closes drawer
  }
  else{
   super.onBackPressed();
  }
}

擬似コードを置き換えるには、ドロワーのドキュメントをご覧ください。両方の方法が存在することを知っています。

10
qazimusab

更新:

サポートライブラリ24.0.0以降、これは回避策なしで可能です。 2つの新しい openDrawer および closeDrawer メソッド 追加済みDrawerLayoutに追加すると、ドロワーをアニメーションなしで開いたり閉じたりできます。

openDrawer(drawerView, false)closeDrawer(drawerView, false)を使用して、ドロワーを遅滞なく開閉できるようになりました。


startActivity()を呼び出さずにcloseDrawer()を呼び出すと、戻るボタンを使用して戻ると、アクティビティのそのインスタンスでドロワーが開いたままになります。 closeDrawer()を呼び出すときにstartActivity()を呼び出すと、使用する回避策に応じて、途切れたアニメーションから長い知覚遅延まで、いくつかの問題が発生します。したがって、最善のアプローチは、startActivity()を呼び出して、戻り時に引き出しを閉じることです。

これをうまく機能させるには、戻るボタンでアクティビティに戻るときに、アニメーションを閉じることなくドロワーを閉じる方法が必要です。 (比較的無駄な回避策は、戻ってナビゲートするときにアクティビティをrecreate()に強制することですが、それをせずにこれを解決することは可能です。)

また、向きの変更後ではなく、ナビゲーション後に戻る場合にのみドロワーを閉じるようにする必要がありますが、それは簡単です。


詳細

(コードを表示するだけの場合は、この説明をスキップできます。)

closeDrawer()からonCreate()を呼び出すと、ドロワーはアニメーションなしで閉じられますが、onResume()からも同じことが言えません。 closeDrawer()からonResume()を呼び出すと、ユーザーに瞬間的に見えるアニメーションでドロワーが閉じられます。 DrawerLayoutは、アニメーションなしでドロワーを閉じる方法を提供しませんが、ドロワーを追加するために拡張することは可能です。

引き出しを閉じると、実際には引き出しが画面から消えるだけなので、引き出しを「閉じた」位置に直接移動することで、アニメーションを効果的にスキップできます。平行移動の方向は重力(左または右の引き出し)に応じて異なり、正確な位置は、すべての子と一緒に配置された後の引き出しのサイズによって異なります。

ただし、DrawerLayoutは内部の状態を拡張LayoutParamsに保持し、引き出しが開いているかどうかを知るために使用するため、単に移動するだけでは十分ではありません。ドロワーを画面外に移動するだけでは、ドロワーが閉じていることがわかりません。これは他の問題の原因となります。 (たとえば、引き出しは次の向きの変更で再び表示されます。)

サポートライブラリをアプリにコンパイルしているため、_Android.support.v4.widget_パッケージにクラスを作成して、デフォルト(パッケージプライベート)部分にアクセスしたり、コピーせずにDrawerLayoutを拡張したりできます。必要な他のクラスの。これにより、サポートライブラリへの将来の変更でコードを更新する負担も軽減されます。 (可能な限り実装の詳細からコードを隔離することが常に最善です。)moveDrawerToOffset()を使用して引き出しを移動し、LayoutParamsを設定して、引き出しが閉じていることを知ることができます。 。


コード

これはアニメーションをスキップするコードです:

_        // move drawer directly to the closed position
        moveDrawerToOffset(drawerView, 0.f); 

        // set internal state so DrawerLayout knows it's closed
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        lp.onScreen = 0.f;
        lp.knownOpen = false;

        invalidate();
_

注:LayoutParamsを変更せずにmoveDrawerToOffset()を呼び出すと、ドロワーは開いた位置に戻ります次の方向変更時に。


オプション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); 

        // 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();
    }
}
_

移動するときにアクティビティにブール値を設定し、引き出しを閉じる必要があることを示します。

_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); 

        // set internal state so DrawerLayout knows it's closed
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        lp.onScreen = 0.f;
        lp.knownOpen = false;

        invalidate();
    }

    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); 

        // set internal state so DrawerLayout knows it's closed
        final LayoutParams lp = (LayoutParams) drawerView.getLayoutParams();
        lp.onScreen = 0.f;
        lp.knownOpen = false;

        invalidate();
    }
}
_

アクティビティレイアウトで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;
    }
}
_
4
Lorne Laliberte

ここにあなたの問題の代替ソリューションがあります。

@Override    
public void onBackPressed(){    
    if(drawerLayout.isDrawerOpen(navigationView)){    
        drawerLayout.closeDrawer(navigationView);    
    }else {    
        finish();    
    }    
}    
3

@James Crossが提供する回答の実装を使用しても機能しましたが、ドロワーを閉じるアニメーションは望ましくなく、あまり手間がかからず修正できませんでした example

_@Override
public void onResume()
{
    super.onResume();
    mDrawerLayout.closeDrawers();
}
_

回避策は、デバイスの戻るボタンが押されたときにアクティビティを再開することです。それは私には理想的とは思えませんが、機能します。 @ mt0sおよび@Qazi Ahmedが示唆するonBackPressed()をオーバーライドし、呼び出しアクティビティを決定するために追加を渡す:

_    mDrawerList.setOnItemClickListener(new ListView.OnItemClickListener()
    {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id)
        {
            switch(position)
            {
                case 0:
                {
                    Intent intent = new Intent(MainActivity.this, NextActivity.class);
                    //pass int extra to determine calling activity
                    intent.putExtra(EXTRA_CALLING_ACTIVITY, CallingActivityInterface.MAIN_ACTIVITY);
                    startActivity(intent);
                }
            }
        }
    });
_

_NextActivity.class_で、呼び出しアクティビティを確認します。

_@Override
public void onBackPressed()
{
    int callingActivity = getIntent().getIntExtra(EXTRA_CALLING_ACTIVITY, CallingActivityInterface.MAIN_ACTIVITY);
    switch(callingActivity)
    {
        case CallingActivityInterface.MAIN_ACTIVITY:
        {
            Intent intent = new Intent(this, MainActivity.class);
            startActivity(intent);
            finish();
        }
        ...
    }
}
_

このように、上ボタンまたは戻るボタンのどちらを使用するかに関係なく、MainActivityに戻ると、ドロワーはアニメーションなしで閉じられます。これを行うにはおそらくもっと良い方法があります。私のアプリは現時点では比較的単純であり、これは機能しますが、誰かが持っている場合、より効果的な方法を待っています。

3
pez

おそらく、アクティビティが開かれたときにナビゲーション描画が常に閉じられるようにしたいと思うでしょう。これを使用してそれを行います。

@Override
public void onResume(){
    mDrawerList.closeDrawer(Gravity.LEFT);
}
1
James Cross

なぜ面倒なのですか?引き出しアイテムをクリックしたときに引き出しを閉じるだけです。これが公式のGoogle Playアプリでのやり方です。

private class DrawerItemClickListener implements ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         drawerLayout.closeDrawer(GravityCompat.START, false);
         selectItem(position); 
    }
}
1
Damnum

簡単なサンプル:

Drawer resultDrawer;

public void onBackPressed(){
    if (this.resultDrawer.isDrawerOpen()) {    
        this.resultDrawer.closeDrawer();    
    } else {    
        super.onBackPressed();    
    }
}
0
Chirag Patel