MichaelThePotato
MichaelThePotato

Reputation: 1525

Is it possible to send arguments other than string or integer in android's new navigation component

The new navigation component is great! however I would like to send "Long" variables between my fragments.

writing this in the navigation graph file works:

<argument
        android:name="discussionId"
        app:type="string" />

writing this won't compile:

<argument
        android:name="discussionId"
        app:type="long" />

Currently, it seems I'm forced to parse them to and from string formats. It works fine, but it seems bizarre to me that I can't use primitive types such as long or byte or short for such a fundamental architecture. Am I missing something? Is this kind of feature set to be developed in the future?

Upvotes: 20

Views: 21721

Answers (5)

GV_FiQst
GV_FiQst

Reputation: 1567

Use primitive wrappers! What's wrong with it?

<argument
    android:name="discussionId"
    app:argType="java.lang.Long" />

// java.lang.Double, java.lang.Float, etc...

Works like a charm with kotlin and even with safe args because all primitive wrappers are implementing Serializable

Upvotes: 8

yonga springfield
yonga springfield

Reputation: 714

This might come in late and resolves the problem in a slightly different manner but I am putting it here because I feel the question demands for a way of sending arguments across during navigation and to a nominal magnitude in a type safe way.

That my friend is one of the problems architecture components viewmodel solves in a very efficient manner.As per the docs, on the section "share data between fragments" we will find the technique used. Essentially, it's all about creating a viewmodel instance that is instead attached to the scope of the enclosing fragment/activity to the two(or more) navigated to fragments. That way, they will survive throughout the entire lifecycle of the navigated to fragments.

Below is a small example that I actually extracted from one of the applications I have in production.

-MainActivity |-NavHostFragment | |-FormFragment(Start destination) | |-ResultFragment( navigated to fragment) | | and a SharedViewModel for the transfer of data amongst destination fragments.

ViewModelCode

public class SharedViewModel extends ViewModel implements ResultFragment.ResultFragmentViewModel, FormFragment.FormFragmentViewModel {
public String name;

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}   }

in FormFragment.

    @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    sharedViewModel = ((FormFragmentViewModel) ViewModelProviders.of(this.getActivity()).get(SharedViewModel.class));
}

public void onFabPressed( View view) {
    String text = editText.getText().toString().trim();
    if(text.matches("")) return ;
    sharedViewModel.setName(text);
    Navigation.findNavController(view).navigate(R.id.action_formFragment_to_resultFragment);

}

public interface FormFragmentViewModel {
    public void setName(String string);
}

in the ResultFragment,

    @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    sharedViewModel = ((ResultFragmentViewModel) ViewModelProviders.of(this.getActivity()).get(SharedViewModel.class));
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_result, container, false);
    FloatingActionButton fab = view.findViewById(R.id.fab);
    ((TextView) view.findViewById(R.id.textview))
        .setText(sharedViewModel.getName());
    fab.setOnClickListener(
            Navigation.createNavigateOnClickListener(R.id.action_global_formFragment)
    );
    return view;
}


public interface ResultFragmentViewModel {
    public String getName();
}

In the above pieces of code, the importance should be laid on the scope sent to the ViewModelProviders.of() method of both fragments being the enclosing activity as it's lifecycle outlives that of both fragments( FormFragment, ResultFragment). Saving the data that we wish to send to the destination fragment in the viewmodel before navigating to the destination fragment knowing we will retrieve it from the same instance of the viewmodel once in the destination fragment. The interfaces are used for intellisense with android studio, abstracting away ( from a developer standpoint) unnecessary methods from the viewmodel respective to in which fragment it is being used in. In this setup, even actions can be sent to the destination fragment using something like a delegate pattern by setting the object to carryout the action once in the destination fragment in the viewmodel before navigating to the destination fragment and while there, retrieve the object and delegate to it the action to be taken. I will like to add that in this setup, it's close to impossible for one to confuse the String name variable for an int.

Hope this helps.

Upvotes: 0

Guillaume
Guillaume

Reputation: 6691

Since version 1.0.0-alpha08 you can use a lot of different types, i have found a list here

"integer" -> IntType
"integer[]" -> IntArrayType
"long" -> LongType
"long[]" -> LongArrayType
"float" -> FloatType
"float[]" -> FloatArrayType
"boolean" -> BoolType
"boolean[]" -> BoolArrayType
"reference" -> ReferenceType
"reference[]" -> ReferenceArrayType
"string" -> StringType
"string[]" -> StringArrayType
null -> StringType

and for the used in navigation graph (for exemple: list of string)

<argument
    android:name="photo_url"
    app:argType="string[]"
/>

Upvotes: 35

Afzal N
Afzal N

Reputation: 2593

Just set the default value to be 0L instead of 0, and the type to be "inferred"

Upvotes: 0

Levi Moreira
Levi Moreira

Reputation: 12005

At the moment you can't use safe args with types apart from integer, string, inferred and reference. An issue has been opened asking for other types.

But you pass a bundle programmatically when using the navigate() method to navigate to a destination:

var bundle = bundleOf("key" to amount)
view.findNavController().navigate(R.id.action_id, bundle)

And you can use the usual getArguments to retrieve the data in the destination fragment:

val value = arguments.getString("key")

Upvotes: 19

Related Questions