Có cách nào để giữ cho phân đoạn tồn tại khi sử dụng BottomNavigationView với NavController mới không?


95

Tôi đang cố gắng sử dụng thành phần điều hướng mới. Tôi sử dụng BottomNavigationView với navController: NavigationUI.setupWithNavController (bottomNavigation, navController)

Nhưng khi tôi chuyển đổi các phân mảnh, chúng sẽ bị phá hủy / tạo mỗi lần ngay cả khi chúng đã được sử dụng trước đó.

Có cách nào để duy trì liên kết các phân đoạn chính của chúng ta với BottomNavigationView không?


2
Theo nhận xét trên Issuetracker.google.com/issues/80029773, có vẻ như vấn đề này sẽ được giải quyết cuối cùng. Tuy nhiên, tôi cũng rất tò mò nếu mọi người có một giải pháp rẻ tiền không liên quan đến việc từ bỏ thư viện để thực hiện công việc này trong thời gian tạm thời.
Jimmy Alexander

Câu trả lời:


68

Thử đi.

Hoa tiêu

Tạo điều hướng tùy chỉnh.

@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

Tạo NavHostFragment tùy chỉnh.

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

navigation.xml

Sử dụng thẻ tùy chỉnh thay vì thẻ phân đoạn.

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

bố cục hoạt động

Sử dụng CustomNavHostFragment thay vì 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>

Cập nhật

Tôi đã tạo dự án mẫu. liên kết

Tôi không tạo NavHostFragment tùy chỉnh. Tôi sử dụng navController.navigatorProvider += navigator.


3
Trong khi giải pháp này hoạt động ( fragmentsđược sử dụng lại, không được tạo lại), có một vấn đề với điều hướng. Mỗi menuitemtrong bottomnavigationviewđược coi là chính - do đó, khi BACKđược nhấn, ứng dụng sẽ kết thúc (hoặc chuyển đến thành phần trên cùng). Giải pháp tốt hơn sẽ là hoạt động giống như ứng dụng YouTube: (1) Nhấn vào Trang chủ; (2) Nhấn vào Xu hướng; (3) Nhấn vào Hộp thư đến; (4) Nhấn vào Xu hướng; (5) Nhấn BACK- chuyển đến Hộp thư đến; (6) Nhấn BACK- chuyển đến Trang chủ; (7) Nhấn BACK- ứng dụng tồn tại. Tóm lại, BACKchức năng của YouTube giữa " menuitems" trở lại gần đây nhất, không lặp lại các mục.
miguelt

3
Làm thế nào để giữ chức năng của nút quay lại? Ứng dụng đang hoàn thiện khi nhấn nút quay lại.
Francis

5
@ jL4, tôi gặp vấn đề tương tự, có thể bạn đã quên xóa app:navGraphkhỏi NavHostFragment trong hoạt động của mình.
Paul Chernenko

6
Tại sao google không loại bỏ chức năng này?
Arsenius

55
NavigationComponent sẽ trở thành một giải pháp chứ không trở thành một vấn đề khác nên lập trình viên phải tạo ra cách giải quyết như thế này.
Arie Agung

24

Liên kết mẫu của Google Chỉ cần sao chép NavigationExtensions vào ứng dụng của bạn và định cấu hình bằng ví dụ. Hoạt động tuyệt vời.


1
Nhưng nó chỉ hoạt động để điều hướng phía dưới. Nếu bạn có một điều hướng chung, bạn gặp sự cố
Georgiy Chebotarev

Tôi đang gặp phải vấn đề tương tự và mọi giải pháp tôi kiểm tra đều chứa mã kotlin nhưng tôi sợ rằng tôi sử dụng Java trong dự án của mình. Ai đó có thể giúp tôi cách khắc phục sự cố này trong java.
CodeRED Đổi mới

@CodeREDInnovations tôi cũng vậy, mặc dù tôi đã thử và biên dịch thành công với tệp kotlin, điều hướng vẫn như thế này: imgur.com/a/DcsKqPr nó không "thay thế", bên cạnh đó, có vẻ như ứng dụng trở nên nặng hơn và đơ.
Acauã Pitta

11

