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