フラグメントを使用する場合のAndroid Navigation Drawer画像とアップキャレットの切り替え


177

Navigation Drawerを使用する場合、Android開発者は、ActionBarで「Navigation Drawerで表される画面のみに実際にNavigation Drawer画像を表示する」ことと、「他のすべての画面に従来のアップカラットを設定する」ことを推奨しています。

詳細はこちらをご覧ください:http : //youtu.be/F5COhlbpIbY

1つのアクティビティを使用して複数のレベルのフラグメントを制御し、ナビゲーションドロワーの画像を取得してすべてのレベルで機能させることができます。

下位レベルのフラグメントを作成するときに、ActionBarDrawerToggle setDrawerIndicatorEnabled(false)を呼び出してナビゲーションドロワーイメージを非表示にし、上向きキャレットを表示することができます

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, 
lowFrag, "lowerFrag").addToBackStack(null).commit();

私が抱えている問題は、元のナビゲーションドロワーイメージの代わりに、アップカラットが表示している最上位のフラグメントに戻ってナビゲートするときです。ナビゲーションドロワーの画像を再表示するために、トップレベルのフラグメントのActionBarを「更新」する方法に関する提案はありますか?


解決

トムの提案は私のために働いた。これが私がしたことです:

主な活動

このアクティビティは、アプリ内のすべてのフラグメントを制御します。

新しいフラグメントを準備して他のフラグメントを置き換えるとき、DrawerToggleを次のsetDrawerIndicatorEnabled(false)ように設定します。

LowerLevelFragment lowFrag = new LowerLevelFragment();

//disable the toggle menu and show up carat
theDrawerToggle.setDrawerIndicatorEnabled(false);
getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout,   
lowFrag).addToBackStack(null).commit();

次に、のオーバーライドでonBackPressed、DrawerToggleを次のsetDrawerIndicatorEnabled(true)ように設定して上記を元に戻しました。

@Override
public void onBackPressed() {
    super.onBackPressed();
    // turn on the Navigation Drawer image; 
    // this is called in the LowerLevelFragments
    setDrawerIndicatorEnabled(true)
}

LowerLevelFragments内

フラグメントで私は次のように変更onCreateonOptionsItemSelectedました:

中には、onCreate追加setHasOptionsMenu(true)オプションメニューの設定を有効にします。また、アクションバーで<setDisplayHomeAsUpEnabled(true)を有効にするように設定します。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // needed to indicate that the fragment would 
    // like to add items to the Options Menu        
    setHasOptionsMenu(true);    
    // update the actionbar to show the up carat/affordance 
    getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}

次にonOptionsItemSelected<が押されるたびに、onBackPressed()アクティビティからを呼び出して、階層の1つ上のレベルに移動し、ナビゲーションドロワーイメージを表示します。

@Override
public boolean onOptionsItemSelected(MenuItem item) {   
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            getActivity().onBackPressed();
            return true;
         
    }

2
また、onBackPressed()メソッドでは、getFragmentManager()。getBackStackEntryCount()メソッドを使用してバックスタックのエントリ数を確認し、結果が0の場合にのみドロワーインジケーターを有効にすることができます。その場合、各LowerLevelFragmentsでhomeAsUpIndicatorを有効にする必要はありません。
pvshnik 2013

2
これはとても便利です!投稿の「ソリューション」部分を移動して、実際の「回答」にする必要があります。あなたは賛成投票でより多くのポイントを獲得し、それ結局のところ答えです
Oleksiy

