IllegalArgumentException : 탐색 대상 xxx가이 NavController에 알려지지 않았습니다.


139

한 Fragment에서 다른 Fragment로 이동하려고 할 때 새로운 Android 탐색 아키텍처 구성 요소에 문제 가 있습니다.이 이상한 오류가 발생합니다.

java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController

이 특정 탐색을 제외하고 다른 모든 탐색은 잘 작동합니다.

findNavController()Fragment의 기능을 사용 하여 NavController.

도움을 주시면 감사하겠습니다.


더 나은 이해를 위해 몇 가지 코드를 제공하십시오.
Alex

12
이것은 나에게도 일어나고 있습니다.
Eury Pérez Beltré

지금까지이 버그의 발생률은 라이브러리의 최신 릴리스에서 감소되었지만 라이브러리가 아직 잘 문서화되지 않은 것 같습니다.
Jerry Okafor

답변:


76

제 경우에는 사용자가 동일한 뷰를 매우 빠르게 두 번 클릭하면이 충돌이 발생합니다. 따라서 여러 번의 빠른 클릭을 방지하기 위해 일종의 논리를 구현해야합니다. 매우 성가 시지만 필요한 것 같습니다.

이를 방지하는 방법에 대한 자세한 내용은 여기에서 확인할 수 있습니다. Android에서 더블 클릭을 방지하는 버튼

2019 년 3 월 19 일 편집 : 좀 더 명확히하기 위해이 충돌은 "동일한 뷰를 매우 빠르게 두 번 클릭"하는 것만으로 만 재현 할 수있는 것은 아닙니다. 또는 두 손가락을 사용하고 동시에 두 개 (또는 그 이상의)보기를 클릭 할 수 있습니다. 여기서 각보기에는 수행 할 고유 한 탐색이 있습니다. 이것은 항목 목록이있을 때 특히 쉽게 수행 할 수 있습니다. 다중 클릭 방지에 대한 위의 정보가이 경우를 처리합니다.

2020 년 4 월 16 일 편집 : 위의 Stack Overflow 게시물을 읽는 데별로 관심이없는 경우를 대비하여 지금까지 오랫동안 사용해온 내 자신의 (Kotlin) 솔루션을 포함하고 있습니다.

OnSingleClickListener.kt

class OnSingleClickListener : View.OnClickListener {

    private val onClickListener: View.OnClickListener

    constructor(listener: View.OnClickListener) {
        onClickListener = listener
    }

    constructor(listener: (View) -> Unit) {
        onClickListener = View.OnClickListener { listener.invoke(it) }
    }

    override fun onClick(v: View) {
        val currentTimeMillis = System.currentTimeMillis()

        if (currentTimeMillis >= previousClickTimeMillis + DELAY_MILLIS) {
            previousClickTimeMillis = currentTimeMillis
            onClickListener.onClick(v)
        }
    }

    companion object {
        // Tweak this value as you see fit. In my personal testing this
        // seems to be good, but you may want to try on some different
        // devices and make sure you can't produce any crashes.
        private const val DELAY_MILLIS = 200L

        private var previousClickTimeMillis = 0L
    }

}

ViewExt.kt

fun View.setOnSingleClickListener(l: View.OnClickListener) {
    setOnClickListener(OnSingleClickListener(l))
}

fun View.setOnSingleClickListener(l: (View) -> Unit) {
    setOnClickListener(OnSingleClickListener(l))
}

HomeFragment.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    settingsButton.setOnSingleClickListener {
        // navigation call here
    }
}

23
두 손가락을 사용하고 동시에 2 개의 뷰를 클릭하는 것에 대한 편집! 그것이 제게 핵심이며 문제를 쉽게 재현 할 수 있도록 도와주었습니다. 그 정보로 훌륭한 업데이트.
Richard Le Mesurier 2019

디버그 단계에서 앱이 계속 실행되기를 기다리는 동안 클릭이 발생했습니다. 는 IDE에 연속으로 두 개의 연속 클릭의 또 다른 사례처럼 보인다
마르코

1
감사합니다. 나에게 몇 가지 충돌과 머리 긁힘을 저장했습니다 :)
user2672052

58

currentDestination내비게이션을 호출하기 전에 확인 하는 것이 도움이 될 수 있습니다.

