Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Action is unknown to this NavController

I'm providing data transfer between fragments in two different ways, first is working normal, while a safe data type cause run-time crash. I'm using that Android docs: https://developer.android.com/topic/libraries/architecture/navigation/navigation-implementing#Safe-args

Don't understand how could I resolve the problem. Thanks for your help

07-13 11:40:07.986 8119-8119/com.flexdecision.ak_lex.navigationsimple E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.flexdecision.ak_lex.navigationsimple, PID: 8119
java.lang.IllegalArgumentException: navigation destination com.flexdecision.ak_lex.navigationsimple:id/transferAction is unknown to this NavController
    at androidx.navigation.NavController.navigate(NavController.java:669)
    at androidx.navigation.NavController.navigate(NavController.java:628)
    at androidx.navigation.NavController.navigate(NavController.java:690)
    at com.flexdecision.ak_lex.navigationsimple.InitialFragment.makeTransfer(InitialFragment.java:58)
    at com.flexdecision.ak_lex.navigationsimple.InitialFragment.lambda$onViewCreated$0(InitialFragment.java:47)
    at com.flexdecision.ak_lex.navigationsimple.-$$Lambda$InitialFragment$shEoLbIe0sVhbTcJ2Al_FvBuU7g.onClick(lambda)
    at android.view.View.performClick(View.java:5198)
    at android.view.View$PerformClick.run(View.java:21147)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5417)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

Navigation graph:

<?xml version="1.0" encoding="utf-8"?>
<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/nav_graph"
    app:startDestination="@id/initialFragment">

    <fragment
        android:id="@+id/initialFragment"
        android:name="com.flexdecision.ak_lex.navigationsimple.InitialFragment"
        android:label="fragment_initial"
        tools:layout="@layout/fragment_initial">
        <action
            android:id="@+id/transferAction"
            app:destination="@+id/nextFragment" />
    </fragment>
    <fragment
        android:id="@+id/nextFragment"
        android:name="com.flexdecision.ak_lex.navigationsimple.NextFragment"
        android:label="fragment_next"
        tools:layout="@layout/fragment_next" >
        <argument
            android:name="firstName"
            android:defaultValue="none"
            app:type="string" />
        <argument
            android:name="lastName"
            android:defaultValue="none"
            app:type="string" />
    </fragment>
</navigation>

Activity layout:

<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />
</android.support.constraint.ConstraintLayout>

Sender:

import androidx.navigation.Navigation;

public class InitialFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_initial, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Button transactionBtn = view.findViewById(R.id.transaction);
        transactionBtn.setOnClickListener(v -> makeTransfer(v));
    }

    private void makeTransfer(View view) {
        Bundle bundle = new Bundle();
        bundle.putString("name", "Aleksey");
        Navigation.findNavController(view).navigate(R.id.transferAction, bundle);

        //Type safe passing data
        InitialFragmentDirections.TransferAction action = InitialFragmentDirections.transferAction();
        action.setLastName("Petrov");
        Navigation.findNavController(view).navigate(action);
    }
}

Receiver:

public class NextFragment extends Fragment {
    private static final String ARG_PARAM1 = "name";

    private String mParam1;
    private TextView firstName;

    public NextFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            Log.d("Next", "Param: " + mParam1);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_next, container, false);
    }


    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        firstName = view.findViewById(R.id.firstName);
        TextView lastName = view.findViewById(R.id.lastNameTV);
        firstName.setText(mParam1);

        String ln = NextFragmentArgs.fromBundle(getArguments()).getLastName();
        lastName.setText(ln);
    }
}

additional:

apply plugin: "androidx.navigation.safeargs"

dependencies {
    def nav_version = "1.0.0-alpha03"

    implementation "android.arch.navigation:navigation-fragment:$nav_version"
    implementation "android.arch.navigation:navigation-ui:$nav_version"
}
like image 273
Aleksey Kabanov Avatar asked Jul 13 '18 12:07

Aleksey Kabanov


People also ask

What is NavController in Android?

NavController manages app navigation within a NavHost . Apps will generally obtain a controller directly from a host, or by using one of the utility methods on the Navigation class rather than create a controller directly. Navigation flows and destinations are determined by the navigation graph owned by the controller.

What is popUpToInclusive?

When navigating back to destination A, we also popUpTo A, which means that we remove B and C from the stack while navigating. With app:popUpToInclusive="true" , we also pop that first A off of the stack, effectively clearing it.


2 Answers

You calling twice to 'Navigation.findNavController(view).navigate':

private void makeTransfer(View view) {
    Bundle bundle = new Bundle();
    bundle.putString("name", "Aleksey");
    Navigation.findNavController(view).navigate(R.id.transferAction, bundle);

    //Type safe passing data 
    InitialFragmentDirections.TransferAction action = InitialFragmentDirections.transferAction();
    action.setLastName("Petrov");
    Navigation.findNavController(view).navigate(action);
} 

First time with bundle and second time with safe args, but after the first call your destination already changed to 'nextFragment', and when you call second 'navigate' the 'NavController' looking for 'transferAction' action inside 'nextFragment' and throws exception.

like image 146
Alex Avatar answered Oct 22 '22 09:10

Alex


In my case, I was trying to navigate from fragment A to fragment B, but fragment A was not navigated to via the nav controller (aka, not present in the navigation graph), it was a fragment that I explicitly initialized initialized from a FragmentStateAdater (tied to a ViewPager). The solution was to create a global action to fragment B, instead of one from fragment A to fragment B

like image 32
heyheyhey Avatar answered Oct 22 '22 10:10

heyheyhey