Androidで別のフラグメントに移動した後にナビゲーションスタックをクリアする方法


121

Androidで新しいナビゲーションアーキテクチャコンポーネントを使用していますが、新しいフラグメントに移動した後、ナビゲーションスタックをクリアできません。

例:loginFragmentにいて、ホームフラグメントに移動したときにこのフラグメントをスタックからクリアして、ユーザーが[戻る]ボタンを押したときにloginFragmentに戻らないようにします。

シンプルなNavHostFragment.findNavController(Fragment).navigate(R.id.homeFragment)を使用してナビゲートしています。

現在のコード:

mAuth.signInWithCredential(credential)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment);
                    } else {
                        Log.w(TAG, "signInWithCredential:failure", task.getException());
                    }
                }
            });

navigate()NavOptionsを使用してましたが、戻るボタンがまだloginFragmentに戻ってきます

NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder.setPopUpTo(R.id.homeFragment, false).build();   
             NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment, null, navOptions);

あなたは使用することができますpopBackStackか追加していないLoginFragment提供backstackすることnulladdToBackStack(null);して、新しいと交換Fragment
ユピー

投稿を編集して、ナビゲーションに現在使用しているコードを追加してください
Barns

質問を編集してコードを追加しました
Youssef El Behi

@Yupiは良い提案をしてくれたと思います。または、次のnavigate()ような方法を使用して、シナリオに最適な方法navigate(int resId, Bundle args, NavOptions navOptions)を提供することができNavOptionsます
Barns

NavOptionsを使用してみましたが、戻るボタンを押してもloginFragmentに戻ります
Youssef El Behi

回答:


179

まず、属性app:popUpTo='your_nav_graph_id'app:popUpToInclusive="true"アクションタグを追加します。

<fragment
    android:id="@+id/signInFragment"
    android:name="com.glee.incog2.android.fragment.SignInFragment"
    android:label="fragment_sign_in"
    tools:layout="@layout/fragment_sign_in" >
    <action
        android:id="@+id/action_signInFragment_to_usersFragment"
        app:destination="@id/usersFragment"
        app:launchSingleTop="true"
        app:popUpTo="@+id/main_nav_graph"
        app:popUpToInclusive="true" />
</fragment>

次に、上記のアクションをパラメーターとして使用して、宛先にナビゲートします。

findNavController(fragment).navigate(
     SignInFragmentDirections.actionSignInFragmentToUserNameFragment())

詳細については、ドキュメントを参照してください。

:メソッドを使用してナビゲートnavigate(@IdRes int resId)すると、目的の結果が得られません。したがって、私はメソッドを使用しましたnavigate(@NonNull NavDirections directions)


1
Androidナビゲーションコンポーネントの新しいバージョンではclearTaskが非推奨となったため、ベストアンサーです。
のMichałZiobro

14
アクションのIDを次のように使用することもできますfindNavController(fragment).navigate(R.id.action_signInFragment_to_usersFragment)
Calin

1
驚くばかり!しかし、今はバックアップ/アップグリフがありますが、それを押しても何も起こりません。それを削除するクリーンな方法は何ですか?
Danish Khan

6
この答えの鍵は、「popUpTo」(現在のフラグメントと、この引数に渡すIDの最初の出現との間のすべてのフラグメントを削除する)がナビゲーショングラフ自体のIDに設定されていることにあると思います。バックスタックを効果的に完全にクリアします。このコメントは、このように正確に説明されていないため、追加しただけで、探していたものでした。+1!
スコットオトゥール

7
:safe-argsグラドルを含めなかった場合、「Directions」クラスは生成されません。詳細については、こちらを
Jaydip Kalkani

28

私の場合、新しいフラグメントを開く前にバックスタックのすべてを削除する必要があったので、このコードを使用しました

navController.popBackStack(R.id.fragment_apps, true);
navController.navigate(R.id.fragment_company);

1行目は、バックスタックを削除します。私の場合は、指定されたフラグメントに到達するまでホームフラグメントであるため、すべてのバックスタックを完全に削除し、fragment_companyでユーザーがクリックすると、アプリを閉じます。


1
fragment_appsですか?
IgorGanapolsky

1
現在のフラグメント。これは、uがfragmentOneにあり、fragmentTwoに行きたい場合に似ています。navController.navigate(R.id.fragmentTwo);
Bbeni

26

あなたの質問は特にポップ動作/ポップ先/ app:popUpTo(xml)の使用方法に関係していると思います