예를 들어, 탐색 그래프 fragmentA및 에 두 개의 조각 대상이 있고 fragmentB에서 fragmentA까지의 작업이 하나만있는 경우입니다 fragmentB. 전화 navigate(R.id.action_fragmentA_to_fragmentB)IllegalArgumentException이미에있을 때 발생합니다 fragmentB. 따라서 currentDestination탐색하기 전에 항상 확인해야합니다 .

if (navController.currentDestination?.id == R.id.fragmentA) {
    navController.navigate(R.id.action_fragmentA_to_fragmentB)
}

3
인수가있는 작업으로 탐색하는 검색 앱이 있습니다. 따라서 currentDestination에서 자신으로 이동할 수 있습니다. navController.currentDestination == navController.graph.node 제외하고 동일한 작업을 끝냈습니다. 그래도 좀 더럽게 느껴졌고 이걸 할 필요가 없다고 느낍니다.
Shawn Maybush

84
도서관은 우리에게이 검사를 강요해서는 안됩니다. 정말 말도 안됩니다.
DaniloDeQueiroz

나는 같은 문제가 있었다. 나는 EditText와 데이터베이스에 EditText의 내용을 저장하는 '저장'버튼을 가지고 있습니다. 항상 '저장'버튼을 누르면 충돌이 발생했습니다. 그 이유는 '저장'버튼을 누를 수 있으려면 뒤로 버튼을 눌러 온 스크린 키보드를 제거해야한다는 사실 때문이라고 생각합니다.
The Fox

오류 상태를 확인하지만 문제가 해결되지는 않습니다. 흥미롭게도이 조건은 원치 않는 이유로 탐색 백 스택이 비어있는 경우 참입니다.
Mike76

1
iOS에서도 버튼을 여러 번 누르면 여러 ViewController가 푸시되는 경우가 있습니다. Android와 iOS 모두이 문제가 있다고 생각합니다.
coolcool1994

47

내비게이션 컨트롤러의 현재 목적지에서 요청 된 작업을 확인할 수 있습니다.

UPDATE 는 안전한 탐색을 위해 전역 작업을 추가했습니다.

fun NavController.navigateSafe(
        @IdRes resId: Int,
        args: Bundle? = null,
        navOptions: NavOptions? = null,
        navExtras: Navigator.Extras? = null
) {
    val action = currentDestination?.getAction(resId) ?: graph.getAction(resId)
    if (action != null && currentDestination?.id != action.destinationId) {
        navigate(resId, args, navOptions, navExtras)
    }
}

1
이 솔루션은 currentDestination의 작업 목록 외부에 정의 된 작업에는 작동하지 않습니다 . 전역 작업이 정의되어 있고 해당 작업을 사용하여 탐색한다고 가정합니다. 작업이 currentDestination의 <action> 목록에 정의되어 있지 않기 때문에 실패합니다. 같은 수표를 추가하면 currentDestination?.getAction(resId) != null || currentDestination?.id != resId문제가 해결되지만 모든 경우에 적용되지 않을 수도 있습니다.
wchristiansen

@wchristiansen, 메모 감사합니다. 나는 세계 행동의 사용과 코드를 업데이 트했습니다
알렉스 너트

@AlexNuts 좋은 대답. 제거 할 수 있다고 생각합니다 ?: graph.getAction(resId)-> currentDestination?.getAction(resId)전역 또는 비 전역 작업 모두에 대한 작업을 반환합니다 (테스트했습니다). 오히려 전달> - 당신이 안전 인수의 사용 한 경우 또한, 더 나은 것 navDirections: NavDirections보다 더 resIdargs별도.
Wess

@AlexNuts 참고이 솔루션은 현재 대상과 동일한 대상으로의 탐색을 지원하지 않습니다. 번들 Y가있는 대상 X에서 번들 Z가있는 대상 X로 이동하는 Iow는 불가능합니다.
Wess

18

또한 Fragment B의 ViewPager가있는 Fragment A가 있고 B에서 C로 이동하려고 할 때 발생할 수 있습니다.

ViewPager에서 조각은 A의 대상이 아니기 때문에 그래프는 사용자가 B에 있다는 것을 알 수 없습니다.

해결책은 B에서 ADirections를 사용하여 C로 이동하는 것입니다.


