IllegalArgumentException: điều hướng đích xxx không xác định đối với NavController này


139

Tôi đang gặp sự cố với thành phần Kiến trúc điều hướng mới của Android khi tôi cố gắng điều hướng từ Fragment này sang Fragment khác , tôi gặp lỗi kỳ lạ này:

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

Mọi điều hướng khác đều hoạt động tốt ngoại trừ điều hướng cụ thể này.

Tôi sử dụng findNavController()chức năng của Fragment để có quyền truy cập vàoNavController .

Bất kỳ trợ giúp sẽ được đánh giá cao.


Vui lòng cung cấp một số mã để hiểu rõ hơn.
Alex

12
Điều này cũng đang xảy ra với tôi.
Eury Pérez Beltré

Cho đến nay tỷ lệ xuất hiện của lỗi này đã giảm với các bản phát hành mới hơn của thư viện, nhưng tôi nghĩ rằng thư viện vẫn chưa được ghi chép đầy đủ.
Jerry Okafor

Câu trả lời:


76

Trong trường hợp của tôi, nếu người dùng nhấp vào cùng một chế độ xem hai lần rất nhanh, sự cố này sẽ xảy ra. Vì vậy, bạn cần phải thực hiện một số loại logic để ngăn chặn nhiều lần nhấp chuột nhanh chóng ... Điều này rất khó chịu, nhưng nó có vẻ là cần thiết.

Bạn có thể đọc thêm về cách ngăn chặn điều này tại đây: Ngăn Android Nhấp đúp vào một nút

Chỉnh sửa 19/3/2019 : Chỉ cần làm rõ thêm một chút, sự cố này không thể tái tạo độc quyền bằng cách "nhấp vào cùng một chế độ xem hai lần rất nhanh". Ngoài ra, bạn có thể chỉ cần sử dụng hai ngón tay và nhấp vào hai (hoặc nhiều) chế độ xem cùng một lúc, nơi mỗi chế độ xem có điều hướng riêng mà chúng sẽ thực hiện. Điều này đặc biệt dễ thực hiện khi bạn có một danh sách các mục. Thông tin trên về ngăn chặn nhiều lần nhấp chuột sẽ xử lý trường hợp này.

Chỉnh sửa 16/4/2020 : Chỉ trong trường hợp bạn không thực sự quan tâm đến việc đọc qua bài đăng Stack Overflow đó ở trên, tôi đang đưa vào giải pháp (Kotlin) của riêng mình mà tôi đã sử dụng từ lâu.

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
Chỉnh sửa về việc sử dụng 2 ngón tay và nhấp vào 2 chế độ xem cùng lúc! Đó là chìa khóa cho tôi và giúp tôi tái tạo vấn đề một cách dễ dàng. Cập nhật tuyệt vời với thông tin đó.
Richard Le Mesurier

Trong giai đoạn gỡ lỗi, tôi đã tình cờ nhấp vào trong khi ứng dụng bị kẹt khi chờ tiếp tục thực thi. Có vẻ như một trường hợp khác gồm hai lần nhấp tiếp theo liên tiếp vào IDE
Marco

1
Cảm ơn vì điều đó. Đã cứu tôi một vài sự cố và một số lỗi gãi đầu :)
user2672052

58

Kiểm tra currentDestinationtrước khi gọi điều hướng có thể hữu ích.

Ví dụ: nếu bạn có hai điểm đến phân đoạn trên biểu đồ điều hướng fragmentAfragmentBvà chỉ có một hành động từ fragmentAđến fragmentB. cuộc gọi navigate(R.id.action_fragmentA_to_fragmentB)sẽ đến IllegalArgumentExceptionkhi bạn đã bật fragmentB. Vì vậy, bạn nên luôn kiểm tra currentDestinationtrước khi điều hướng.

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

3
Tôi có một ứng dụng tìm kiếm điều hướng bằng một hành động với các đối số. Do đó, nó có thể điều hướng từ currentDestination đến chính nó. Tôi đã làm tương tự ngoại trừ navController.currentDestination == navController.graph.node. Mặc dù vậy, nó cảm thấy hơi bẩn và tôi cảm thấy mình không nên làm điều này.
Shawn Maybush

85
Thư viện không nên bắt chúng tôi kiểm tra việc này, điều đó quả thực là vô lý.
DaniloDeQueiroz

