Cara menghapus Stack navigasi setelah menavigasi ke fragmen lain di Android


121

Saya menggunakan Komponen Arsitektur Navigasi baru di android dan saya terjebak dalam membersihkan tumpukan navigasi setelah pindah ke fragmen baru.

Contoh: Saya berada di loginFragment dan saya ingin fragmen ini dihapus dari tumpukan saat saya menavigasi ke fragmen beranda sehingga pengguna tidak akan kembali ke loginFragment saat dia menekan tombol kembali.

Saya menggunakan NavHostFragment.findNavController (Fragment) .navigate (R.id.homeFragment) sederhana untuk menavigasi.

Kode Saat Ini:

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

Saya mencoba menggunakan NavOptions di navigation () , tetapi tombol kembali masih mengirim saya kembali ke 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);

Anda dapat menggunakan popBackStackatau tidak menambahkan LoginFragmentuntuk backstack menyediakan nulluntuk addToBackStack(null);dan menggantinya dengan yang baruFragment
Yupi

Harap edit posting Anda dan tambahkan kode yang sekarang Anda gunakan untuk menavigasi
Barns

Saya mengedit pertanyaan saya dan menambahkan kode saya
Youssef El Behi

Saya pikir @Yupi telah memberikan saran yang bagus. Atau Anda dapat menggunakan navigate()metode seperti navigate(int resId, Bundle args, NavOptions navOptions)dan memberikan NavOptionsyang paling sesuai dengan senario Anda
Lumbung

Saya mencoba menggunakan The NavOptions tetapi tombol kembali masih mengirim saya kembali ke loginFragment
Youssef El Behi

Jawaban:


179

Pertama, tambahkan atribut app:popUpTo='your_nav_graph_id'dan app:popUpToInclusive="true"ke tag tindakan.

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

Kedua, navigasikan ke tujuan, menggunakan tindakan di atas sebagai parameter.

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

Lihat dokumen untuk informasi lebih lanjut.

CATATAN : Jika Anda menavigasi menggunakan metode navigate(@IdRes int resId), Anda tidak akan mendapatkan hasil yang diinginkan. Oleh karena itu, saya menggunakan metode navigate(@NonNull NavDirections directions).


1
Jawaban terbaik karena clearTask sekarang tidak digunakan lagi di versi baru Komponen Navigasi Android!
Michał Ziobro

14
Anda juga dapat menggunakan id tindakan seperti itufindNavController(fragment).navigate(R.id.action_signInFragment_to_usersFragment)
Calin

1
Hebat! Namun, sekarang saya memiliki mesin terbang mundur / atas, tetapi menekannya tidak melakukan apa-apa. Apa cara bersih untuk menghapusnya juga?
Danish Khan

6
Saya pikir kunci untuk jawaban ini terletak pada "popUpTo" (yang menghapus semua fragmen antara fragmen Anda saat ini dan kejadian pertama dari id yang Anda berikan ke argumen ini) diatur ke id grafik navigasi itu sendiri . Secara efektif membersihkan backstack. Hanya menambahkan komentar ini karena saya belum melihatnya menjelaskan persis seperti ini, dan itulah yang saya cari. +1!
Scott O'Toole

7
CATATAN : Bukan berarti kelas "Arah" tidak akan dibuat jika Anda tidak menyertakan gradle safe-args. Untuk info lebih lanjut kunjungi di sini
Jaydip Kalkani

28

Dalam kasus saya, saya perlu menghapus semua yang ada di Stack belakang sebelum saya membuka fragmen baru jadi saya menggunakan kode ini

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

baris pertama menghapus Stack belakang sampai mencapai fragmen yang ditentukan dalam kasus saya itu adalah fragmen home sehingga menghapus semua back stack sepenuhnya, dan ketika pengguna mengklik kembali di fragment_company dia menutup aplikasi.


1
Apa fragment_apps??
IgorGanapolsky

1
Fragmen saat ini. Ini seperti jika Anda menggunakan fragmentOne dan ingin menggunakan fragmentTwo Anda juga perlu menggunakan navController.popBackStack (R.id.fragmentOne, true); navController.navigate (R.id.fragmentTwo);
Bbeni

26

Saya pikir pertanyaan Anda secara khusus berkaitan dengan cara menggunakan Pop Behavior / Pop To / app: popUpTo (dalam xml)

Dalam dokumentasi,
Munculkan ke tujuan tertentu sebelum menavigasi. Ini memunculkan semua tujuan yang tidak cocok dari tumpukan belakang hingga tujuan ini ditemukan.

Contoh (Aplikasi berburu Pekerjaan Sederhana) grafik start_screen_nav
saya seperti ini:

startScreenFragment (start) -> loginFragment -> EmployerMainFragment

                            -> loginFragment -> JobSeekerMainFragment

jika saya ingin menavigasi ke EmployerMainFragmentdan memunculkan semua termasuk startScreenFragment maka kodenya adalah:

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

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

jika saya ingin menavigasi ke EmployerMainFragmentdan memunculkan semua yang tidak termasuk startScreenFragment maka kodenya adalah:

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

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

jika saya ingin menavigasi ke EmployerMainFragmentdan muncul loginFragmenttetapi tidak startScreenFragment maka kodenya adalah:

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

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

ATAU

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

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

Bagaimana melakukan ini secara terprogram daripada dalam XML?
IgorGanapolsky

14

CATATAN: Tugas yang jelas tidak digunakan lagi, deskripsi resmi tidak digunakan

Metode ini tidak digunakan lagi. Gunakan setPopUpTo (int, boolean) dengan id grafik NavController dan setel inklusif ke true.

Jawaban Lama

Jika Anda tidak ingin melihat semua kode fuzz itu, Anda cukup check- Clear Taskin Launch Optionsdi properti tindakan.

