불법 논변예외: 탐색 대상 xxxx는 이 NavController에 알 수 없습니다.
새 Android Navigation Architecture 구성 요소에 문제가 있습니다. 한 조각에서 다른 조각으로 이동하려고 하면 다음과 같은 이상한 오류가 나타납니다.
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
이 내비게이션을 제외한 다른 모든 내비게이션은 정상 작동합니다.
사용합니다.findNavController()
프래그먼트의 기능을 사용하여NavController
.
어떤 도움이라도 주시면 감사하겠습니다.
저의 경우, 사용자가 동일한 보기를 매우 빠르게 두 번 클릭하면 이 충돌이 발생합니다.따라서 여러 번의 빠른 클릭을 방지하기 위해 일종의 논리를 구현해야 합니다.그것은 매우 성가신 일이지만, 꼭 필요할 것 같습니다.
Android 버튼 두 번 클릭 방지에 대한 자세한 내용은 여기에서 확인할 수 있습니다.
편집 3/19/2019: 조금 더 명확히 하자면, 이 충돌은 단지 "동일한 보기를 매우 빠르게 두 번 클릭"하는 것만으로 재현될 수 있는 것은 아닙니다.또는 손가락 두 개만 사용하여 두 개 이상의 보기를 동시에 클릭할 수도 있으며, 각 보기에는 수행할 수 있는 고유한 탐색 기능이 있습니다.항목 목록이 있을 때 특히 쉽게 사용할 수 있습니다.멀티클릭 방지에 대한 위의 정보는 이 경우를 처리할 것입니다.
편집 4/16/2020: 위의 스택 오버플로 게시물을 통해 읽는 것에 큰 관심이 없을 경우를 대비하여, 저는 지금 오랫동안 사용하고 있는 저만의 (코틀린) 솔루션을 포함하고 있습니다.
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
}
}
보기Ext.kt
fun View.setOnSingleClickListener(l: View.OnClickListener) {
setOnClickListener(OnSingleClickListener(l))
}
fun View.setOnSingleClickListener(l: (View) -> Unit) {
setOnClickListener(OnSingleClickListener(l))
}
홈프래그먼트.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settingsButton.setOnSingleClickListener {
// navigation call here
}
}
을 합니다.currentDestination
navigation을 호출하기 전에 도움이 될 수 있습니다.
예를 들어 탐색 그래프에 두 개의 조각 대상이 있는 경우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)
}
}
충돌을 막기 위해 제가 한 일은 다음과 같습니다.
Base Fragment가 있고 거기에 이것을 추가했습니다.fun
확실하게 하기 위해destination
에 의해 알려져 있습니다.currentDestination
:
fun navigate(destination: NavDirections) = with(findNavController()) {
currentDestination?.getAction(destination.actionId)
?.let { navigate(destination) }
}
제가 SafeArgs 플러그인을 사용하고 있다는 것에 주목할 필요가 있습니다.
프래그먼트 A에 프래그먼트 B의 뷰 페이저가 있는 경우에도 발생할 수 있습니다. 그리고 B에서 C로 탐색하려고 할 경우에도 발생할 수 있습니다.
ViewPager에서 조각들이 A의 목적지가 아니기 때문에 그래프는 당신이 B에 있는 것을 알 수 없습니다.
해결책은 B의 A 방향을 사용하여 C로 이동하는 것일 수 있습니다.
해보세요.
- 다음 확장 기능(또는 일반 기능)을 만듭니다.
업데이트됨(반영하지 않고 더욱 가독성이 높음)
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.DialogFragmentNavigator
import androidx.navigation.fragment.FragmentNavigator
fun Fragment.safeNavigateFromNavController(directions: NavDirections) {
val navController = findNavController()
when (val destination = navController.currentDestination) {
is FragmentNavigator.Destination -> {
if (javaClass.name == destination.className) {
navController.navigate(directions)
}
}
is DialogFragmentNavigator.Destination -> {
if (javaClass.name == destination.className) {
navController.navigate(directions)
}
}
}
}
OLD(반사 있음)
import androidx.fragment.app.Fragment
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.fragment.FragmentNavigator
inline fun <reified T : Fragment> NavController.safeNavigate(directions: NavDirections) {
val destination = this.currentDestination as FragmentNavigator.Destination
if (T::class.java.name == destination.className) {
navigate(directions)
}
}
- 프래그먼트에서 다음과 같이 사용합니다.
val direction = FragmentOneDirections.actionFragmentOneToFragmentTwo()
// new usage
safeNavigateFromNavController(direction)
// old usage
// findNavController().safeNavigate<FragmentOne>(action)
제 고민은.
다른 두 조각(FragmentTwo 및 Fragment)으로 가는 조각(FragmentOne)이 있습니다.셋).일부 낮은 장치에서 사용자가 FragmentTwo로 리디렉션하는 버튼을 누르지만 사용자가 Fragment로 리디렉션하는 버튼을 누른 후 몇 밀리초 안에셋. 결과는 다음과 같습니다.
치명적 예외: java.lang.불법 논변현재 대상(프래그먼트)에서 예외 탐색 작업/대상 작업_fragmentOne_to_fragmentTwo를 찾을 수 없습니다.3) class= fragment3
해결책은 다음과 같습니다.
현재 목적지가 현재 프래그먼트에 속하는지 확인합니다.사실이면 내비게이션 에이션을 실행합니다.
그게 다예요!
TL;DR Navigation 컨트롤러는 UI보다 빠르게 변경되며 동일한 두 개를 전송합니다.navigate(R.id.destn_id)
두 개의 서로 다른 상태에 있는 내비게이션 컨트롤러에 연결합니다.
고정하려면 랩을 사용합니다.navigate
와의 통화try-catch
(simple 방식), 또는 다음 중 하나만 호출할 수 있도록 합니다.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
메소드 확인(in)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);
그것은 심지어 조금 더 짧아졌습니다.코드는 충돌이 발생한 정확한 장소에서 테스트되었습니다.더 이상 경험하지 못했으며, 동일한 실수를 더 이상 피하기 위해 다른 내비게이션에도 동일한 솔루션을 사용할 것입니다.
어떤 생각이든 환영합니다!
충돌의 원인이 정확히 무엇인가요?
여기서는 방법을 사용할 때 동일한 탐색 그래프, 탐색 컨트롤러 및 백스택으로 작업합니다.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
방법findDestination
.
제 경우에는 사용자 지정 뒤로 가기 버튼을 사용하고 있었습니다.전화해봤어요.onBackPressed()
아래의 암호 대신에
findNavController(R.id.navigation_host_fragment).navigateUp()
이것이 원인입니다.IllegalArgumentException
일어날 것입니다.사용하기 위해 바꾼 후에.navigateUp()
방법 대신에, 저는 다시는 사고를 당하지 않았습니다.
저의 경우, 문제가 발생한 것은 제 프래그먼트 중 하나를 A 내부에서 재사용했을 때입니다.viewpager
의 자식으로서 조각난viewpager
.viewpager
fragment(부모 fragment)가 Navigation 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>
오늘은
def navigation버전 = "2.2.1"
여전히 문제가 있습니다.코틀린에 대한 제 접근법은 다음과 같습니다.
// 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)
}
}
저의 경우 여러 개의 nav 그래프 파일을 가지고 있었고 1개의 nav 그래프 위치에서 다른 nav 그래프의 목적지로 이동하려고 했습니다.
이를 위해서는 이렇게 첫번째 nav 그래프에 두번째 nav 그래프를 넣어야 합니다.
<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">
두번째 그래프에서
자세한 내용은 여기에
탐색하기 전에 탐색을 요청하는 Fragment가 여전히 이 요지에서 가져온 현재 목적지인지 확인할 수 있습니다.
기본적으로 나중에 조회할 수 있도록 조각에 태그를 설정합니다.
/**
* 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" />
버그와 해결책에 대한 더 많은 정보와navigateSafe(...)
이 NavController에서는 "두려운 자를 고치기"의 확장 방법을 알 수 없습니다.
제가 이 연장자를 썼습니다.
fun Fragment.navigateAction(action: NavDirections) {
val navController = this.findNavController()
if (navController.currentDestination?.getAction(action.actionId) == null) {
return
} else {
navController.navigate(action)
}
}
클릭 즉시 제어를 위한 보일러 플레이트 코드 대신 탐색 전 체크를 하여 같은 문제를 해결하였습니다.
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번 케이스는.android:splitMotionEvents="false"
xml에서setMotionEventSplittingEnabled(false)
소스 파일이 도움이 될 것입니다.이 특성을 false로 설정하면 하나의 보기만 클릭할 수 있습니다.여기서 읽어보실 수 있습니다.
경우 2의 경우, 사용자가 보기를 여러 번 클릭할 수 있도록 탐색 과정이 지연되는 문제가 발생합니다(API 호출, 애니메이션 등).사용자가 보기를 두 번 클릭하지 않도록 탐색이 즉각적으로 수행되도록 루트 문제를 해결해야 합니다.API 호출의 경우처럼 지연이 불가피한 경우 뷰를 비활성화하거나 클릭 불가능하게 만드는 것이 적절한 해결책이 될 것입니다.
제 경우에는 제가 내비게이션을 사용했기 때문에 버그가 발생했습니다.Single Top
그리고.Clear Task
옵션을 선택할 수 있습니다.
백스택의 fragmentManager 제어와 백스택의 Navigation Architecture 제어를 혼합하는 것도 이러한 문제를 일으킬 수 있는 것 같습니다.
예를 들어 원래 CameraX 기본 샘플은 아래와 같이 fragmentManager 백스택 탐색을 사용했으며 탐색과 올바르게 상호 작용하지 않은 것처럼 나타납니다.
// Handle back button press
view.findViewById<ImageButton>(R.id.back_button).setOnClickListener {
fragmentManager?.popBackStack()
}
메인 프래그먼트(이 경우 카메라 프래그먼트)에서 이동하기 전에 이 버전으로 '현재 목적지'를 기록했다가 메인 프래그먼트로 돌아갈 때 다시 기록하면 로그에 있는 id에서 id가 동일하지 않음을 알 수 있습니다.Navigation은 조각으로 이동할 때 업데이트하고 fragmntManager는 다시 이동할 때 업데이트하지 않는 것으로 추측됩니다.로그에서:
이전: D/CameraXBasic: currentDest?: 안드로이드 x, navig, fragment.조각 탐색기 $Destination@b713195
이후: D/CameraXBasic: currentDest?: 안드로이드 x, navig, fragment.조각 탐색기 $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?: 안드로이드 x, navig, fragment.조각 탐색기 $Destination@b713195
이후: D/CameraXBasic: currentDest?: 안드로이드 x, navig, fragment.조각 탐색기 $Destination@b713195
적어도 이때만큼은 Navigation과 fragmentManager navigation을 매우 조심스럽게 섞는 것이 이야기의 교훈이라고 생각합니다.
우스꽝스럽지만 매우 강력한 방법은 다음과 같습니다.
view?.findNavController()?.navigateSafe(action)
이 확장명 만들기:
fun NavController.navigateSafe(
navDirections: NavDirections? = null
) {
try {
navDirections?.let {
this.navigate(navDirections)
}
}
catch (e:Exception)
{
e.printStackTrace()
}
}
이 트위터 스레드에서 이안 레이크의 조언에 대해 생각한 후 저는 다음과 같은 접근 방법을 생각해 냈습니다.하고 있다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)
현재 대상에 다음 작업이 있는지 확인하여 이 문제를 해결합니다.
public static void launchFragment(BaseFragment fragment, int action) {
if (fragment != null && NavHostFragment.findNavController(fragment).getCurrentDestination().getAction(action) != null) {
NavHostFragment.findNavController(fragment).navigate(action);
}
}
public static void launchFragment(BaseFragment fragment, NavDirections directions) {
if (fragment != null && NavHostFragment.findNavController(fragment).getCurrentDestination().getAction(directions.getActionId()) != null) {
NavHostFragment.findNavController(fragment).navigate(directions);
}
}
사용자가 두 개의 다른 버튼을 빠르게 클릭하면 문제가 해결됩니다.
@AlexNuts 답변으로 업데이트하여 중첩 그래프로 이동할 수 있습니다.작업이 중첩 그래프를 대상으로 사용하는 경우 다음과 같이 수행됩니다.
<action
android:id="@+id/action_foo"
android:destination="@id/nested_graph"/>
현재 대상이 그래프가 될 수 없으므로 이 작업의 대상 ID를 현재 대상과 비교할 수 없습니다.중첩 그래프의 시작 대상을 확인해야 합니다.
fun NavController.navigateSafe(directions: NavDirections) {
// Get action by ID. If action doesn't exist on current node, return.
val action = (currentDestination ?: graph).getAction(directions.actionId) ?: return
var destId = action.destinationId
val dest = graph.findNode(destId)
if (dest is NavGraph) {
// Action destination is a nested graph, which isn't a real destination.
// The real destination is the start destination of that graph so resolve it.
destId = dest.startDestination
}
if (currentDestination?.id != destId) {
navigate(directions)
}
}
그러나 이렇게 하면 동일한 목적지로 두 번 이동할 수 없으며, 이는 때때로 필요합니다.이를 허용하기 위해 다음에 체크를 추가할 수 있습니다.action.navOptions?.shouldLaunchSingleTop()
덧붙이기app:launchSingleTop="true"
중복 대상을 원하지 않는 작업에 적용할 수 있습니다.
이 충돌을 피하기 위해 내 동료 중 한 명이 작은 도서관을 썼는데, 그것은 그 도서관이 노출되어 있었습니다.SafeNavController
NavController
는 동시에 여러 탐색 명령으로 인해 충돌이 발생하는 경우를 처리합니다.
전체 문제와 해결책에 대한 짧은 기사입니다.
도서관은 여기서 찾을 수 있습니다.
클릭이 빠른 문제와 내비게이션이 충돌하는 문제에 대한 또 다른 해결책은 다음과 같습니다.
fun NavController.doIfCurrentDestination(@IdRes destination: Int, action: NavController.()-> Unit){
if(this.currentDestination?.id == destination){action()}
}
다음과 같이 사용합니다.
findNavController().doIfCurrentDestination(R.id.my_destination){ navigate(...) }
이 솔루션의 이점은 기존의 모든 통화를 쉽게 처리할 수 있다는 것입니다.naviagte()
이미 사용하고 있는 서명이 무엇이든 간에 100만 건의 오버로드를 발생시킬 필요가 없습니다.
이 문제에는 여러 가지 이유가 있을 수 있습니다.제 경우 MVVM 모델을 사용하고 있었고 부울이 참일 때 탐색을 위한 부울을 관찰하고 있었습니다. -> 탐색하지 않으면 아무것도 수행하지 않으며 잘 작동했지만 여기서 한 가지 오류가 있습니다.
목적지 조각에서 뒤로 버튼을 눌렀을 때 같은 문제가 발생했습니다.문제는 부울값을 false로 변경하는 것을 잊어버려서 부울값이 엉망이 되어 버렸습니다.방금 viewModel에서 함수를 만들어 값을 false로 변경하고 findNavController() 직후에 호출했습니다.
내비게이션 드로어를 사용해서 같은 오류가 발생했습니다.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( )
-는 -
if (Navigation.findNavController(v).getCurrentDestination().getId() ==
R.id.your_destination_fragment_id)
연락이 닿지 않은 이유는(Navigation.findNavController(v).getCurrentDestination().getId()
항상 집의 파편을 가리키고 있었습니다.를 사용해야 합니다.Navigation.findNavController(v).navigate(R.id.your_action)
또는 모든 탐색 작업에 대한 navgraph controller 기능을 사용할 수 있습니다.
두 가지 경우(두 번 탭, 두 번 버튼 동시 탭)를 처리하는 링에 제 답을 던지는 것은 우아하면서도 진짜 오류를 감추려 하지 않습니다.
우리는 a를 사용할 수 있습니다.navigateSafe()
탐색하려는 대상이 현재 대상에서는 유효하지 않지만 이전 대상에서는 유효한지 확인하는 기능입니다.이 경우 코드는 사용자가 동시에 두 번 두 번 두 번 두드리거나 두 번 두 번 두드린 것으로 가정합니다.
이 해결책은 완벽하지는 않지만 부모의 목적지로 이동하려고 할 때 실제 문제를 숨길 수 있기 때문입니다.하지만 이것은 가능성이 없을 것으로 추정됩니다.
코드:
fun NavController.navigateSafe(directions: NavDirections) {
val navigateWillError = currentDestination?.getAction(directions.actionId) == null
if (navigateWillError) {
if (previousBackStackEntry?.destination?.getAction(directions.actionId) != null) {
// This is probably some user tapping two different buttons or one button twice quickly
// Ignore...
return
}
// This seems like a programming error. Proceed and let navigate throw.
}
navigate(directions)
}
수업 이름을 몇 개 바꾼 후에 이 예외를 잡았습니다.예를 들어,나는 수업을 받았습니다.FragmentA
와 함께@+is/fragment_a
탐색 그래프와FragmentB
와 함께@+id/fragment_b
. 그리고 삭제했습니다.FragmentA
이름을 바꾸었습니다.FragmentB
.FragmentA
. 그래서 그 노드 이후에FragmentA
여전히 내비게이션 그래프에 머물렀고,android:name
FragmentB
노드의 이름이 변경되었습니다.path.to.FragmentA
. 두 개의 노드가 있었는데 같은 노드를 가지고 있었습니다.android:name
그 밖의android:id
, 제거된 클래스의 노드에 필요한 조치가 정의되었습니다.
뒤로 버튼을 두 번 누르면 생각납니다.처음엔 내가 가로채서KeyListener
오버라이드(override)KeyEvent.KEYCODE_BACK
. 아래 코드를 이름있는 함수에 추가하였습니다.OnResume
프래그먼트의 경우 이 질문/문제가 해결됩니다.
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
기능 이 상황들을 분석해 보겠습니다이 상황들을 분석해 보겠습니다.
먼저 Fragment A가 Fragment B로 이동한 다음 Fragment B가 Fragment A로 이동한 다음 뒤로 버튼을 누릅니다.충돌이 나타납니다.
두 번째로, FragmentA는 FragmentB로 탐색한 다음, FragmentB는 FragmentC로 탐색하고, FragmentC는 FragmentA로 탐색한 다음 뒤로 버튼을 누릅니다.충돌이 나타납니다.
그래서 뒤로 버튼을 누르면 프래그먼트 A가 프래그먼트 B나 프래그먼트 C로 돌아가고, 그러면 로그인이 엉망이 된다고 생각합니다.마침내 나는 그 함수의 이름이popBackStack
탐색보다는 뒤로 이동하는 데 사용할 수 있습니다.
NavHostFragment.findNavController(this@TeacherCloudResourcesFragment).
.popBackStack(
R.id.teacher_prepare_lesson_main_fragment,false
)
지금까지는 정말 문제가 해결됐습니다.
너무 빨리 클릭하면 null 및 crash가 발생합니다.
RxBinding lib을 사용하여 이 문제를 해결할 수 있습니다.발생하기 전에 클릭 시 스로틀과 지속 시간을 추가할 수 있습니다.
RxView.clicks(view).throttleFirst(duration, TimeUnit.MILLISECONDS)
.subscribe(__ -> {
});
안드로이드의 조절에 관한 이러한 기사들이 도움이 될 것입니다.건배!
2.3.1 Navigation을 호출하는데 애플리케이션 구성이 변경될 때 동일한 오류가 발생합니다.디버그를 통해 문제의 원인을 찾았을 때,GaphId
인에NavHostFragment
호출에 의해 현재 설정된 ID로 적용되지 않았습니다.navController.setGraph()
.GraphId
NavHostFragment
에 의해서만 얻을 수 있습니다.<androidx.fragment.app.FragmentContainerView/>
태그가 개일 이 이때 여러개가 있으면 이 문제가 발생합니다.GraphId
코드에 s가 동적으로 설정됩니다.인터페이스가 복원되면 캐시에서 대상을 찾을 수 없습니다.GraphId
. 수동으로 값을 지정하면 이 문제를 해결할 수 있습니다.mGraphId
인에NavHostFragment
그래프를 전환할 때 반사를 통해 확인할 수 있습니다.
navController.setGraph(R.navigation.home_book_navigation);
try {
Field graphIdField = hostFragment.getClass().getDeclaredField("mGraphId");
graphIdField.setAccessible(true);
graphIdField.set(navHostFragment, R.navigation.home_book_navigation);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
언급URL : https://stackoverflow.com/questions/51060762/illegalargumentexception-navigation-destination-xxx-is-unknown-to-this-navcontr
'programing' 카테고리의 다른 글
pthread 조건 변수를 언제 사용해야 합니까? (0) | 2023.10.29 |
---|---|
Swift에서 Change 문자InRange는 어떻게 작동합니까? (0) | 2023.10.29 |
XML과 XSD의 차이점은 무엇입니까? (0) | 2023.10.29 |
프레임 포인터는 언제 생략해야 합니까? (0) | 2023.10.29 |
장고에서 jQuery/Ajax와 함께 포스팅하려면 어떻게 해야 합니까? (0) | 2023.10.29 |