Tôi gặp vấn đề tương tự. Tôi đã có một EditText và một nút 'lưu' để lưu trữ nội dung của EditText trong cơ sở dữ liệu. Nó luôn luôn bị lỗi khi nhấn nút 'lưu'. Tôi nghi ngờ rằng lý do có liên quan đến thực tế là, để có thể nhấn nút 'lưu', tôi cần phải thoát khỏi bàn phím ảo bằng cách nhấn vào nút quay lại.
The Fox

Điều này kiểm tra một điều kiện lỗi, nhưng nó không giải quyết được vấn đề. Điều thú vị là điều kiện này đúng nếu ngăn chứa điều hướng trở nên trống vì những lý do không mong muốn.
Mike76

1
ngay cả trong iOS mặc dù đôi khi nhiều ViewController được đẩy khi bạn nhấn nút nhiều lần. Đoán cả Android và iOS đều gặp sự cố này.
coolcool1994

47

Bạn có thể kiểm tra hành động được yêu cầu trong điểm đến hiện tại của bộ điều khiển điều hướng.

UPDATE đã thêm cách sử dụng các hành động toàn cầu để điều hướng an toàn.

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
Giải pháp này sẽ không hoạt động đối với bất kỳ hành động nào được xác định bên ngoài currentDestinationdanh sách hành động của. Giả sử bạn đã xác định một hành động chung và sử dụng hành động đó để điều hướng. Điều này sẽ không thành công vì hành động không được xác định trong danh sách <action> của currentDestination. Thêm một séc như thế currentDestination?.getAction(resId) != null || currentDestination?.id != resIdsẽ giải quyết nó nhưng cũng có thể không bao gồm mọi trường hợp.
wchristiansen

@wchristiansen, cảm ơn bạn đã ghi chú. Tôi đã cập nhật mã với việc sử dụng các hành động toàn cầu
Alex Nuts

@AlexNuts câu trả lời tuyệt vời. Tôi nghĩ rằng bạn có thể xóa ?: graph.getAction(resId)-> currentDestination?.getAction(resId)sẽ trả về một hành động cho cả hành động Toàn cầu hoặc không toàn cầu (Tôi đã thử nghiệm nó). Ngoài ra, sẽ tốt hơn nếu bạn đã sử dụng Safe args -> thay vì vượt qua trong navDirections: NavDirectionshơn resIdargsriêng biệt.
Wess

@AlexNuts Lưu ý rằng giải pháp này không hỗ trợ điều hướng đến cùng đích với đích hiện tại. Hiện tại, không thể điều hướng từ điểm đến X bằng Gói Y đến điểm đến X bằng Gói Z.
Wess ngày

18

Điều đó cũng có thể xảy ra nếu bạn có Phân đoạn A với ViewPager gồm các Phân đoạn B Và bạn cố gắng điều hướng từ B đến C

Vì trong ViewPager, các đoạn không phải là đích đến của A, nên biểu đồ của bạn sẽ không biết bạn đang ở B.

Một giải pháp có thể là sử dụng ADirections trong B để điều hướng đến C


Trong trường hợp này, không phải lần nào cũng xảy ra tai nạn mà chỉ hiếm khi xảy ra. Làm thế nào để giải quyết nó?
Srikar Reddy

Bạn có thể thêm một hành động toàn cục bên trong navGraph và sử dụng nó để điều hướng
Abraham Mathew

1
Như B không nên cần phải nhận thức của cha mẹ chính xác của nó, nó sẽ là tốt hơn để sử dụng ADirections thông qua một giao diện giống như (parentFragment as? XActionListener)?.Xaction()và lưu ý bạn có thể giữ chức năng này như là một biến địa phương nếu đó là hữu ích
HMAC

bạn có thể vui lòng chia sẻ một số mẫu mã để minh họa điều này như tôi có cùng một vấn đề
Ikhiloya Imokhai

ai cũng có thể làm ơn một mã mẫu, tôi đang mắc kẹt ở cùng một vấn đề. Có một phân mảnh và sau đó là một phân mảnh tab
Usman Zafer

13

Những gì tôi đã làm để ngăn chặn sự cố là như sau:

Tôi có một BaseFragment, trong đó tôi đã thêm cái này funđể đảm bảo rằng cái destinationđược biết bởi currentDestination:

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

Cần lưu ý rằng tôi đang sử dụng plugin SafeArgs .


12

Trong trường hợp của tôi, tôi đang sử dụng nút quay lại tùy chỉnh để điều hướng lên. Tôi đã gọi onBackPressed()thay cho mã sau

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