なぜ、あなたはここでフラグメントを交換します:.replace(R.id.frag_layout。これがもう1つの階層レベルである場合.addは、バックスタックにそれを期待します。
JJD 2014年

5
Bro、theDrawerToggle.setDrawerIndicatorEnabled(false);フラグメントの内部をどのように参照しますか?Main Activityクラスファイルで宣言されていると思います。これを参照する方法が見つかりません。ヒントはありますか?
Skynet 2014年

1
ツールバーを使用しているときは、表示オプションを切り替えて、当面の間はホームを使用しないようにする必要がありました。そうしないとsetDisplayOptions()ToolbarWidgetWrapper(内部android.support.v7.internal.widgetパッケージの)内のメソッドは、同じフラグメントを2回目に入力したときにアイコンを再作成しません。他の人もこの問題に遭遇したときのために、これをここに残してください。
Wolfram Rittmeyer 2015

回答:


29

新しいアクティビティで下位レベルのフラグメントを実装するのではなく、下位レベルのフラグメントを実装するために既存のフラグメントを置き換えることを書いています。

その後、手動で戻る機能を実装する必要があると思います。ユーザーが押し戻したときに、スタックをポップするコードがあります(たとえば、Activity::onBackPressedオーバーライドで)。そのため、どこにいても逆にすることができますsetDrawerIndicatorEnabled


トムさん、ありがとうございました。使用したソリューションで元の投稿を更新しました。
EvilAsh 2013年

6
下位レベルのフラグメントでtheDrawerToggleを参照する方法は?これはメインアクティビティで定義されています。これに頭を悩ませることはできません。
Skynet、2014年

1
フラグメントからアクティビティを取得できます。したがって、メインのアクティビティにゲッターを追加して、ドロワーのトグルにアクセスします。
Raphael Royer-Rivard

83

1-2-3と簡単です。

あなたが達成したい場合:

1)ドロワーインジケーター -バックスタックにフラグメントがない場合、またはドロワーが開いている場合

2)矢印 -一部のフラグメントがバックスタックにある場合

private FragmentManager.OnBackStackChangedListener
        mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        syncActionBarArrowState();
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    getSupportActionBar().setDisplayShowHomeEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    mDrawerToggle = new ActionBarDrawerToggle(
            this,             
            mDrawerLayout,  
            R.drawable.ic_navigation_drawer, 
            0, 
            0  
    ) {

        public void onDrawerClosed(View view) {
            syncActionBarArrowState();
        }

        public void onDrawerOpened(View drawerView) {
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    };

    mDrawerLayout.setDrawerListener(mDrawerToggle);
    getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener);
}

@Override
protected void onDestroy() {
    getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener);
    super.onDestroy();
}

private void syncActionBarArrowState() {
    int backStackEntryCount = 
        getSupportFragmentManager().getBackStackEntryCount();
    mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

3)両方のインジケーターがその形状に従って動作する

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (mDrawerToggle.isDrawerIndicatorEnabled() && 
        mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    } else if (item.getItemId() == android.R.id.home && 
               getSupportFragmentManager().popBackStackImmediate()) {
        return true;
    } else {
        return super.onOptionsItemSelected(item);
    }
}

PS 3行インジケーターの動作に関するその他のヒントについては、Android開発者向けのナビゲーションドロワーの作成をご覧ください。


3
私はあなたのものに似たものになりました。このソリューション(BackStackChangedListenerを使用して表示されているものを切り替える)が最もエレガントだと思います。ただし、2つの変更点があります。1)onDrawerClosed / onDrawerOpenedの呼び出しでドロワーインジケーターを呼び出さない/変更しない-必要ありません。2)すべてが継承するAbstractFragmentを作成することで、フラグメント自体がアップナビゲーションを処理できるようにします。 onOptionsItemSelected(..)を実装し、常にsetHasOptionsMenu(true)を呼び出します。
Espen Riskedal 2013年

2
また、矢印モードでナビゲーションドロワーのスワイプジェスチャーをロックする必要があります。mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); ドロワーインジケーターモードで再度有効にする
artkoenig

5
私は、これらすべてをNavigationDrawerフラグメントで実行することになりました。おかげで、チャームのように動作します。
凍るような素晴らしい

1
setActionBarArrowDependingOnFragmentsBackStack()...なんて長い名前だ:P
S.Thiongane

2
フラグメントAからBに移動し、Aをバックスタックに追加しても、これにはupボタンが表示されません。:(
aandis

14

私は次のものを使用しました:

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if(getSupportFragmentManager().getBackStackEntryCount() > 0){
                    mDrawerToggle.setDrawerIndicatorEnabled(false);
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
                }
                else {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    mDrawerToggle.setDrawerIndicatorEnabled(true);
                }
            }
        });

1
本当にありがとうございますこれは実際に機能した唯一のものです。これを追加drawerToggle.setToolbarNavigationClickListener(すると、矢印をクリックするとこのリスナーが呼び出されます
EpicPandaForce

@Yuriyに感謝します。これは私の問題の解決に役立ちました
Muhammad Shoaib Murtaza

12

アクションバーの上ボタンが機能しない場合は、リスナーを追加することを忘れないでください。

// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onBackPressed();
        }
});