ドキュメントでは、
ナビゲートする前に特定の目的地にポップアップします。これにより、この宛先が見つかるまで、一致しないすべての宛先がバックスタックからポップされます。

例(単純な就職活動アプリ)
私のstart_screen_navグラフは次のとおりです。

startScreenFragment (start) -> loginFragment -> EmployerMainFragment

                            -> loginFragment -> JobSeekerMainFragment

を含めてEmployerMainFragmentすべてに移動してポップしたい場合、コードは次のようになります: startScreenFragment

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"
            app:popUpToInclusive="true" />

を除いてEmployerMainFragmentすべてに移動してポップしたい場合、コードは次のようになります: startScreenFragment

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"/>

私はに移動したい場合EmployerMainFragmentや、ポップloginFragmentが、ない startScreenFragmentし、コードは次のようになります。

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/loginFragment"
            app:popUpToInclusive="true"/>

または

        <action
            android:id="@+id/action_loginFragment_to_employerMainFragment"
            app:destination="@id/employerMainFragment"

            app:popUpTo="@+id/startScreenFragment"/>

XMLではなくプログラムでこれを行う方法
IgorGanapolsky

14

注:明確なタスクは非推奨です。公式の説明は

このメソッドは非推奨です。NavControllerのグラフのIDでsetPopUpTo(int、boolean)を使用し、包括的にtrueに設定します。

古い答え

あなたは、コード内のすべてのものファズを通過したくない場合は、単に確認することができますClear TaskでのLaunch Optionsアクションのプロパティで。

起動オプション

編集: Android Studio 3.2 Beta 5以降、[クリアタスク]は[起動オプション]ウィンドウに表示されなくなりましたが、追加することで、ナビゲーションのXMLコード、アクションタグで引き続き使用できます。

app:clearTask="true"

代わりに、clearTask xml属性の非推奨メッセージに「popUpToをグラフのルートに設定し、popUpToInclusiveを使用する」というように、グラフのルートのIDを使用する必要があります。私はこのようにして、findNavController()。navigate(@IdRes resId、bundle)で同じ望ましい動作を実現しました
artnest

popUpToとpopUpToInclusiveを設定するという推奨されるアプローチを使用しても、開始宛先以外の宛先間のアクションでは機能しないようです:issuetracker.google.com/issues/116831650
issues hsson

私は実際にそれが開始宛先で動作しないという問題に遭遇しています、イライラします。
Mel

ポップしたいフラグメントがわからない場合、どうすればいいですか?たとえば、新しいアイテムを作成するフラグメントがあります。作成後、このアイテムの詳細ビューを表示します。しかし、後でユーザーがcreateフラグメントにポップバックするのではなく、前のビューに戻るようにします。これは、すべてのアイテムのリストか、新しいアイテムのベースとなった別のアイテムのいずれかです。次に、clearTaskが唯一のオプションですか?
findusl

いいえ、実際にはもう使用すべきではありません。代わりに、包括的にpopUpToを使用する必要があります。私はpopUpToの使用法の私の発見ですぐに答えを更新します。
Mel

5

新しいナビゲーションアーキテクチャコンポーネントを使用して、一部のフラグメントのナビゲーションでUPを無効にする方法のおかげで、ようやくそれを理解できますか?

NavOptionとして.setClearTask(true)を指定する必要がありました。

mAuth.signInWithCredential(credential)
            .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        Log.d(TAG, "signInWithCredential:success");


                        NavOptions.Builder navBuilder = new NavOptions.Builder();
                        NavOptions navOptions = navBuilder.setClearTask(true).build();
                        NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment,null,navOptions);
                    } else {
                        Log.w(TAG, "signInWithCredential:failure", task.getException());

                    }

                }
            });

2
navigate(int resId, Bundle args, NavOptions navOptions)and NavOptions
Barns

はい。ありがとう@Barns
Youssef El Behi

14
ええ「setClearTask」が廃止されましたが、代わりに、あなたは使用することができます: val navOptions = NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build() findNavController().navigate(R.id.my_next_frag, null, navOptions)
パブロ・レイエス

1
@ c-それはうまくいくはずです。おそらく、間違った宛先fragmentIdを使用しています(たとえばR.id.my_next_frag、ログインまたはスプラッシュスクリーンのようである可能性があります)。また、1つだけpopBackしたい場合は、次を使用できますNavHostFragment.findNavController(this).popBackStack()
Pablo Reyes