Điều này gây ra sự IllegalArgumentExceptioncố. Sau khi tôi thay đổi nó để sử dụng navigateUp()phương pháp thay thế, tôi không gặp sự cố nữa.


Tôi không hiểu sự khác biệt giữa onBackPressed và cái này là gì, vẫn bị mắc kẹt với nút quay lại hệ thống và ghi đè nó và thay thế bằng cái này có vẻ điên rồ
Daniel Wilson

2
Tôi đồng ý rằng nó có vẻ điên rồ. Nhiều điều tôi gặp phải trong thành phần kiến ​​trúc điều hướng android cảm thấy hơi điên rồ, nó được thiết lập IMO quá cứng nhắc. Suy nghĩ về việc thực hiện triển khai của riêng tôi cho dự án của chúng tôi vì nó chỉ tạo ra quá nhiều đau đầu
Neil

Không hiệu quả với tôi ... Vẫn gặp lỗi tương tự.
Otziii

5

TL; DR Kết thúc navigatecuộc gọi của bạn bằng try-catch(cách đơn giản) hoặc đảm bảo rằng sẽ chỉ có một cuộc gọi trong sốnavigate trong khoảng thời gian ngắn. Vấn đề này có thể sẽ không biến mất. Sao chép đoạn mã lớn hơn trong ứng dụng của bạn và dùng thử.

Xin chào. Dựa trên một số câu trả lời hữu ích ở trên, tôi muốn chia sẻ giải pháp của mình có thể được mở rộng.

Đây là mã gây ra sự cố này trong ứng dụng của tôi:

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

Một cách để dễ dàng tái tạo lỗi là chạm bằng nhiều ngón tay vào danh sách các mục mà lần nhấp vào mỗi mục sẽ giải quyết trong điều hướng đến màn hình mới (về cơ bản giống như mọi người đã lưu ý - hai lần nhấp trở lên trong một khoảng thời gian rất ngắn ). Tôi nhận thấy rằng:

  1. Lời navigatekêu gọi đầu tiên luôn hoạt động tốt;
  2. Thứ hai và tất cả các lệnh gọi khác của navigatephương thức giải quyết trong IllegalArgumentException.

Theo quan điểm của tôi, tình trạng này có thể xuất hiện rất thường xuyên. Vì việc lặp lại mã là một thực tế không tốt và luôn tốt nếu có một điểm ảnh hưởng nên tôi đã nghĩ đến giải pháp tiếp theo:

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.");
    }
}

}

Và do đó, mã ở trên chỉ thay đổi trong một dòng từ sau:

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

đến điều này:

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

Nó thậm chí còn trở nên ngắn hơn một chút. Mã đã được kiểm tra ở chính xác nơi xảy ra sự cố. Không trải nghiệm nó nữa và sẽ sử dụng giải pháp tương tự cho các điều hướng khác để tránh thêm sai lầm tương tự.

Mọi suy nghĩ đều được chào đón!

Chính xác nguyên nhân gây ra sự cố

Hãy nhớ rằng ở đây chúng tôi làm việc với cùng một biểu đồ điều hướng, bộ điều khiển điều hướng và ngăn xếp dự phòng khi chúng tôi sử dụng phương pháp Navigation.findNavController .

Chúng tôi luôn nhận được cùng một bộ điều khiển và đồ thị ở đây. Khi nào navigate(R.id.my_next_destination)được gọi là đồ thị và ngăn xếp thay đổi gần như ngay lập tức trong khi giao diện người dùng chưa được cập nhật. Chỉ là không đủ nhanh, nhưng điều đó là ổn. Sau khi back-stack đã thay đổi, hệ thống điều hướng sẽ nhận được navigate(R.id.my_next_destination)cuộc gọi thứ hai . Vì back-stack đã thay đổi, giờ đây chúng tôi hoạt động liên quan đến phân đoạn trên cùng trong ngăn xếp. Phân đoạn trên cùng là phân đoạn bạn điều hướng đến bằng cách sử dụng R.id.my_next_destination, nhưng nó không chứa bất kỳ đích nào tiếp theo có ID R.id.my_next_destination. Vì vậy, bạn nhận được IllegalArgumentExceptiondo ID mà phân đoạn không biết gì về.

Lỗi chính xác này có thể được tìm thấy trong NavController.javaphương pháp findDestination.


4