이 경우 충돌은 매번 발생하는 것이 아니라 드물게 발생합니다. 그것을 해결하는 방법?
Srikar Reddy

당신은 navGraph 내부 전역 조치를 추가하고 이동하는 데 사용할 수 있습니다
아브라함 매튜

1
B는 정확한 부모를 알 필요가 없기 때문에 ADirections와 같은 인터페이스를 통해 사용하는 것이 더 좋을 것 (parentFragment as? XActionListener)?.Xaction()입니다. 도움이된다면이 함수를 지역 변수로 유지할 수 있습니다
hmac

나는이 같은 문제가 당신이를 설명하는 샘플 코드를 공유하시기 바랍니다 수 있습니다
Ikhiloya Imokhai을

누구든지 샘플 코드를 plz 할 수 있습니다. 나는 같은 문제에 갇혀 있습니다. 조각과 tabfragment가 있습니다
Usman Zafer

13

충돌을 방지하기 위해 내가 한 일은 다음과 같습니다.

나는 BaseFragment를 가지고 있으며, 거기에 다음 fun을 통해 알 수 있도록 이것을 추가 destination했습니다 currentDestination.

fun navigate(destination: NavDirections) = with(findNavController()) {
    currentDestination?.getAction(destination.actionId)
        ?.let { navigate(destination) }
}

내가 SafeArgs 플러그인을 사용하고 있다는 점은 주목할 가치가 있습니다.


12

제 경우에는 위로 이동하기 위해 맞춤 뒤로 버튼을 사용했습니다. onBackPressed()다음 코드 대신 전화 했습니다.

findNavController(R.id.navigation_host_fragment).navigateUp()

이로 인해 IllegalArgumentException발생했습니다. navigateUp()대신 방법 을 사용하도록 변경 한 후 다시 충돌이 발생하지 않았습니다.


나는 onBackPressed와 이것의 차이가 무엇인지 이해하지 못합니다. 여전히 시스템 뒤로 버튼으로 갇혀 있고 그것을 무시하고 이것을 대체하는 것은 미친 것 같습니다
Daniel Wilson

2
미친 것 같다는 데 동의합니다. 안드로이드 내비게이션 아키텍처 구성 요소에서 내가 만난 많은 것들이 약간 미쳤습니다. 너무 엄격하게 IMO로 설정되었습니다. 너무 많은 골칫거리를 만들기 때문에 우리 프로젝트를 직접 구현하는 것에 대해 생각하고 있습니다
Neil

나를 위해 작동하지 않습니다 ... 여전히 같은 오류가 발생합니다.
Otziii

5

TL; DRnavigate 통화를 try-catch(간단한 방법)으로 감싸 거나 navigate짧은 시간에 한 번의 통화 만 있는지 확인하십시오 . 이 문제는 사라지지 않을 것입니다. 앱에서 더 큰 코드 스 니펫을 복사하여 사용해보세요.

안녕하세요. 위의 몇 가지 유용한 답변을 바탕으로 확장 가능한 솔루션을 공유하고 싶습니다.

내 응용 프로그램에서이 충돌을 일으킨 코드는 다음과 같습니다.

@Override
public void onListItemClicked(ListItem item) {
    Bundle bundle = new Bundle();
    bundle.putParcelable(SomeFragment.LIST_KEY, item);
    Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);
}

버그를 쉽게 재현하는 방법은 새 화면 탐색에서 각 항목을 클릭하면 해결되는 항목 목록을 여러 손가락으로 탭하는 것입니다 (기본적으로 사람들이 언급 한 것과 동일 함-매우 짧은 시간에 두 번 이상의 클릭) ). 난 그것을 알아 챘다:

  1. 첫 번째 navigate호출은 항상 잘 작동합니다.
  2. 두 번째 및 navigate메서드 의 다른 모든 호출은 IllegalArgumentException.

내 관점에서이 상황은 매우 자주 나타날 수 있습니다. 코드를 반복하는 것은 나쁜 습관이며 항상 다음 해결책을 생각한 한 가지 영향을주는 것이 좋습니다.