Sau nhiều giờ nghiên cứu, tôi đã tìm ra giải pháp. Nó luôn ở ngay trước mặt chúng tôi :) Có một chức năng: popBackStack(destination, inclusive)điều hướng đến điểm đến nhất định nếu được tìm thấy trong backStack. Nó trả về Boolean, vì vậy chúng tôi có thể điều hướng đến đó theo cách thủ công nếu bộ điều khiển không tìm thấy phân mảnh.

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

Làm thế nào và ở đâu để sử dụng cái này? Nếu tôi điều hướng từ Phân đoạn A đến B, thì Từ B đến A, làm cách nào tôi có thể đi mà không gọi các phương thức oncraeteView () và onViewCreate (), nói ngắn gọn là không cần khởi động lại phân đoạn A
Kishan Solanki

@UsamaSaeedUS: Bạn có thể vui lòng chia sẻ mã, cách sử dụng nó? Giống như những gì được đề cập bởi Kishan.
Code_Life

2

Nếu bạn gặp khó khăn khi chuyển đối số, hãy thêm:

fragment.arguments = args

Trong lớp KeepStateNavigator


1

Hiện không có sẵn.

Một giải pháp khác là bạn có thể lưu trữ tất cả dữ liệu đã tìm nạp của mình vào mô hình chế độ xem và có sẵn dữ liệu đó khi bạn tạo lại phân đoạn. Đảm bảo rằng bạn có được chế độ xem bằng ngữ cảnh hoạt động.

Bạn có thể sử dụng LiveData để làm cho dữ liệu có thể quan sát được trong vòng đời dữ liệu của bạn


1
Điều này thực sự đúng, và data> view nhưng vấn đề là khi bạn muốn giữ trạng thái view, ví dụ: nhiều trang được tải và người dùng đã cuộn xuống :).
Joaquim Ley

1

Tôi đã sử dụng liên kết do @STAR_ZERO cung cấp và nó hoạt động tốt. Đối với những người gặp sự cố với nút quay lại, bạn có thể xử lý nó trong máy chủ hoạt động / điều hướng như thế này.

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

Chỉ cần kiểm tra xem đích hiện tại có phải là phân đoạn gốc / nhà của bạn hay không (thường là phân đoạn đầu tiên trong chế độ xem điều hướng dưới cùng), nếu không, chỉ cần điều hướng trở lại phân đoạn, nếu có, chỉ cần thoát ứng dụng hoặc làm bất cứ điều gì bạn muốn.

Btw, giải pháp này cần hoạt động cùng với liên kết giải pháp ở trên do STAR_ZERO cung cấp, sử dụng keep_state_fragment .


idk tại sao nhưng hiện tạiDestination !!. id luôn cho tôi các giá trị giống nhau cho cả 3 đoạn
Avishek Das

1

Giải pháp do @ piotr-prus cung cấp đã giúp tôi, nhưng tôi phải thêm một số kiểm tra điểm đến hiện tại:

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

không có kiểm tra này, đích hiện tại sẽ tạo lại nếu bạn điều hướng nhầm đến nó, vì nó sẽ không được tìm thấy trong ngăn xếp phía sau.


Tôi rất vui vì giải pháp của tôi đã hiệu quả với bạn. Tôi thực sự đang kiểm tra menuItem hiện tại trong bottomNavigationView trước khi gọi đoạn từ backStack bằng: bottomNavigationView.setOnNavigationItemSelectedListener
Piotr Prus

0

Theo Tài liệu Android ,

Khi tạo NavHostFragment bằng FragmentContainerView hoặc nếu thêm NavHostFragment vào hoạt động của bạn theo cách thủ công thông qua FragmentTransaction, cố gắng truy xuất NavController trong onCreate () của Hoạt động thông qua Navigation.findNavController (Activity, int @IdRes) sẽ không thành công. Thay vào đó, bạn nên truy xuất NavController trực tiếp từ NavHostFragment.

Thay đổi nav_host_fragment của bạn thành -> FragmentContainerView và truy xuất navController

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

Bạn có thể duy trì trạng thái phân mảnh với các Thành phần Điều hướng bằng cách sử dụng phương pháp này

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.