Trong trường hợp của tôi, sự cố xảy ra khi tôi đã sử dụng lại một trong các Phân đoạn của mình bên trong một viewpagerphân đoạn dưới dạng con của viewpager. Các viewpagerFragment (đó là đoạn mẹ) đã được bổ sung trong xml Navigation, nhưng hành động đó đã không được thêm vào trong viewpagerđoạn mẹ.

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">

Đã khắc phục sự cố bằng cách thêm hành động vào phân đoạn máy ngắm chính cũng như được hiển thị bên dưới:

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

Hôm nay

def navigationVersion = "2.2.1"

Vấn đề vẫn tồn tại. Cách tiếp cận của tôi trên Kotlin là:

// 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

Bạn có thể kiểm tra trước khi điều hướng nếu Fragment yêu cầu điều hướng vẫn là điểm đến hiện tại, lấy từ ý chính này .

Về cơ bản, nó đặt một thẻ trên phân đoạn để tra cứu sau này.

/**
 * 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 chỉ là một id bạn sẽ phải thêm vào ids.xml của mình để đảm bảo rằng nó là duy nhất. <item name="tag_navigation_destination_id" type="id" />

Thông tin thêm về lỗi và giải pháp cũng như navigateSafe(...)các phương pháp mở rộng trong "Khắc phục sự cố đáng sợ"… không được biết đối với NavController này ”


Tôi đã nghiên cứu một số giải pháp khác nhau cho vấn đề này, và giải pháp của bạn chắc chắn là giải pháp tốt nhất. Làm tôi buồn khi thấy quá ít tình yêu dành cho nó
Lu-ca

1
có thể hữu ích khi tạo một số nhận dạng duy nhất thay cho NAV_DESTINATION_IDmột cái gì đó giống như stackoverflow.com/a/15021758/1572848
William Reed

vâng, tôi đã cập nhật câu trả lời
Frank

Thẻ đến từ đâu và tại sao nó lại cần? Tôi gặp sự cố trong đó id thực tế của thành phần điều hướng không khớp với những thứ này R.id.
riezebosch

R.id.tag_navigation_destination_idchỉ là một id bạn sẽ phải thêm vào ids.xml của mình, để đảm bảo rằng nó là duy nhất. <item name="tag_navigation_destination_id" type="id" />
Frank

3

Trong trường hợp của tôi, tôi có nhiều tệp đồ thị điều hướng và tôi đang cố gắng di chuyển từ vị trí 1 đồ thị điều hướng đến một điểm đến trong một đồ thị điều hướng khác.

Đối với điều này, chúng ta phải bao gồm biểu đồ điều hướng thứ 2 trong biểu đồ điều hướng thứ nhất như thế này

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

và thêm điều này vào hành động của bạn:

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

ở đâu 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">

trong đồ thị thứ hai.

Thông tin thêm tại đây


3

Tôi đã giải quyết vấn đề tương tự bằng cách đặt dấu kiểm trước khi điều hướng thay vì mã viết sẵn để nhấp vào điều khiển tức thì

 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 */

theo câu trả lời này

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


2

Trong trường hợp của tôi, lỗi xảy ra vì tôi đã thực hiện hành động điều hướng với Single Topvà các Clear Tasktùy chọn được bật sau màn hình giật gân.


1
Nhưng clearTask không được dùng nữa, bạn nên sử dụng popUpTo () để thay thế.
Jerry Okafor

@ Po10cio Không cần cờ nào trong số đó, tôi chỉ cần gỡ nó ra và nó đã được sửa.
Eury Pérez Beltré 24/09/18

2

Tôi gặp lỗi tương tự vì tôi đã sử dụng Ngăn điều hướng và getSupportFragmentManager().beginTransaction().replace( )đồng thời ở đâu đó trong mã của mình.

Tôi đã loại bỏ lỗi bằng cách sử dụng điều kiện này (kiểm tra nếu đích):

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

Trong trường hợp của tôi, lỗi trước đó đã được kích hoạt khi tôi nhấp vào các tùy chọn ngăn điều hướng. Về cơ bản, đoạn mã trên đã ẩn lỗi, vì trong đoạn mã của tôi, ở đâu đó tôi đã sử dụng điều hướng bằng getSupportFragmentManager().beginTransaction().replace( )Điều kiện -

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

