새 NavController와 함께 BottomNavigationView를 사용할 때 조각을 유지하는 방법이 있습니까?


96

새 탐색 구성 요소를 사용하려고합니다. navController와 함께 BottomNavigationView를 사용합니다. NavigationUI.setupWithNavController (bottomNavigation, navController)

그러나 프래그먼트를 전환 할 때 이전에 사용 된 경우에도 매번 파괴 / 생성됩니다.

BottomNavigationView에 대한 주요 프래그먼트 링크를 유지하는 방법이 있습니까?


2
issuetracker.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>

활동 레이아웃

NavHostFragment 대신 CustomNavHostFragment를 사용하십시오.

<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다시 생성되지 않고 재 사용됨) 탐색에 문제가 있습니다. 각각의 menuitemin bottomnavigationview은 기본으로 간주됩니다. 따라서 BACK를 누르면 앱이 종료되거나 최상위 구성 요소로 이동합니다. 더 나은 해결책은 YouTube 앱처럼 작동하는 것입니다. (1) 홈을 탭하십시오. (2) 트렌드 탭; (3)받은 편지함을 누릅니다. (4) 트렌드 탭; (5) 탭 BACK-받은 편지함으로 이동합니다. (6) 탭 BACK-홈으로 이동합니다. (7) 탭 BACK-앱이 있습니다. 요약하면 BACK' menuitems' 사이의 YouTube 기능은 항목을 반복하지 않고 최신 기능 으로 돌아갑니다.
— miguelt

3
뒤로 버튼 기능을 유지하는 방법은 무엇입니까? 뒤로 버튼을 누르면 앱이 종료됩니다.
— Francis

5
@ jL4, 동일한 문제가 발생했습니다. 아마도 app:navGraph활동의 NavHostFragment에서 제거 하는 것을 잊었을 것입니다 .
— Paul Chernenko

6
Google이이 기능을 즉시 사용하지 않는 이유는 무엇입니까?
— Arsenius 2019

55
NavigationComponent는 또 다른 문제가 아닌 솔루션이되어야하므로 프로그래머는 이와 같은 해결 방법을 만들어야합니다.
— 아리 아궁

24

Google 샘플 링크 NavigationExtensions를 애플리케이션에 복사하고 예제별로 구성하면됩니다. 잘 작동합니다.


1
그러나 하단 탐색에서만 작동합니다. 일반 내비게이션이있는 경우 문제가 있습니다
— Georgiy Chebotarev

나는 동일한 문제에 직면하고 있으며 모든 솔루션에 kotlin 코드가 포함되어 있지만 프로젝트에서 Java를 사용하는 것이 두렵습니다. 누군가가 Java 에서이 문제를 해결하는 방법을 도울 수 있습니까?
— CodeRED 혁신

@CodeREDInnovations 나도 kotlin 파일로 컴파일을 시도하고 성공했지만 탐색은 다음과 같이 유지됩니다. imgur.com/a/DcsKqPr 그것은 "대체"하지 않습니다. 그 외에도 앱이 무거워지고 고착되는 것 같습니다.
— Acauã Pitta

@ AcauãPitta Java에서 솔루션을 찾았습니까?
— developKinberg

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

이것을 사용하는 방법과 장소? 단편 A에서 B로 이동 한 다음 B에서 A로 이동하면 어떻게 oncraeteView () 및 onViewCreated () 메서드를 호출하지 않고 간단히 갈 수 있습니까? 단편 A
— Kishan Solanki

@UsamaSaeedUS : 코드, 사용 방법을 공유해 주시겠습니까? Kishan이 언급 한 것과 같습니다.
— Code_Life

2

인수 전달에 문제가있는 경우 다음을 추가하십시오.

fragment.arguments = args

클래스 KeepStateNavigator


1

지금은 사용할 수 없습니다.

해결 방법으로 가져온 모든 데이터를 뷰 모델에 저장하고 조각을 다시 만들 때 해당 데이터를 읽을 수 있도록 할 수 있습니다. 활동 컨텍스트를 사용하여보기를 가져와야합니다.

LiveData를 사용하여 데이터 수명주기를 인식 할 수 있도록 만들 수 있습니다.


1
이것은 실제로 사실이며 데이터>보기이지만 문제는보기 상태를 유지하려는 경우입니다 (예 : 여러 페이지가로드되고 사용자가 아래로 스크롤했습니다. :).
— Joaquim Ley

1

@STAR_ZERO에서 제공하는 링크를 사용했는데 제대로 작동합니다. 뒤로 버튼에 문제가있는 분들은 이와 같이 활동 / 탐색 호스트에서 처리 할 수 ​​있습니다.

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

현재 대상이 루트 / 홈 프래그먼트 (일반적으로 하단 탐색보기의 첫 번째 프래그먼트)인지 확인하고, 그렇지 않은 경우 프래그먼트로 다시 이동하고, 예인 경우 앱을 종료하거나 원하는대로 수행합니다.

Btw,이 솔루션은 keep_state_fragment를 사용하여 STAR_ZERO에서 제공 한 위의 솔루션 링크와 함께 작동해야합니다 .


idk 왜하지만 currentDestination !!. id는 항상 3 개의 조각 모두에 대해 동일한 값을 제공합니다
— Avishek Das

1

@ piotr-prus가 제공 한 솔루션이 도움이되었지만 현재 대상 확인을 추가해야했습니다.

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

이 검사가 없으면 현재 대상은 백 스택에서 찾을 수 없기 때문에 실수로 탐색하면 다시 생성됩니다.


내 솔루션이 효과가있어 기쁩니다. 실제로 backStack에서 조각을 호출하기 전에 bottomNavigationView에서 현재 menuItem을 확인하고 있습니다. bottomNavigationView.setOnNavigationItemSelectedListener
— Piotr Prus

0

Android 문서 에 따르면 ,

FragmentContainerView를 사용하여 NavHostFragment를 만들거나 FragmentTransaction을 통해 NavHostFragment를 활동에 수동으로 추가하는 경우 Navigation.findNavController (Activity, @IdRes int)를 통해 활동의 onCreate ()에서 NavController를 검색하려는 시도가 실패합니다. 대신 NavHostFragment에서 직접 NavController를 검색해야합니다.

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.