ホームボタンを使用したドロワーナビゲーションの実装に問題が発生しました。アクションボタン以外はすべて機能しました。


10

DrawerToggleの状態に応じて、MainActivityでHomeアイテムの選択を処理してみてください。これにより、すべてのフラグメントに同じコードを追加する必要がなくなります。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            onBackPressed();
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

+1のきちんとしたソリューション。回答を完全に使用できるようにするには、バックスタックにチェックを追加する必要があります。空の場合、引き出しインジケーターを自動的にtrueに設定します。
HpTerm 2013年

@HpTerm onBackPressed()両方に同じ動作をしたかったので、バックスタックを処理します。
dzeikei 2013年

6

ファローアップ

@dzeikeiによって与えられた解決策は素晴らしいですが、フラグメントを使用する場合、バックスタックが空のときに引き出しインジケーターを戻す設定を自動的に処理するように拡張できます。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Only handle with DrawerToggle if the drawer indicator is enabled.
    if (mDrawerToggle.isDrawerIndicatorEnabled() &&
            mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        // Handle home button in non-drawer mode
        case android.R.id.home:
            // Use getSupportFragmentManager() to support older devices
            FragmentManager fragmentManager = getFragmentManager();
            fragmentManager.popBackStack();
            // Make sure transactions are finished before reading backstack count
            fragmentManager.executePendingTransactions();
            if (fragmentManager.getBackStackEntryCount() < 1){
                mDrawerToggle.setDrawerIndicatorEnabled(true);  
            }
            return true;

        default:
            return super.onOptionsItemSelected(item);
    }
}

編集

@JJDの質問について。

フラグメントはアクティビティで保持/管理されます。上記のコードはそのアクティビティで1回記述されていますが、のアップキャレットのみを処理しますonOptionsItemSelected

私のアプリの1つでは、戻るボタンが押されたときのアップキャレットの動作も処理する必要がありました。これはをオーバーライドすることで処理できますonBackPressed

@Override
public void onBackPressed() {
    // Use getSupportFragmentManager() to support older devices
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.executePendingTransactions();
    if (fragmentManager.getBackStackEntryCount() < 1){
        super.onBackPressed();
    } else {
        fragmentManager.executePendingTransactions();
        fragmentManager.popBackStack();
        fragmentManager.executePendingTransactions();
        if (fragmentManager.getBackStackEntryCount() < 1){
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        }
    }
};

間にコードの重複を注意onOptionsItemSelected及びonBackPressed方法を作成し、両方の場所でそのメソッドを呼び出すことによって回避することができます。

またexecutePendingTransactions、私の場合はさらに2回追加する必要があります。追加しないと、アップキャレットの動作がおかしくなることがあります。


これは、アップキャレット動作を実装するために追加する必要がある完全なコードですか、それとも他の投稿の1つを参照していますか?これを明確にしてください。
JJD 2014年

編集ありがとうございます。実際、私mDrawerToggleNavigationDrawerFragmentクラスで維持しています。これを機能させるには、ホームボタン/インジケーターの状態も切り替える必要がありますNavigationDrawerFragment#toggleDrawerIndicator。以下を参照してください。さらに、最初のチェックインが必要かどうかはわかりませんonOptionsItemSelected。コメントを外しました。- 簡略化された例
JJD 2014年

実装の改訂:最初のチェックインについてはあなたが正しいonOptionsItemSelected。これにより、Navigation Drawerがトップレベルの階層で開かれたままになります。ただし、コードをに移動しましたNavigationDrawerFragment#onOptionsItemSelected。これは、にさらさmDrawerToggleれないようにするのに役立ちますMainActivity
JJD 2014年

@JJD階層の各レベルのアップキャレットのためにすべてが機能しているのに少し苦労したことを覚えています。それもあなたのために働く限り、それは結構です。もちろん、あなたが言うように、コードを他の場所に移動して公開しないでください。
HpTerm 2014年

2

ハンバーガーメニューの表示状態を更新するホスティングアクティビティのインターフェイスを作成しました。トップレベルのフラグメントの場合はトグルを設定し、上true矢印を表示したいフラグメントの場合はトグルをに設定しますfalse

public class SomeFragment extends Fragment {

    public interface OnFragmentInteractionListener {
        public void showDrawerToggle(boolean showDrawerToggle);
    }