không bao giờ đạt được bởi vì (Navigation.findNavController(v).getCurrentDestination().getId()luôn luôn cố gắng để phân mảnh nhà. Bạn chỉ được sử dụng Navigation.findNavController(v).navigate(R.id.your_action)hoặc điều khiển các chức năng của bộ điều khiển đồ thị cho tất cả các hành động điều hướng của mình.



1

Tôi đã bắt gặp ngoại lệ này sau khi đổi tên một số lớp. Ví dụ: Tôi có các lớp được gọi FragmentAvới @+is/fragment_atrong đồ thị điều hướng và FragmentBvới @+id/fragment_b. Sau đó, tôi đã xóa FragmentAvà đổi tên FragmentBđể FragmentA. Vì vậy, sau đó nút của FragmentAvẫn ở lại chuyển hướng đồ thị, và android:namecác FragmentB's nút được đổi tên path.to.FragmentA. Tôi có hai nút giống nhau android:namevà khác nhau android:id, và hành động tôi cần được xác định trên nút của lớp đã loại bỏ.


1

Nó xảy ra với tôi khi tôi nhấn nút quay lại hai lần. Lúc đầu, tôi chặn KeyListenervà ghi đè KeyEvent.KEYCODE_BACK. Tôi đã thêm mã bên dưới vào hàm có tên OnResumecho Fragment, và sau đó câu hỏi / vấn đề này được giải quyết.

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

Khi nó xảy ra với tôi lần thứ hai và trạng thái của nó giống như lần đầu tiên, tôi thấy rằng tôi có thể sử dụng adsurdchức năng. Hãy phân tích những tình huống này.

  1. Đầu tiên, FragmentA điều hướng đến FragmentB, sau đó FragmentB điều hướng đến FragmentA, sau đó nhấn nút quay lại ... lỗi xuất hiện.

  2. Thứ hai, FragmentA điều hướng đến FragmentB, sau đó FragmentB điều hướng đến FragmentC, FragmentC điều hướng đến FragmentA, sau đó nhấn nút quay lại ... crash xuất hiện.

Vì vậy, tôi nghĩ khi nhấn nút quay lại, FragmentA sẽ trở lại FragmentB hoặc FragmentC, sau đó nó gây ra tình trạng lộn xộn đăng nhập. Cuối cùng, tôi thấy rằng chức năng được đặt tên popBackStackcó thể được sử dụng để quay lại thay vì điều hướng.

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

Cho đến nay, vấn đề đã thực sự được giải quyết.


1

Có vẻ như việc trộn lẫn kiểm soát phân mảnh của backstack và điều khiển Navigation Architecture của backstack cũng có thể gây ra vấn đề này.

Ví dụ: mẫu cơ bản của CameraX ban đầu đã sử dụng điều hướng backstackgmentManager như bên dưới và nó xuất hiện như thể nó không tương tác chính xác với Điều hướng:

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

Nếu bạn ghi nhật ký 'đích hiện tại' với phiên bản này trước khi chuyển từ phân đoạn chính (phân đoạn camera trong trường hợp này) và sau đó ghi nhật ký lại khi bạn quay lại phân đoạn chính, bạn có thể thấy từ id trong nhật ký rằng id không giống nhau. Theo dự đoán, Điều hướng đã cập nhật nó khi di chuyển đến mảnh và sau đó fragmntManager không cập nhật lại khi di chuyển trở lại. Từ nhật ký:

Trước : D / CameraXBasic: currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

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

Phiên bản cập nhật của mẫu cơ bản CameraX sử dụng Điều hướng để quay lại như sau:

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

Điều này hoạt động chính xác và các bản ghi hiển thị cùng một id khi quay lại phân đoạn chính.

Trước : D / CameraXBasic: currentDest ?: androidx.navigation.fragment.FragmentNavigator$Destination@b713195

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

Tôi nghi ngờ đạo đức của câu chuyện, ít nhất là tại thời điểm này, là phải rất cẩn thận trộn Điều hướng với điều hướnggmentManager.


Điều này nghe có vẻ hợp lý, tôi sẽ điều tra thêm. Có ai có thể xác minh hoặc chứng minh tuyên bố này không?
Jerry Oka vào

@JerryOkafor - Tôi đã thử nghiệm nó trong một ứng dụng mà tôi đang làm việc dựa trên CameraX Sample và xác minh nó nhưng sẽ rất tốt nếu ai đó cũng thấy điều này. Tôi thực sự đã bỏ lỡ 'điều hướng quay lại' ở một nơi trong cùng một ứng dụng nên gần đây cũng đã sửa lại nó.
Mick

1

Một cách nực cười nhưng rất mạnh mẽ là: Đơn giản hãy gọi thế này:

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

Chỉ cần tạo phần mở rộng này:

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

1

Có thể có nhiều lý do cho vấn đề này. Trong trường hợp của tôi, tôi đang sử dụng mô hình MVVM và tôi đang quan sát một boolean để điều hướng khi boolean là true -> điều hướng khác không làm gì cả và điều này đã hoạt động tốt nhưng có một lỗi ở đây

Khi nhấn nút quay lại từ đoạn đích, tôi cũng gặp phải vấn đề tương tự. Và vấn đề là đối tượng boolean vì tôi đã quên thay đổi giá trị boolean thành false, điều này đã tạo ra sự lộn xộn. Tôi vừa tạo một hàm trong viewModel để thay đổi giá trị của nó thành false và đã gọi nó ngay sau findNavController ()


1

Thông thường, khi điều này xảy ra với tôi, tôi đã gặp phải vấn đề được mô tả bởi Charles Madere: Hai sự kiện điều hướng được kích hoạt trên cùng một ui, một sự kiện thay đổi currentDestination và sự kiện khác không thành công do currentDestination bị thay đổi. Điều này có thể xảy ra nếu bạn nhấn đúp hoặc nhấp vào hai dạng xem với trình nghe nhấp chuột gọi findNavController.navigate.

Vì vậy, để giải quyết vấn đề này, bạn có thể sử dụng if-checks, try-catch hoặc nếu bạn quan tâm thì có findSafeNavController () sẽ kiểm tra điều này cho bạn trước khi điều hướng. Nó cũng có kiểm tra xơ để đảm bảo bạn không quên về vấn đề này.

GitHub

Bài viết chi tiết vấn đề


1

Nếu bạn nhấp vào quá nhanh, nó sẽ gây ra vô hiệu và sụp đổ.

Chúng tôi có thể sử dụng RxBinding lib để trợ giúp việc này. Bạn có thể thêm ga và thời lượng khi nhấp chuột trước khi nó xảy ra.

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

Những bài viết này về điều chỉnh trên Android có thể hữu ích. Chúc mừng!


1

Nếu bạn đang sử dụng chế độ xem tái chế, chỉ cần thêm thời gian hồi chiêu của trình xử lý nhấp chuột vào lần nhấp của bạn và cả trong việc sử dụng tệp xml của chế độ xem tái chế của bạn android:splitMotionEvents="false"


1
Hãy xem câu trả lời bên dưới của tôi
Crazy

1

Sau khi suy nghĩ về lời khuyên của Ian Lake trong chủ đề twitter này, tôi đã nghĩ ra cách tiếp cận sau. Đã NavControllerWrapperđịnh nghĩa như vậy:

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

Sau đó, trong mã điều hướng:

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

0

Điều này đã xảy ra với tôi, vấn đề của tôi là tôi đang nhấp vào FAB tab item fragment. Tôi đang cố điều hướng từ một trong các phân đoạn mục tab đếnanother fragment .

Tuy nhiên, theo Ian hồ trong câu trả lời này chúng ta phải sử dụng tablayoutviewpager, không có hỗ trợ phần chuyển hướng . Vì điều này, không có đường dẫn điều hướng từ tablayout chứa phân đoạn đến phân đoạn mục tab.

Ví dụ:

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

Giải pháp là tạo một đường dẫn từ bố cục tab chứa phân đoạn đến phân đoạn dự định, ví dụ: đường dẫn: container fragment -> another fragment

Bất lợi:

  • Biểu đồ điều hướng không còn thể hiện chính xác luồng người dùng.

0

Trong trường hợp của tôi, tôi đã gặp lỗi đó khi cố gắng điều hướng từ một chuỗi khác, trong 50% trường hợp. Chạy mã trên chuỗi chính giúp

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

Tôi muốn xem thêm phiếu bầu về điều này, nghe có vẻ hợp lý nhưng tôi không thể xác minh nó.
Jerry Okafor

0

Trong trường hợp của tôi, điều này xảy ra khi tôi vô tình thêm một +điểm đến đang hoạt động và sự cố chỉ xảy ra khi tôi đi đến cùng một phân đoạn nhiều lần.

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

Giải pháp là xóa +khỏi đích hành động, chỉ sử dụng @id/profileFragmentthay vì@+id/profileFragment

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

0

Đã cập nhật giải pháp @Alex Nuts

Nếu không có hành động cho phân đoạn cụ thể và muốn điều hướng đến phân đoạn

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

Tôi đã viết phần mở rộng này

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

0

Tôi đã tạo chức năng mở rộng này cho 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)
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.