2
@PabloReyesこれが正解です。削除する最後のフラグメントを指定するだけです。たとえば、次のスタックがあるとします。ナビゲーションxmlで次のように指定するだけです:app:popUpTo = "@ id / Fragment_1" app:popUpToInclusive = "true"
user3193413

5

ここに私がそれを成し遂げる方法があります。

 //here the R.id refer to the fragment one wants to pop back once pressed back from the newly  navigated fragment
 val navOption = NavOptions.Builder().setPopUpTo(R.id.startScorecardFragment, false).build()

//now how to navigate to new fragment
Navigation.findNavController(this, R.id.my_nav_host_fragment)
                    .navigate(R.id.instoredBestPractice, null, navOption)

なぜあなたの包括的フラグなのfalseですか?
IgorGanapolsky

2
ドキュメントに よるとinclusive boolean: true to also pop the given destination from the back stack 、スタックから現在のフラグメントをポップしたくないので、inclusiveをfalseに設定します。
Abdul Rahman Shamair

4
    NavController navController 
    =Navigation.findNavController(requireActivity(),          
    R.id.nav_host_fragment);// initialize navcontroller

    if (navController.getCurrentDestination().getId() == 
     R.id.my_current_frag) //for avoid crash
  {
    NavDirections action = 
    DailyInfoSelectorFragmentDirections.actionGoToDestionationFragment();

    //for clear current fragment from stack
    NavOptions options = new 
    NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build();
    navController.navigate(action, options);
    }

1

次のように、基本アクティビティのバックプレスをオーバーライドできます。

override fun onBackPressed() {

  val navigationController = nav_host_fragment.findNavController()
  if (navigationController.currentDestination?.id == R.id.firstFragment) {
    finish()
  } else if (navigationController.currentDestination?.id == R.id.secondFragment) {
    // do nothing
  } else {
    super.onBackPressed()
  }

}

1

注:これは実際の質問とは関係がない場合があります。

この方法は、ナビゲーションで維持するためにフラグメントの単一インスタンスが必要な場合に役立ちます。

// imports
import androidx.navigation.navOptions

// navigate to fragment
navController.navigate(R.id.nav_single_instance_fragment, null,
                            navOptions {
                                popUpTo = R.id.nav_single_instance_fragment
                                launchSingleTop = true
                            })

これnavOptionsはDSL で、インポートする必要があります。この回答の時点では、次のGradleバージョンのナビゲーションUIを使用しています。

    def nav_version = "2.2.0"
    implementation "androidx.navigation:navigation-ui:$nav_version"
    implementation "androidx.navigation:navigation-fragment:$nav_version"
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

0

私はしばらくの間、戻るボタンが開始フラグメントに戻るのを防ぐために苦労しました。これは、私の場合、一度だけ表示されるはずのイントロメッセージでした。

簡単な解決策は、ユーザーが留まる必要のある宛先を指すグローバルアクションを作成することでした。あなたはapp:popUpTo="..."正しく設定する必要があります-それを飛び出させたい目的地に設定してください。私の場合、それは私の紹介メッセージでした。また設定app:popUpToInclusive="true"


0

これを小さな例で説明できます。2つのフラグメントの従業員リストがあり、従業員を削除しています。従業員の削除では、キャンセルと削除の2つのクリックイベントがあります。[キャンセル]をクリックすると、従業員リスト画面に戻ります。[削除]をクリックすると、以前のすべてのバックスタックをクリアして、従業員リスト画面に移動します。

前の画面に移動するには:

<action
        android:id="@+id/action_deleteEmployeeFragment_to_employeesListFragment2"
        app:destination="@id/employeesListFragment"/>


    btn_cancel.setOnClickListener {
                it.findNavController().popBackStack()
            }

以前のすべてのバックスタックをクリアして新しい画面に移動するには:

<action
        android:id="@+id/action_deleteEmployeeFragment_to_employeesListFragment2"
        app:destination="@id/employeesListFragment"
        app:popUpTo="@id/employeesListFragment"
        app:popUpToInclusive="true"
        app:launchSingleTop="true" />


 btn_submit.setOnClickListener {
                   it.findNavController().navigate(DeleteEmployeeFragmentDirections.actionDeleteEmployeeFragmentToEmployeesListFragment2())
        }

詳細情報:Jetpackナビゲーションの例


0

次のように簡単に実行できます。

getFragmentManager().popBackStack();

カウントを確認したい場合は、次のようにします。

getFragmentManager().getBackStackEntryCount()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.