public class NavigationHandler {

public static void navigate(View view, @IdRes int destination) {
    navigate(view, destination, /* args */null);
}

/**
 * Performs a navigation to given destination using {@link androidx.navigation.NavController}
 * found via {@param view}. Catches {@link IllegalArgumentException} that may occur due to
 * multiple invocations of {@link androidx.navigation.NavController#navigate} in short period of time.
 * The navigation must work as intended.
 *
 * @param view        the view to search from
 * @param destination destination id
 * @param args        arguments to pass to the destination
 */
public static void navigate(View view, @IdRes int destination, @Nullable Bundle args) {
    try {
        Navigation.findNavController(view).navigate(destination, args);
    } catch (IllegalArgumentException e) {
        Log.e(NavigationHandler.class.getSimpleName(), "Multiple navigation attempts handled.");
    }
}

}

따라서 위의 코드는 다음과 같이 한 줄로만 변경됩니다.

Navigation.findNavController(recyclerView).navigate(R.id.action_listFragment_to_listItemInfoFragment, bundle);

이에:

NavigationHandler.navigate(recyclerView, R.id.action_listFragment_to_listItemInfoFragment, bundle);

심지어 조금 더 짧아졌습니다. 코드는 충돌이 발생한 정확한 위치에서 테스트되었습니다. 더 이상 경험하지 않았으며 동일한 실수를 더 방지하기 위해 다른 탐색에 동일한 솔루션을 사용할 것입니다.

어떤 생각이라도 환영합니다!

충돌의 정확한 원인

여기서는 method를 사용할 때 동일한 탐색 그래프, 탐색 컨트롤러 및 백 스택으로 작업 Navigation.findNavController합니다.

여기에서는 항상 동일한 컨트롤러와 그래프를 얻습니다. navigate(R.id.my_next_destination)를 호출 하면 UI가 아직 업데이트되지 않은 동안 거의 즉시 백 스택이 변경 됩니다. 충분히 빠르지는 않지만 괜찮습니다. 백 스택이 변경된 후 내비게이션 시스템은 두 번째 navigate(R.id.my_next_destination)호출을 받습니다 . 백 스택이 변경되었으므로 이제 스택의 최상위 조각을 기준으로 작동합니다. 맨 위 조각은를 사용하여 탐색하는 조각 R.id.my_next_destination이지만 ID가있는 다음 대상은 포함하지 않습니다 R.id.my_next_destination. 따라서 IllegalArgumentException조각이 아무것도 모르는 ID 때문에 얻을 수 있습니다.

이 정확한 오류는 NavController.javamethod 에서 찾을 수 있습니다 findDestination.


4

제 경우에는 프래그먼트 내부의 viewpager프래그먼트 중 하나를 viewpager. viewpager(상위 단편이었다) 단편 탐색 XML에 첨가하고, 그 작업이 첨가되지 않은 viewpager부모 단편.

nav.xml
//reused fragment
<fragment
    android:id="@+id/navigation_to"
    android:name="com.package.to_Fragment"
    android:label="To Frag"
    tools:layout="@layout/fragment_to" >
    //issue got fixed when i added this action to the viewpager parent also
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>
....
// viewpager parent fragment
<fragment
    android:id="@+id/toViewAll"
    android:name="com.package.ViewAllFragment"
    android:label="to_viewall_fragment"
    tools:layout="@layout/fragment_view_all">

아래와 같이 상위 뷰 페이지 조각에 작업을 추가하여 문제를 해결했습니다.

nav.xml
//reused fragment
<fragment
    android:id="@+id/navigation_to"
    android:name="com.package.to_Fragment"
    android:label="To Frag"
    tools:layout="@layout/fragment_to" >
    //issue got fixed when i added this action to the viewpager parent also
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>
....
// viewpager parent fragment
<fragment
    android:id="@+id/toViewAll"
    android:name="com.package.ViewAllFragment"
    android:label="to_viewall_fragment"
    tools:layout="@layout/fragment_view_all"/>
    <action android:id="@+id/action_to_to_viewall"
        app:destination="@+id/toViewAll"/>
</fragment>

4

오늘

def navigationVersion = "2.2.1"

문제가 여전히 존재합니다. Kotlin에 대한 나의 접근 방식은 다음과 같습니다.

// To avoid "java.lang.IllegalArgumentException: navigation destination is unknown to this NavController", se more https://stackoverflow.com/q/51060762/6352712
fun NavController.navigateSafe(
    @IdRes destinationId: Int,
    navDirection: NavDirections,
    callBeforeNavigate: () -> Unit
) {
    if (currentDestination?.id == destinationId) {
        callBeforeNavigate()
        navigate(navDirection)
    }
}

