วิธีล้างแถบการนำทางหลังจากนำทางไปยังส่วนอื่นใน Android


122

ฉันใช้ส่วนประกอบสถาปัตยกรรมการนำทางใหม่ใน 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());
                    }
                }
            });

ฉันลองใช้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 ให้nullไปaddToBackStack(null);และแทนที่ด้วยใหม่Fragment
Yupi

โปรดแก้ไขโพสต์ของคุณและเพิ่มรหัสที่คุณใช้นำทาง
Barns

ฉันแก้ไขคำถามและเพิ่มรหัส
Youssef El Behi

ฉันคิดว่า @Yupi ได้ให้คำแนะนำที่ดี หรือคุณอาจใช้navigate()วิธีการเช่นnavigate(int resId, Bundle args, NavOptions navOptions)และจัดหารุ่นNavOptionsที่เหมาะสมที่สุดของคุณ
Barns

ฉันพยายามใช้ NavOptions แต่ปุ่มย้อนกลับยังคงส่งฉันกลับไปที่ loginFragment
Youssef El Behi

คำตอบ:


181

ขั้นแรกให้เพิ่มแอตทริบิวต์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
คำตอบที่ดีที่สุดขณะนี้ clearTask เลิกใช้งานใน Android Navigation Component เวอร์ชันใหม่แล้ว!
Michał Ziobro

14
นอกจากนี้ยังสามารถใช้รหัสของการกระทำเช่นนั้นได้findNavController(fragment).navigate(R.id.action_signInFragment_to_usersFragment)
Calin

1
! น่ากลัว อย่างไรก็ตามตอนนี้ฉันมีสัญลักษณ์กลับ / ขึ้น แต่การกดมันไม่ได้ทำอะไรเลย วิธีที่สะอาดในการลบออกด้วยคืออะไร?
ข่านเดนมาร์ก

6
ผมคิดว่ากุญแจสำคัญในการตอบนี้อยู่ในว่า "popUpTo" (ซึ่งเอาชิ้นส่วนทั้งหมดระหว่างส่วนปัจจุบันของคุณและเกิดขึ้นครั้งแรกของ id ที่คุณส่งผ่านไปยังเรื่องนี้) ถูกตั้งค่าเป็นID ของกราฟนำทางตัวเอง ล้างแบ็คสแต็คได้อย่างมีประสิทธิภาพ เพิ่มความคิดเห็นนี้เท่านั้นเพราะฉันไม่เห็นมันอธิบายแบบนี้เลยและมันคือสิ่งที่ฉันกำลังมองหา +1!
Scott O'Toole

7
หมายเหตุ : ไม่ใช่ว่าคลาส "เส้นทาง" จะไม่ถูกสร้างขึ้นหากคุณไม่ได้รวมเซฟ - อาร์กเกรเดิ้ล ดูข้อมูลเพิ่มเติมได้ที่นี่
Jaydip Kalkani

28

ฉันคิดว่าคำถามของคุณเกี่ยวข้องกับวิธีใช้ Pop Behavior / Pop To / 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

28

ในกรณีของฉันฉันจำเป็นต้องลบทุกอย่างในกองหลังก่อนที่ฉันจะเปิดส่วนใหม่ดังนั้นฉันจึงใช้รหัสนี้

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

บรรทัดแรกจะลบกองหลังออกจนกว่าจะถึงแฟรกเมนต์ที่ระบุในกรณีของฉันมันคือแฟรกเมนต์ของบ้านดังนั้นจึงลบสแต็กด้านหลังทั้งหมดออกอย่างสมบูรณ์และเมื่อผู้ใช้คลิกกลับในแฟรกเมนต์ _ บริษัท เขาจะปิดแอพ


1
คืออะไรfragment_apps??
IgorGanapolsky

1
ส่วนปัจจุบัน เหมือนกับว่าคุณอยู่ใน fragmentOne และต้องการไปที่ fragmentTwo คุณก็ต้องใช้ navController.popBackStack (R.id.fragmentOne, true) navController.navigate (R.id.fragmentTwo);
Bbeni

14

หมายเหตุ:เลิกใช้งาน Clear แล้วคำอธิบายอย่างเป็นทางการคือ

วิธีนี้เลิกใช้แล้ว ใช้ setPopUpTo (int, boolean) กับ id ของกราฟของ NavController และตั้งค่ารวมเป็นจริง

คำตอบเก่า

หากคุณไม่ต้องการที่จะไปผ่านทุกฝอยว่าในรหัสคุณก็สามารถตรวจสอบClear TaskในLaunch Optionsในคุณสมบัติของการดำเนินการ

เปิดตัวเลือก

