한 Fragment에서 다른 Fragment로 이동하려고 할 때 새로운 Android 탐색 아키텍처 구성 요소에 문제 가 있습니다.이 이상한 오류가 발생합니다.
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
이 특정 탐색을 제외하고 다른 모든 탐색은 잘 작동합니다.
findNavController()
Fragment의 기능을 사용 하여 NavController
.
도움을 주시면 감사하겠습니다.
답변
제 경우에는 사용자가 동일한 뷰를 매우 빠르게 두 번 클릭하면이 충돌이 발생합니다. 따라서 여러 번의 빠른 클릭을 방지하기 위해 일종의 논리를 구현해야합니다. 매우 성가 시지만 필요한 것 같습니다.
이를 방지하는 방법에 대한 자세한 내용은 여기에서 확인할 수 있습니다. 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
}
}
답변
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)
}
답변
내비게이션 컨트롤러의 현재 목적지에서 요청 된 작업을 확인할 수 있습니다.
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)
}
}
답변
또한 Fragment B의 ViewPager가있는 Fragment A가 있고 B에서 C로 이동하려고 할 때 발생할 수 있습니다.
ViewPager에서 조각은 A의 대상이 아니기 때문에 그래프는 사용자가 B에 있다는 것을 알 수 없습니다.
해결책은 B에서 ADirections를 사용하여 C로 이동하는 것입니다.
답변
충돌을 방지하기 위해 내가 한 일은 다음과 같습니다.
나는 BaseFragment를 가지고 있으며, 거기에 다음 fun
을 통해 알 수 있도록 이것을 추가 destination
했습니다 currentDestination
.
fun navigate(destination: NavDirections) = with(findNavController()) {
currentDestination?.getAction(destination.actionId)
?.let { navigate(destination) }
}
내가 SafeArgs 플러그인을 사용하고 있다는 점은 주목할 가치가 있습니다.
답변
제 경우에는 위로 이동하기 위해 맞춤 뒤로 버튼을 사용했습니다. onBackPressed()
다음 코드 대신 전화 했습니다.
findNavController(R.id.navigation_host_fragment).navigateUp()
이로 인해 IllegalArgumentException
발생했습니다. navigateUp()
대신 방법 을 사용하도록 변경 한 후 다시 충돌이 발생하지 않았습니다.
답변
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);
}
버그를 쉽게 재현하는 방법은 새 화면 탐색에서 각 항목을 클릭하면 해결되는 항목 목록을 여러 손가락으로 탭하는 것입니다 (기본적으로 사람들이 언급 한 것과 동일 함-매우 짧은 시간에 두 번 이상의 클릭) ). 난 그것을 알아 챘다:
- 첫 번째
navigate
호출은 항상 잘 작동합니다. - 두 번째 및
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.java
method 에서 찾을 수 있습니다 findDestination
.