fun NavController.navigateSafe(@IdRes destinationId: Int, navDirection: NavDirections) {
    if (currentDestination?.id == destinationId) {
        navigate(navDirection)
    }
}

4

탐색을 요청하는 조각 이이 요점에서 가져온 현재 대상인지 탐색하기 전에 확인할 수 있습니다 .

기본적으로 나중에 조회 할 수 있도록 조각에 태그를 설정합니다.

/**
 * Returns true if the navigation controller is still pointing at 'this' fragment, or false if it already navigated away.
 */
fun Fragment.mayNavigate(): Boolean {

    val navController = findNavController()
    val destinationIdInNavController = navController.currentDestination?.id
    val destinationIdOfThisFragment = view?.getTag(R.id.tag_navigation_destination_id) ?: destinationIdInNavController

    // check that the navigation graph is still in 'this' fragment, if not then the app already navigated:
    if (destinationIdInNavController == destinationIdOfThisFragment) {
        view?.setTag(R.id.tag_navigation_destination_id, destinationIdOfThisFragment)
        return true
    } else {
        Log.d("FragmentExtensions", "May not navigate: current destination is not the current fragment.")
        return false
    }
}

R.id.tag_navigation_destination_id 고유한지 확인하기 위해 ids.xml에 추가해야하는 ID입니다. <item name="tag_navigation_destination_id" type="id" />

버그 및 솔루션에 대한 자세한 정보, "두려운"…이 NavController에 알려지지 않은 문제 해결 "의navigateSafe(...) 확장 방법


나는이 문제에 대한 몇 가지 다른 해결책을 연구했으며, 당신의 것이 가장 훌륭합니다. 그것에 대한 너무 작은 사랑을보고 슬프게합니다
Luke

1
NAV_DESTINATION_ID다음과 같은 대신 고유 식별자를 만드는 것이 유용 할 수 있습니다. stackoverflow.com/a/15021758/1572848
William Reed

예, 답변을 업데이트했습니다
Frank

태그의 출처는 어디이며 왜 필요한가요? 탐색 구성 요소의 실제 ID가 .NET과 일치하지 않는 문제가 있습니다 R.id.
riezebosch

R.id.tag_navigation_destination_id고유한지 확인하기 위해 ids.xml에 추가해야하는 ID 일뿐입니다. <item name="tag_navigation_destination_id" type="id" />
Frank

3

제 경우에는 여러 탐색 그래프 파일이 있었고 한 탐색 그래프 위치에서 다른 탐색 그래프의 대상으로 이동하려고했습니다.

이를 위해 다음과 같이 첫 번째 탐색 그래프에 두 번째 탐색 그래프를 포함해야합니다.

<include app:graph="@navigation/included_graph" />

그리고 이것을 당신의 행동에 추가하십시오 :

<action
        android:id="@+id/action_fragment_to_second_graph"
        app:destination="@id/second_graph" />

어디에 second_graph:

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

두 번째 그래프에서.

여기에 더 많은 정보


2

제 경우에는 스플래시 화면 후에 Single TopClear Task옵션을 사용 하여 탐색 작업을 수행했기 때문에 버그가 발생했습니다 .


1
그러나 clearTask는 더 이상 사용되지 않으므로 대신 popUpTo ()를 사용해야합니다.
Jerry Okafor

@ Po10cio 그 플래그 중 어느 것도 필요하지 않았으며 방금 제거했으며 수정되었습니다.
Eury Pérez Beltré

2

getSupportFragmentManager().beginTransaction().replace( )내 코드 어딘가에서 동시에 탐색 서랍을 사용했기 때문에 동일한 오류가 발생했습니다 .

이 조건 (대상인지 테스트)을 사용하여 오류를 제거했습니다.

if (Navigation.findNavController(v).getCurrentDestination().getId() == R.id.your_destination_fragment_id)
Navigation.findNavController(v).navigate(R.id.your_action);

제 경우에는 탐색 창 옵션을 클릭 할 때 이전 오류가 발생했습니다. 기본적으로 위의 코드는 오류를 숨겼습니다. 내 코드에서 어딘가에서 getSupportFragmentManager().beginTransaction().replace( )The condition을 사용하여 탐색을 사용했기 때문입니다.

 if (Navigation.findNavController(v).getCurrentDestination().getId() ==
  R.id.your_destination_fragment_id) 