แก้ไข:ตั้งแต่ Android Studio 3.2 Beta 5 Clear Task จะไม่ปรากฏในหน้าต่าง Launch Options อีกต่อไป แต่คุณยังสามารถใช้งานได้ในโค้ด XML ของการนำทางในแท็กการดำเนินการโดยการเพิ่ม

app:clearTask="true"

คุณควรใช้ id ของรูทของกราฟแทนเนื่องจากข้อความ clearTask xml ระบุแอตทริบิวต์ที่ระบุว่า: "ตั้งค่า popUpTo เป็นรูทของกราฟของคุณและใช้ popUpToInclusive" ฉันทำแบบนี้แล้วและบรรลุพฤติกรรมที่ต้องการเช่นเดียวกันกับ findNavController (). navigation (@IdRes resId, bundle)
artnest

การใช้แนวทางที่แนะนำในการตั้งค่า popUpTo และ popUpToInclusive ดูเหมือนจะไม่ได้ผลสำหรับการดำเนินการระหว่างปลายทางอื่นที่ปลายทางเริ่มต้น: issueetracker.google.com/issues/116831650
hsson

ฉันพบปัญหาที่ใช้ไม่ได้กับปลายทางเริ่มต้นซึ่งน่าหงุดหงิด
Mel

คุณจะทำสิ่งนี้ได้อย่างไรถ้าคุณไม่ทราบว่าส่วนที่คุณต้องการให้ปรากฏ? ตัวอย่างเช่นฉันมีส่วนที่สร้างรายการใหม่ หลังจากสร้างฉันต้องการแสดงมุมมองรายละเอียดของรายการนี้ แต่ฉันไม่ต้องการให้ผู้ใช้กลับไปที่ส่วนสร้างในภายหลัง แต่กลับไปที่มุมมองก่อนหน้า ซึ่งอาจเป็นรายการของรายการทั้งหมดหรือรายการอื่นที่อิงจากรายการใหม่ clearTask เป็นตัวเลือกเดียวหรือไม่?
findusl

ไม่จริงไม่ควรใช้อีกต่อไป คุณควรใช้ popUpTo แทน ฉันจะอัปเดตคำตอบเร็ว ๆ นี้พร้อมผลการใช้งาน popUpTo
Mel

5

ในที่สุดฉันก็คิดออกด้วยวิธีปิดใช้งาน UP ในการนำทางสำหรับบางส่วนด้วยส่วนประกอบสถาปัตยกรรมการนำทางใหม่

ฉันต้องระบุ. setClearTask (true) เป็น 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
ดีใจที่ได้เห็นคุณสามารถทำตามคำแนะนำของฉันในการใช้navigate(int resId, Bundle args, NavOptions navOptions)และ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)
Pablo Reyes

1
@ c-an มันควรจะทำงาน คุณอาจใช้แฟรกเมนต์ปลายทางที่ไม่ถูกต้อง (เช่นR.id.my_next_fragอาจเป็นเหมือนล็อกอินหรือสแปลชสกรีน) นอกจากนี้หากคุณต้องการ popBack เพียงอันเดียวคุณสามารถใช้: NavHostFragment.findNavController(this).popBackStack().
Pablo Reyes

2
@PabloReyes นี่คือคำตอบที่ถูกต้อง เพียงระบุว่าส่วนใดเป็นส่วนสุดท้ายที่จะลบ: สมมติว่าคุณมีสแต็กนี้: Fragment_1 Fragment_2 Fragment_3 ตอนนี้คุณอยู่บน Fragment_3 และต้องการไปที่ Fragment_4 และล้างส่วนอื่น ๆ ทั้งหมด เพียงระบุในการนำทาง 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 ดังนั้นฉันจึงตั้งค่าการรวมเป็นเท็จเพราะฉันไม่ต้องการเปิดส่วนปัจจุบันจากสแต็ก
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

ฉันสามารถอธิบายสิ่งนี้ผ่านตัวอย่างเล็ก ๆ ฉันมีรายชื่อพนักงานสองชิ้นและลบพนักงาน ในการลบพนักงานฉันมีเหตุการณ์สองคลิกที่ยกเลิกและลบ ถ้าฉันคลิกยกเลิกมันก็กลับไปที่หน้าจอรายชื่อพนักงานและถ้าฉันคลิกลบฉันต้องการล้างแบ็คสต็อกก่อนหน้าทั้งหมดและไปที่หน้าจอรายชื่อพนักงาน

สำหรับไปที่หน้าจอก่อนหน้า:

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


0

คุณสามารถทำได้ง่ายๆดังนี้:

getFragmentManager().popBackStack();

หากคุณต้องการตรวจสอบจำนวนคุณสามารถทำได้ดังนี้:

getFragmentManager().getBackStackEntryCount()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.