มีวิธีที่จะทำให้ส่วนยังคงมีชีวิตอยู่เมื่อใช้ BottomNavigationView กับ NavController ใหม่หรือไม่?


96

ฉันกำลังพยายามใช้ส่วนประกอบการนำทางใหม่ ฉันใช้ BottomNavigationView กับ navController: NavigationUI.setupWithNavController (bottomNavigation, navController)

แต่เมื่อฉันเปลี่ยนชิ้นส่วนพวกมันแต่ละครั้งจะทำลาย / สร้างแม้ว่าจะเคยใช้มาก่อนก็ตาม

มีวิธีที่จะทำให้ชิ้นส่วนหลักของเรามีชีวิตอยู่เชื่อมโยงไปยัง BottomNavigationView ของเราหรือไม่?


2
ตามความคิดเห็นในIssetracker.google.com/issues/80029773ดูเหมือนว่าสิ่งนี้จะได้รับการแก้ไขในที่สุด ฉันก็อยากรู้เหมือนกันว่าถ้าคนมีวิธีแก้ปัญหาราคาถูกที่ไม่เกี่ยวข้องกับการละทิ้งห้องสมุดเพื่อให้ทำงานนี้ได้ในระหว่างนี้
Jimmy Alexander

คำตอบ:


69

ลองทำตามนี้

เนวิเกเตอร์

สร้างเนวิเกเตอร์แบบกำหนดเอง

@Navigator.Name("custom_fragment")  // Use as custom tag at navigation.xml
class CustomNavigator(
    private val context: Context,
    private val manager: FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?) {
        val tag = destination.id.toString()
        val transaction = manager.beginTransaction()

        val currentFragment = manager.primaryNavigationFragment
        if (currentFragment != null) {
            transaction.detach(currentFragment)
        }

        var fragment = manager.findFragmentByTag(tag)
        if (fragment == null) {
            fragment = destination.createFragment(args)
            transaction.add(containerId, fragment, tag)
        } else {
            transaction.attach(fragment)
        }

        transaction.setPrimaryNavigationFragment(fragment)
        transaction.setReorderingAllowed(true)
        transaction.commit()

        dispatchOnNavigatorNavigated(destination.id, BACK_STACK_DESTINATION_ADDED)
    }
}

NavHostFragment

สร้าง NavHostFragment แบบกำหนดเอง

class CustomNavHostFragment: NavHostFragment() {
    override fun onCreateNavController(navController: NavController) {
        super.onCreateNavController(navController)
        navController.navigatorProvider += PersistentNavigator(context!!, childFragmentManager, id)
    }
}

navigation.xml

ใช้แท็กที่กำหนดเองแทนแท็กแฟรกเมนต์

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/navigation"
    app:startDestination="@id/navigation_first">

    <custom_fragment
        android:id="@+id/navigation_first"
        android:name="com.example.sample.FirstFragment"
        android:label="FirstFragment" />
    <custom_fragment
        android:id="@+id/navigation_second"
        android:name="com.example.sample.SecondFragment"
        android:label="SecondFragment" />
</navigation>

รูปแบบกิจกรรม

ใช้ CustomNavHostFragment แทน NavHostFragment

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="com.example.sample.CustomNavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/navigation" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

อัปเดต

ฉันสร้างโครงการตัวอย่าง ลิงค์

ฉันไม่ได้สร้าง NavHostFragment แบบกำหนดเอง ฉันใช้navController.navigatorProvider += navigator.


3
แม้ว่าโซลูชันนี้fragmentsจะใช้งานได้ ( ใช้ซ้ำไม่ได้สร้างขึ้นใหม่) แต่มีปัญหากับการนำทาง แต่ละmenuitemอินbottomnavigationviewถือเป็นหลักดังนั้นเมื่อBACKกดแอปจะเสร็จสิ้น (หรือไปที่คอมโพเนนต์ด้านบน) ทางออกที่ดีกว่าคือการทำงานเหมือนกับแอป YouTube: (1) แตะหน้าแรก; (2) แตะแนวโน้ม; (3) แตะ Inbox; (4) แตะแนวโน้ม; (5) แตะBACK- ไปที่กล่องจดหมาย; (6) แตะBACK- ไปที่หน้าแรก; (7) แตะBACK- มีแอปอยู่ โดยสรุปBACKฟังก์ชันการทำงานของYouTube ระหว่าง " menuitems" จะย้อนกลับไปเป็นรายการล่าสุดโดยไม่ซ้ำรายการ
miguelt

3
วิธีใช้ปุ่มย้อนกลับ แอปกำลังสิ้นสุดเมื่อกดปุ่มย้อนกลับ
Francis

5
@ jL4 ฉันมีปัญหาเดียวกันคุณอาจลืมลบออกapp:navGraphจาก NavHostFragment ของกิจกรรมของคุณ
Paul Chernenko

6
ทำไม google ไม่แกะฟังก์ชันนี้ออกจากกล่อง?
Arsenius

55
NavigationComponent ควรกลายเป็นวิธีแก้ปัญหาไม่ให้กลายเป็นปัญหาอื่นดังนั้นโปรแกรมเมอร์ต้องสร้างวิธีแก้ปัญหาเช่นนี้
Arie Agung

24

ลิงก์ตัวอย่างของ Google เพียงคัดลอก NavigationExtensions ไปยังแอปพลิเคชันของคุณและกำหนดค่าตามตัวอย่าง ใช้งานได้ดี


1
แต่ใช้ได้กับการนำทางด้านล่างเท่านั้น หากคุณมีการนำทางทั่วไปคุณมีปัญหา
Georgiy Chebotarev