Luncurkan Opsi

Edit: Mulai Android Studio 3.2 Beta 5, Hapus Tugas tidak lagi terlihat di jendela Opsi Peluncuran, tetapi Anda masih dapat menggunakannya dalam kode XML navigasi, dalam tag tindakan , dengan menambahkan

app:clearTask="true"

Anda harus menggunakan id dari root grafik sebagai gantinya karena pesan clearTask xml atributedeprecation mengatakan: "setel popUpTo ke root grafik Anda dan gunakan popUpToInclusive". Saya telah melakukannya seperti ini dan mencapai perilaku yang diinginkan yang sama dengan findNavController (). Navigasikan (@IdRes resId, bundle)
artnest

Menggunakan pendekatan direkomendasikan pengaturan popUpTo dan popUpToInclusive tidak tampaknya akan bekerja untuk tindakan antara tujuan lain yang awal tujuan: issuetracker.google.com/issues/116831650
hsson

Saya benar-benar mengalami masalah di mana itu tidak berfungsi dengan tujuan awal, membuat frustrasi.
Mel

Bagaimana Anda melakukan ini jika Anda tidak tahu bagian mana yang ingin Anda letakan? Misalnya saya memiliki fragmen yang membuat item baru. Setelah membuat, saya ingin menunjukkan tampilan detail item ini. Tetapi saya tidak ingin pengguna untuk kembali ke membuat fragmen setelahnya, tetapi ke tampilan sebelumnya. Itu bisa berupa daftar semua item atau item berbeda yang menjadi dasar item baru. Apakah kemudian clearTask satu-satunya pilihan?
findusl

Tidak, sebenarnya seharusnya tidak digunakan lagi. Sebaliknya Anda harus menggunakan popUpTo secara inklusif. Saya akan segera memperbarui jawaban dengan temuan saya tentang penggunaan popUpTo.
Mel

5

Saya akhirnya mengetahuinya berkat Bagaimana cara menonaktifkan UP di Navigasi untuk beberapa fragmen dengan Komponen Arsitektur Navigasi baru?

Saya harus menentukan .setClearTask (true) sebagai NavOption.

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
Senang melihat Anda dapat menerapkan saran saya untuk menggunakan navigate(int resId, Bundle args, NavOptions navOptions)danNavOptions
Barns

Iya. Terima kasih @Barns
Youssef El Behi

14
ya "setClearTask" tidak digunakan lagi, tetapi Anda dapat menggunakan: val navOptions = NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build() findNavController().navigate(R.id.my_next_frag, null, navOptions)
Pablo Reyes

1
@ c-an itu harus bekerja. Mungkin Anda menggunakan fragmentId tujuan yang salah (misalnya R.id.my_next_fragseperti login atau splash screen); juga jika Anda ingin popBack hanya satu, Anda dapat menggunakan: NavHostFragment.findNavController(this).popBackStack().
Pablo Reyes

2
@PabloReyes Ini adalah jawaban yang benar. Cukup tentukan fragmen mana yang terakhir dihapus: Misalkan Anda memiliki tumpukan ini: Fragment_1 Fragment_2 Fragment_3 Anda sekarang berada di Fragment_3 dan ingin menavigasi ke Fragment_4 serta menghapus semua fragmen lainnya. Cukup tentukan dalam navigasi xml: app: popUpTo = "@ id / Fragment_1" app: popUpToInclusive = "true"
pengguna3193413

5

Inilah cara saya menyelesaikannya.

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

Mengapa bendera inklusif Anda false?
IgorGanapolsky

2
Menurut dokumentasi inclusive boolean: true to also pop the given destination from the back stack Jadi, saya menetapkan inklusif ke false karena saya tidak ingin mengeluarkan fragmen saat ini dari tumpukan.
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

Anda dapat mengganti penekanan belakang dari aktivitas dasar seperti ini:

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

Catatan: Ini mungkin tidak berhubungan dengan pertanyaan sebenarnya.

Pendekatan ini akan berguna jika satu instance fragmen perlu dipertahankan dalam Navigasi .

// 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
                            })

Ini navOptionsadalah DSL dan perlu diimpor. Pada saat jawaban ini saya menggunakan UI Navigasi versi gradle berikut .

    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

Saya berjuang sebentar untuk mencegah tombol kembali kembali ke fragmen awal saya, yang dalam kasus saya adalah pesan intro yang seharusnya hanya muncul sekali.

Solusi yang mudah adalah dengan membuat tindakan global yang menunjuk ke tujuan yang harus dituju oleh pengguna. Anda harus menyetelnya app:popUpTo="..."dengan benar - setel ke tujuan yang Anda inginkan untuk dimunculkan. Dalam kasus saya, itu adalah pesan intro saya. Juga diaturapp:popUpToInclusive="true"


0

Saya bisa menjelaskan ini melalui contoh kecil. Saya memiliki dua fragmen daftar karyawan dan menghapus karyawan. Dalam menghapus karyawan, saya memiliki dua peristiwa klik membatalkan dan menghapus. Jika saya mengklik batal, maka cukup kembali ke layar daftar karyawan dan jika saya klik hapus maka saya ingin menghapus semua backstacks sebelumnya dan pergi ke layar daftar karyawan.

Untuk membuka layar sebelumnya:

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


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

Untuk menghapus semua backstack sebelumnya dan pergi ke layar baru:

<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())
        }

Untuk referensi lebih lanjut: Contoh Navigasi Jetpack


0

Anda dapat melakukan sesederhana:

getFragmentManager().popBackStack();

Jika Anda ingin memeriksa hitungan, Anda dapat melakukan seperti:

getFragmentManager().getBackStackEntryCount()
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.