(Navigation.findNavController(v).getCurrentDestination().getId()항상 집 조각에 poiting 했기 때문에 도달하지 못했습니다 . Navigation.findNavController(v).navigate(R.id.your_action)모든 탐색 작업에 대해 그래프 컨트롤러 기능 만 사용 하거나 탐색 해야합니다 .


2

즉시 클릭 제어를위한 상용구 코드 대신 탐색 전에 확인을 넣어 동일한 문제를 해결했습니다.

 if (findNavController().currentDestination?.id == R.id.currentFragment) {
        findNavController().navigate(R.id.action_current_next)}
/* Here R.id.currentFragment is the id of current fragment in navigation graph */

이 대답에 따르면

https://stackoverflow.com/a/56168225/7055259



1

클래스 이름을 변경 한 후이 예외가 발생했습니다. 예를 들면 : 나는라는 클래스를 가지고 FragmentA@+is/fragment_a탐색 그래프와 FragmentB함께를 @+id/fragment_b. 그럼 삭제 FragmentA하고 이름 FragmentBFragmentA. 그래서의 노드 후 FragmentA여전히 탐색 그래프에 머물렀고, 및 android:nameFragmentB의 노드 이름이 바뀌 었습니다 path.to.FragmentA. 나는 같고 android:name다른 두 개의 노드를 가지고 있었고 android:id필요한 동작은 제거 된 클래스의 노드에 정의되었습니다.


1

뒤로 버튼을 두 번 누르면 나에게 발생합니다. 첫째, 절편에서 KeyListener와 재정 KeyEvent.KEYCODE_BACK. OnResumeFragment의 이름이 지정된 함수에 아래 코드를 추가하면 이 질문 / 문제가 해결됩니다.

  override fun onResume() {
        super.onResume()
        view?.isFocusableInTouchMode = true
        view?.requestFocus()
        view?.setOnKeyListener { v, keyCode, event ->
            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BACK) {
                activity!!.finish()
                true
            }
            false
        }
    }

두 번째로 발생하고 상태가 첫 번째와 동일하면 adsurd기능을 사용할 수 있습니다. 이러한 상황을 분석해 봅시다.

  1. 먼저, FragmentA가 FragmentB로 이동 한 다음 FragmentB가 FragmentA로 이동 한 다음 뒤로 버튼을 누릅니다. 충돌이 나타납니다.

  2. 둘째, FragmentA는 FragmentB로 이동 한 다음 FragmentB는 FragmentC로 이동하고, FragmentC는 FragmentA로 이동 한 다음 뒤로 버튼을 누릅니다. 충돌이 나타납니다.

그래서 뒤로 버튼을 누르면 FragmentA가 FragmentB 또는 FragmentC로 돌아가서 로그인이 엉망이됩니다. 마지막으로 이름이 지정된 함수를 popBackStack탐색하는 대신 뒤로 사용할 수 있음을 발견했습니다 .

  NavHostFragment.findNavController(this@TeacherCloudResourcesFragment).
                        .popBackStack(
                            R.id.teacher_prepare_lesson_main_fragment,false
                        )

지금까지 문제가 실제로 해결되었습니다.


1

백 스택의 fragmentManager 컨트롤과 백 스택의 탐색 아키텍처 컨트롤을 혼합하면이 문제가 발생할 수도 있습니다.

예를 들어 원래 CameraX 기본 샘플은 아래와 같이 fragmentManager 백 스택 내비게이션을 사용했으며 내비게이션과 올바르게 상호 작용하지 않은 것처럼 보입니다.

// Handle back button press
        view.findViewById<ImageButton>(R.id.back_button).setOnClickListener {
            fragmentManager?.popBackStack()
        }

메인 프래그먼트 (이 경우 카메라 프래그먼트)에서 이동하기 전에이 버전으로 '현재 대상'을 로그 한 다음 메인 프래그먼트로 돌아갈 때 다시 로그하면 로그의 ID에서 ID가 똑같지 않다. 추측에 따르면 탐색은 조각으로 이동할 때 업데이트했고 fragmntManager는 뒤로 이동할 때 다시 업데이트하지 않았습니다. 로그에서 :