ฉันประสบปัญหาเดียวกันและทุกวิธีที่ฉันตรวจสอบว่ามีรหัส kotlin แต่ฉันกลัวว่าจะใช้ Java ในโครงการของฉัน ใครสามารถช่วยฉันวิธีแก้ไขปัญหานี้ใน java
CodeRED Innovations

@CodeREDInnovations ฉันเช่นกันแม้ว่าฉันจะพยายามและคอมไพล์อย่างเต็มที่ด้วยไฟล์ kotlin การนำทางยังคงเป็นเช่นนี้: imgur.com/a/DcsKqPrมันไม่ได้ "แทนที่" นอกจากนั้นดูเหมือนว่าแอปจะหนักขึ้นและติดขัด
Acauã Pitta

@ AcauãPittaคุณพบวิธีแก้ปัญหาใน Java หรือไม่?
พัฒนาคินเบิร์ก

11

หลังจากการวิจัยหลายชั่วโมงฉันพบวิธีแก้ปัญหา อยู่ตรงหน้าเราตลอดเวลา :) มีฟังก์ชั่น: popBackStack(destination, inclusive)ซึ่งนำทางไปยังปลายทางที่กำหนดหากพบใน backStack มันส่งคืนบูลีนดังนั้นเราจึงสามารถนำทางไปที่นั่นได้ด้วยตนเองหากคอนโทรลเลอร์ไม่พบแฟรกเมนต์

if(findNavController().popBackStack(R.id.settingsFragment, false)) {
        Log.d(TAG, "SettingsFragment found in backStack")
    } else {
        Log.d(TAG, "SettingsFragment not found in backStack, navigate manually")
        findNavController().navigate(R.id.settingsFragment)
    }

จะใช้อย่างไรและที่ไหน? ถ้าฉันนำทางจาก Fragment A ไป B จากนั้นจาก B ไป A ฉันจะไปได้อย่างไรโดยไม่เรียกใช้เมธอด oncraeteView () และ onViewCreated () ในระยะสั้นโดยไม่ต้องรีสตาร์ทส่วน A
Kishan Solanki

@UsamaSaeedUS: คุณช่วยแชร์รหัสวิธีใช้งานได้ไหม? เช่นเดียวกับสิ่งที่คิชานกล่าวถึง
Code_Life


1

ไม่สามารถใช้ได้ในขณะนี้

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

คุณสามารถใช้ LiveData เพื่อทำให้ข้อมูลของคุณสามารถสังเกตได้ตลอดอายุการใช้งาน


1
นี่เป็นความจริงและ data> view แต่ปัญหาคือเมื่อคุณต้องการคงสถานะการดูไว้เช่นโหลดหลายเพจและผู้ใช้เลื่อนลง :)
Joaquim Ley

1

ฉันใช้ลิงค์ที่ให้มาโดย @STAR_ZERO และใช้งานได้ดี สำหรับผู้ที่มีปัญหากับปุ่มย้อนกลับคุณสามารถจัดการได้ในโฮสต์กิจกรรม / การนำทางเช่นนี้

override fun onBackPressed() {
        if(navController.currentDestination!!.id!=R.id.homeFragment){
            navController.navigate(R.id.homeFragment)
        }else{
            super.onBackPressed()
        }
    }

เพียงตรวจสอบว่าปลายทางปัจจุบันเป็นส่วนของรูท / โฮมของคุณหรือไม่ (โดยปกติจะเป็นส่วนแรกในมุมมองการนำทางด้านล่าง) ถ้าไม่ใช่ให้กลับไปที่ส่วนถ้าใช่ให้ออกจากแอปเท่านั้นหรือทำอะไรก็ได้

Btw จำเป็นที่จะต้องแก้ปัญหานี้จะทำงานร่วมกับการเชื่อมโยงการแก้ปัญหาดังกล่าวข้างต้นให้บริการโดย STAR_ZERO ใช้keep_state_fragment


idk ทำไม แต่ currentDestination !!. id ให้ค่าเท่ากันสำหรับทั้ง 3 ส่วน
Avishek Das

1

วิธีแก้ปัญหาโดย @ piotr-prus ช่วยฉันได้ แต่ฉันต้องเพิ่มการตรวจสอบปลายทางปัจจุบัน:

if (navController.currentDestination?.id == resId) {
    return       //do not navigate
}

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


ฉันดีใจที่โซลูชันของฉันได้ผลสำหรับคุณ ฉันกำลังตรวจสอบ menuItem ปัจจุบันอยู่ด้านล่าง NavigationView ก่อนที่จะเรียก Fragment จาก backStack โดยใช้: bottomNavigationView.setOnNavigationItemSelectedListener
Piotr Prus

0

ตามที่ Android เอกสาร ,

เมื่อสร้าง NavHostFragment โดยใช้ FragmentContainerView หรือหากเพิ่ม NavHostFragment ในกิจกรรมของคุณด้วยตนเองผ่าน FragmentTransaction การพยายามดึง NavController ใน onCreate () ของกิจกรรมผ่าน Navigation.findNavController (Activity, @IdRes int) จะล้มเหลว คุณควรดึง NavController โดยตรงจาก NavHostFragment แทน

เปลี่ยน nav_host_fragment ของคุณเป็น -> FragmentContainerView และดึง navController

    val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController

คุณสามารถรักษาสถานะแฟรกเมนต์ด้วยส่วนประกอบการนำทางโดยใช้แนวทางนี้

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