    private OnFragmentInteractionListener mListener;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            this.mListener = (OnFragmentInteractionListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        mListener.showDrawerToggle(false);
    }
}

それから私の活動では...

public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {

    private ActionBarDrawerToggle mDrawerToggle;

    public void showDrawerToggle(boolean showDrawerIndicator) {
        mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
    }

}

2

この回答は機能しましたが、少し問題がありました。getSupportActionBar().setDisplayHomeAsUpEnabled(false)明示的に呼び出されなかった、それがbackstackにアイテムが変化するので、なかった場合でも、引き出しのアイコンを非表示にする原因となったsetActionBarArrowDependingOnFragmentsBackStack()方法は私のために働きました。

private void setActionBarArrowDependingOnFragmentsBackStack() {
        int backStackEntryCount = getSupportFragmentManager()
                .getBackStackEntryCount();
        // If there are no items in the back stack
        if (backStackEntryCount == 0) {
            // Please make sure that UP CARAT is Hidden otherwise Drawer icon
            // wont display
            getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            // Display the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(true);
        } else {
            // Show the Up carat
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            // Hide the Drawer Icon
            mDrawerToggle.setDrawerIndicatorEnabled(false);
        }

    }

私の場合、厳密なソリューションはactionBarDrawerToggle.setDrawerIndicatorEnabled(getSupportFragmentManager()。getBackStackEntryCount()<0);のみを使用していました。
Gorets

1

論理は明確です。フラグメントバックスタックがクリアされている場合は、戻るボタンを表示します。フラグメントスタックが明確でない場合は、素材のハンバーガーバックアニメーションを表示します。

getSupportFragmentManager().addOnBackStackChangedListener(
    new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            syncActionBarArrowState();
        }
    }
);


private void syncActionBarArrowState() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

//add these in Your NavigationDrawer fragment class

public void setDrawerIndicatorEnabled(boolean flag){
    ActionBar actionBar = getActionBar();
    if (!flag) {
        mDrawerToggle.setDrawerIndicatorEnabled(false);
        actionBar.setDisplayHomeAsUpEnabled(true);
        mDrawerToggle.setHomeAsUpIndicator(getColoredArrow());
    } else {
        mDrawerToggle.setDrawerIndicatorEnabled(true);
    }
    mDrawerToggle.syncState();
    getActivity().supportInvalidateOptionsMenu();
}

//download back button from this(https://www.google.com/design/icons/) website and add to your project

private Drawable getColoredArrow() {
    Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp);
    Drawable wrapped = DrawableCompat.wrap(arrowDrawable);

    if (arrowDrawable != null && wrapped != null) {
        // This should avoid tinting all the arrows
        arrowDrawable.mutate();
        DrawableCompat.setTint(wrapped, Color.GRAY);
    }
    return wrapped;
}

1

GMAILアプリを見て、ここに来て、キャレット/アフォーダンスアイコンを検索した場合。

これを行うようにお願いしますが、上記の答えはどれも明確ではありませんでした。受け入れられた回答を変更することができました。

  • NavigationDrawer->リストビューにはサブフラグメントが含まれています。


  • サブフラグメントはこのようにリストされます

  • firstFragment == position 0 --->これにはサブフラグメントがあります->フラグメント

  • secondFragment
  • thirdFragmentなど...

firstFragmentには他のフラグメントがあります。

DrawerActivityでこれを呼び出します

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager().getBackStackEntryCount() > 0) {
                mDrawerToggle.setDrawerIndicatorEnabled(false);
            } else {
                mDrawerToggle.setDrawerIndicatorEnabled(true);
            }
        }
    });

そして断片的に

    setHasOptionsMenu(true);    

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Get item selected and deal with it
    switch (item.getItemId()) {
        case android.R.id.home:
            //called when the up affordance/carat in actionbar is pressed
            activity.onBackPressed();
            return true;
    }
    return false;
}

OnBackPressed Drawerアクティビティメソッドで、ドロワートグルをtrueに設定して、ナビゲーションリストアイコンを再度有効にします。

ありがとう、Pusp



0

IMO、riwnodennyk のソリューションまたはTomのソリューションでonNavigateUp()(ここに示すように)を使用する方がクリーンで、動作が良いようです。onOptionsItemSelectedコードを次のコードに置き換えるだけです。

@Override
public boolean onSupportNavigateUp() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        // handle up navigation
        return true;
    } else {
        return super.onSupportNavigateUp();
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.