이전 : D / CameraXBasic : currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

이후 : D / CameraXBasic : currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@9807d8f

CameraX 기본 샘플의 업데이트 된 버전은 Navigation을 사용하여 다음과 같이 반환합니다.

 // Handle back button press
        view.findViewById<ImageButton>(R.id.back_button).setOnClickListener {
            Navigation.findNavController(requireActivity(), R.id.fragment_container).navigateUp()
        }

이것은 올바르게 작동하며 로그는 기본 조각으로 돌아 왔을 때 동일한 ID를 표시합니다.

이전 : D / CameraXBasic : currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

: D / CameraXBasic : currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

적어도 현재로서는 이야기의 교훈이 Navigation과 fragmentManager 탐색을 매우 신중하게 혼합하는 것이라고 생각합니다.


그럴듯하게 들리면 더 자세히 조사하겠습니다. 누구든지이 주장을 확인하거나 입증 할 수 있었습니까?
Jerry Okafor

@JerryOkafor-CameraX 샘플을 기반으로 작업 중이던 앱에서 테스트하고 확인했지만 다른 사람도 이것을 보았는지 확인하는 것이 좋습니다. 실제로 동일한 앱의 한 위치에서 '뒤로 탐색'을 놓쳤으므로 최근에도 다시 수정했습니다.
Mick

1

말도 안되는 방법이지만 매우 강력한 방법은 다음과 같습니다.

view?.findNavController()?.navigateSafe(action)

이 확장 만들기 :

fun NavController.navigateSafe(
    navDirections: NavDirections? = null
) {
    try {
        navDirections?.let {
            this.navigate(navDirections)
        }
    }
    catch (e:Exception)
    {
        e.printStackTrace()
    }
}

1

이 문제에는 여러 가지 이유가있을 수 있습니다. 제 경우에는 MVVM 모델을 사용하고 있었고 부울이 true 일 때 탐색을 위해 부울을 관찰했습니다-> 탐색하지 않으면 아무것도하지 않고 잘 작동했지만 여기에 한 가지 실수가 있습니다.

대상 조각에서 뒤로 버튼을 눌렀을 때 동일한 문제가 발생했습니다. 부울 값을 false로 변경하는 것을 잊었으므로 문제는 부울 객체였습니다. findNavController () 바로 다음에 호출했습니다.


1

일반적으로 이런 일이 발생하면 Charles Madere가 설명한 문제가 있습니다. 동일한 UI에서 두 개의 탐색 이벤트가 트리거되었습니다. 하나는 currentDestination을 변경하고 다른 하나는 currentDestination이 변경되어 실패했습니다. 두 번 탭하거나 findNavController.navigate를 호출하는 클릭 리스너로 두 개의보기를 클릭하면 이런 일이 발생할 수 있습니다.

따라서이 문제를 해결하려면 if-checks, try-catch를 사용하거나 관심이있는 경우 탐색하기 전에이를 확인하는 findSafeNavController ()를 사용할 수 있습니다. 또한이 문제를 잊지 않도록 보풀 검사 기능이 있습니다.

GitHub

문제를 자세히 설명하는 기사


1

너무 빨리 클릭하면 null 및 충돌이 발생합니다.

이를 위해 RxBinding lib를 사용할 수 있습니다. 클릭이 발생하기 전에 스로틀과 기간을 추가 할 수 있습니다.

 RxView.clicks(view).throttleFirst(duration, TimeUnit.MILLISECONDS)
            .subscribe(__ -> {
            });

Android의 제한에 대한 이 문서 가 도움이 될 수 있습니다. 건배!


1

recyclerview를 사용하는 경우 클릭에 클릭 리스너 쿨 다운을 추가하고 recyclerview xml 파일 사용 android:splitMotionEvents="false"


1
내 아래 답변을보세요
Crazy

1

이 트위터 스레드 에서 Ian Lake의 조언을 생각한 후 다음과 같은 접근 방식을 생각해 냈습니다. 데 NavControllerWrapper같은 정의 :

class NavControllerWrapper constructor(
  private val navController: NavController
) {

  fun navigate(
    @IdRes from: Int,
    @IdRes to: Int
  ) = navigate(
    from = from,
    to = to,
    bundle = null
  )

  fun navigate(
    @IdRes from: Int,
    @IdRes to: Int,
    bundle: Bundle?
  ) = navigate(
    from = from,
    to = to,
    bundle = bundle,
    navOptions = null,
    navigatorExtras = null
  )

  fun navigate(
    @IdRes from: Int,
    @IdRes to: Int,
    bundle: Bundle?,
    navOptions: NavOptions?,
    navigatorExtras: Navigator.Extras?
  ) {
    if (navController.currentDestination?.id == from) {
      navController.navigate(
        to,
        bundle,
        navOptions,
        navigatorExtras
      )
    }
  }

  fun navigate(
    @IdRes from: Int,
    directions: NavDirections
  ) {
    if (navController.currentDestination?.id == from) {
      navController.navigate(directions)
    }
  }

  fun navigateUp() = navController.navigateUp()

  fun popBackStack() = navController.popBackStack()
}

그런 다음 탐색 코드에서 :

val navController = navControllerProvider.getNavController()
navController.navigate(from = R.id.main, to = R.id.action_to_detail)

0

이것은 나에게 일어났습니다. 내 문제는에서 FAB를 클릭하는 것이 었습니다 tab item fragment. 탭 항목 조각 중 하나에서 another fragment.

그러나에 따르면 이안 호수 에서 이 답변 우리는 사용할 필요가 없다 tablayout하고 viewpager, 더 탐색 구성 요소 지원 . 이 때문에 조각을 포함하는 tablayout에서 탭 항목 조각으로의 탐색 경로가 없습니다.

전의:

containing fragment -> tab layout fragment -> tab item fragment -> another fragment

해결책은 조각을 포함하는 탭 레이아웃에서 의도 한 조각으로의 경로를 만드는 것입니다. 예 : path : container fragment -> another fragment

불리:

  • 탐색 그래프가 더 이상 사용자 흐름을 정확하게 나타내지 않습니다.

0

제 경우에는 50 %의 경우 다른 스레드에서 탐색하려고 할 때 오류가 발생했습니다. 메인 스레드에서 코드 실행

requireActivity().runOnUiThread {
    findNavController().navigate(...)
}

나는 이것에 대한 더 많은 표를보고 싶습니다. 그럴듯하게 들리지만 그것을 확인할 수 없습니다.
Jerry Okafor

0

제 경우에 이것은 실수로 +in action 대상을 추가했을 때 발생 했으며 충돌은 동일한 조각에 여러 번 갔을 때만 발생했습니다.

 <action
        android:id="@+id/action_to_profileFragment"
        app:destination="@+id/profileFragment" />

해결책은 +작업 대상에서 제거 하고 @id/profileFragment대신 사용하는 것입니다.@+id/profileFragment

 <action
        android:id="@+id/action_to_profileFragment"
        app:destination="@id/profileFragment" />

0

@Alex Nuts 솔루션 업데이트

특정 조각에 대한 작업이없고 조각으로 이동하려는 경우

fun NavController.navigateSafe(
@IdRes actionId: Int, @IdRes fragmentId: Int, args: Bundle? = null,
navOptions: NavOptions? = null, navExtras: Navigator.Extras? = null) 
{
  if (actionId != 0) {
      val action = currentDestination?.getAction(actionId) ?: graph.getAction(actionId)
      if (action != null && currentDestination?.id != action.destinationId) {
          navigate(actionId, args, navOptions, navExtras)
    }
    } else if (fragmentId != 0 && fragmentId != currentDestination?.id)
        navigate(fragmentId, args, navOptions, navExtras)
}

0

이 확장을 작성했습니다

fun Fragment.navigateAction(action: NavDirections) {
    val navController = this.findNavController()
    if (navController.currentDestination?.getAction(action.actionId) == null) {
        return
    } else {
        navController.navigate(action)
    }
}

0

Fragment에 대해 다음 확장 기능을 만들었습니다.

fun Fragment.safeNavigate(
    @IdRes actionId: Int,
    @Nullable args: Bundle? = null,
    @Nullable navOptions: NavOptions? = null,
    @Nullable navigatorExtras: Navigator.Extras? = null
) {
    NavHostFragment.findNavController(this).apply {
        if (currentDestination?.label == this@safeNavigate::class.java.simpleName) {
            navigate(actionId, args, navOptions, navigatorExtras)
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.