Passing argument from fragment to activity using SafeArgs - android

Using navigation graph, when i navigate from fragment to activity and also passing argument using safeargs, in activity, I cannot get argument. How can I get argument passing from fragment???
In fragment, I can get argument by getArgument() function, but not in activity.
In fragment I switch to another activity by:
findNavController().navigate(AFragmentDirections.actionAFragmentToBActivity(1)
and in B activity get argument in onCreate by :
val args = BActivityArgs.fromBundle(savedInstanceState!!)
but my app crash immediately.

The accepted answer is not an answer to your question. As you point out: you cannot use getArguments() in your Activity, you can only do that in a fragment. However, in an activity you can get the data like this (java syntax):
String aField = BActivityArgs.fromBundle(getIntent().getExtras()).getAField()
So, just replace getArguments() with getIntent().getExtras() if you have an Activity on the receiving end.

Check it out the Android Doc :-
https://developer.android.com/guide/navigation/navigation-pass-data#java
Send Data
#Override
public void onClick(View view) {
EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
int amount = Integer.parseInt(amountTv.getText().toString());
ConfirmationAction action =
SpecifyAmountFragmentDirections.confirmationAction()
action.setAmount(amount)
Navigation.findNavController(view).navigate(action);
}
Get Data :-
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
TextView tv = view.findViewById(R.id.textViewAmount);
int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
tv.setText(amount + "")
}

BActivityArgs.fromBundle(getIntent().getExtras()).getAField();
Work perfectly

As stated in the official documentation HERE :
The Navigation component is designed for apps that have one main activity with multiple fragment destinations. The main activity is associated with a navigation graph and contains a NavHostFragment that is responsible for swapping destinations as needed. In an app with multiple activity destinations, each activity has its own navigation graph
A solution might be: rethink if the activity could be converted to a Fragment and then the newly created Fragment could be handled by the same Navigation Component. Thus allowing you to use the normal SafeArgs syntax to pass and retrieve data.
If you are still having problems with the SafeArgs plugin, I would highly recommend this medium article by the official Android team, HERE

Related

open fragment from activity and pass arguments using navigation components

I can navigate from one fragment which is inside of main_graph from navigation components to one activity, I also added the activity inside the main_graph from navigation components:
see the picture below:
My problem is that I can't navigate from the new activity that was added and send arguments or one bundle to the previous fragment as we can see in the picture of above. This activity is not the MainAcitivity. I found this question and on there the guy did something like this:
val bundle = Bundle()
bundle.putString("id", id)
(activity as MainActivity).navController.navigate(R.id.orderDetailFragment, bundle)
I don't understand why for me not works this code: (activity as MainActivity) I think that it should be something like this in my case:
val navController by lazy { findNavController(R.id.main_graph) }
val bundle = Bundle()
bundle.putString("barcode", "123456789")
navController.navigate(R.id.registerProductFragment, bundle)
However I'm getting this error:
Process: com.example.inventariooffline, PID: 15859
java.lang.IllegalArgumentException: ID does not reference a View inside this Activity
at androidx.core.app.ActivityCompat.requireViewById(ActivityCompat.java:371)
at androidx.navigation.Navigation.findNavController(Navigation.java:58)
at androidx.navigation.ActivityKt.findNavController(Activity.kt:30)
I just one return from the activity that I have created to my previous fragment with safe arguments or a bundle.
I hope did be clear guys, thanks so much.

Android/Kotlin replacement for EventBus postSticky()

I am searching for postSticky() method replacement. It is being used for simple passing value to previous Fragment, but thing is that I am using BackStackUtil for navigation so instance() method is being called when returning only if stack gets cleared somehow before getting back.Previous Fragment is holding List of items, when next Fragment can modify picked item and yet another one can do something else so it is chain of sticky events when each of these is being passed to previous Fragment. App structure won't let me to apply Coordinator pattern at current stage and also I don't want to attach Bundle to Fragments kept on stack. I was looking for solutions but I couldn't find any. I also don't want to store values in some static fields or SharedPreferences/Data storage.I was thinking about shared ViewModel but I don't really like this idea to be honest, so I would appreciate any ideas or just confirmation if shared VM is the only/best way.
Do you have any other ideas?
In your A fragment, before navigating to B fragment, listen to savedStateHandle:
findNavController()
.currentBackStackEntry
?.savedStateHandle?.getLiveData<Bundle>("DATA_KEY")
?.observe(viewLifecycleOwner) { result ->
// Result from fragment B
}
In your B fragment, before navigating back, set the data to pass to A fragment:
findNavController()
.previousBackStackEntry
?.savedStateHandle
?.set("DATA_KEY", result)
You can remove the observer using:
findNavController()
.currentBackStackEntry
?.savedStateHandle?.remove<Bundle>
Note that here the passed type is Bundle (the type in getLiveData<Bundle>) but you can use any type you want.

Android: How do I trigger an event once a fragment is totally loaded?

I am trying to load initial data onto a fragment in my application. In the following lines of code (located at the very end of onCreate, I attempt retrieve the fragment and load data onto it:
// Display the current list of matches to the user
var eventsFragment: EventsFragment =
supportFragmentManager.findFragmentByTag("eventsFragment") as EventsFragment
eventsFragment.displaySchedule(currList)
However, I receive the following error:
Caused by: kotlin.TypeCastException: null cannot be cast to non-null
type com.example.alarmfornbamatches.ui.main.EventsFragment
My guess is that the fragment hasn't loaded at the end of onCreate. So how do I execute the displaySchedule function once the fragment is fully loaded and available to be referenced for UI updates?
You can solve the TypeCastException by using this:
// Display the current list of matches to the user
var eventsFragment: EventsFragment? = supportFragmentManager.findFragmentByTag("eventsFragment") as EventsFragment
eventsFragment?.displaySchedule(currList)
You are looking for a fragment that you haven't created, in a place you haven't put it yet. You must first create the fragment and add it to the supportFragmentManager using transactions. If you want do pass data to the fragment you should do so by add this to your fragment (if your list is not string, then it must be serializable and use putSerializable instead.
companion object{
fun newInstance(list: ArrayList<String>) = EventsFragment().apply{
arguments = Bundle().apply {
putStringArrayList(KEY, list)
}
}
}
and then pull the data out of the arguments bundle in the onCreate method in your fragment.
Now in the onCreate in your activity you will create the fragment and add it like so
eventsFragment = EventsFragment.newInstance(list)
supportFragmentManager.beginTransaction().add(R.id.frameLayoutId, eventsFragment, fragmentTag).commit()
only now, if necessary can you use the code you have to get the fragment again, and you should always do it expecting ti to possibly come back as null:
eventsFragment = supportFragmentManager.findFragmentByTag(fragmentTag) as? EventsFragment ?: EventsFragment.newInstance(list)
The problem with your code is you are trying to find a fragment which is not there that's why its null. Not sure where you made the Fragment transaction.
To fix this There can be two cases:
If you already have the data in your Activity its better you pull it inside Fragment by using getActivity() in #onViewCreated() or by a Shared ViewModel.
If you are loading data from a source(Network/IO) then you can use a Shared ViewModel or Get fragment from back stack and call its method or you can use conventional callback stuff.
I can add some sample code but i am not sure which option is best fit for you since its not clear from question that u are using MVVM or not.

Cannot resolve method getActivity()

I am new to Android and learning to create fragments in Android by following this
example: Fragment Navigation Drawer
The code between Navigating between Menu Items and Add Navigation Header consists a method getActivity().
As the author didn't mentioned where to paste this code, I pasted in my MainActivity.java file
Is code between Navigating between Menu Items and Add Navigation Header pasted at correct location by me?
In method selectDrawerItem(MenuItem menuItem) there is a comment // Create a new fragment and specify the planet to show based on position
Does author expects me to add something over here.
The project files layout created by me on AndroidStudio is as follow:AndroidStudio Snapshot
You can use:
this Or `MainActivity.this`
Instead of:
getActivity()
An Activity has no getActivity() method.
Fragments have.
Because getActivity() says: "return the Activity which contains me".
And while Framents are contained in Activities, Activities themselves aren't.
It has been clearly pointed out that you cannot use the getActivity() method in an activity. Well, other alternatives apart from the this keyword could be;
Get current activity context : using the getContext()
This method can be called on a View like text view like so textView.getContext(); This will give the context of the activity in which the view is currently hosted in. i.e something like so View.getContext();
Get App-level context : getApplicationContext() this method returns the activity that houses the entire life cycle of the application.
In Fragment it is best to use onAttach() method to get the instance of an Activity attached to it.
here is a sample code:
#Override
public void onAttach (Activity activity) {
super.onAttach(activity);
}

Fragments, Parameters, and ActionBar 'Up' Navigation

So I am trying to get some experience with Fragments, but I'm finding some roadblocks.
My current situation is as follows.
I have an activity that displays a List whose content is determined by Extra Intent parameters sent from the 'calling' activity.
This List activity uses ListFragment declared in the XML like so:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" android:background="#color/black">
<fragment class="com.pixlworks.NLC.DirectoryBrowse$ListingFragment"
android:id="#+id/listing"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Currently I get the parameter that indicates the type of content directly in the Fragment by accessing the Extra data of the Activity Intent (or saved Bundle if available):
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null)
mListingType = savedInstanceState.getString(Utils.DIRECTORY_TYPE_STORE_KEY);
else
mListingType = getActivity().getIntent().getStringExtra(Utils.DIRECTORY_TYPE_STORE_KEY);
// get content by type, create and set the adapter
}
Now part of my problem is that I am not sure this is the right way to 'pass' that parameter from the Activity to the Fragment.
On top of that, I am getting issues with this setup when using the Action Bar's UP Navigation. When I click on an item in this List Activity it goes to another activity showing the details of the selected item. From this detail activity:
If I use the back button, the List Activity is brought back from the stack as usual and everything works fine.
If I use the ActionBar's UP (despite following steps here), it would seem that a new instance is created instead of using the one in the stack and this new instance obviously is not getting the Extra parameter in the Intent. Since I am expecting the value to exist in the saved Bundle or in the Intent, my app crashes in this situation.
So to boil things down, I am not sure which of these to follow and how to make them work properly with 'UP' navigation:
A) Hold the 'type' parameter in a field in the Activity and save it in the Activity's Bundle onSaveInstanceState. In which case I am not sure how to then pass the value to the Fragment. In this case I would just need to make sure that UP calls the existing instance of the Activity List
B) Continue with my current setup of saving the value in the Fragment instead of the Activity, but again, how to handle the UP navigation correctly?
I know it is kind of multiple things I am asking here at the same time, but they are all connected, so I hope that I can get some help on this.
Thanks for any help in advance!
The UP navigation makes more sense to be used within the same activity level. That is the intention of the codes that you followed in the developers page. Because you started a new activity, if you want to return to previous activity like the back button you will need to call finish() to destroy the details activity first.
As for passing data from activity to fragment, when you create a new instance of fragment, you can pass the data to it as bundle, for example:
// in fragment class
public static MyFragment newInstance(Bundle arg) {
MyFragment f = new MyFragment();
f.setArguments(arg);
return f;
}
When you create a new fragment, you can call:
// in activity
Bundle arg = new Bundle();
int info = ...;
arg.putInt("INFO",info);
...
MyFragment mFragment = MyFragment.newInstance(arg);
Finally, to get the data in fragment:
int info = getArguments().getInt("INFO");
...
Instead of directly calling MyFragment mFragment = new MyFragment() to instantiate the fragment, you should use a static method to instantiate it. This is to prevent some crashes which might happen if you rotate the screen and the framework complains that it couldn't find a public empty constructor.
UPDATE
To answer your questions:
1) Say you start from activity A -> activity B. Then in activity B you press the up button. By logic of use, the up button will not bring you back to activity A, because its intention is to navigate one level up,but still inside, activity B. To return to activity A, you need to call finish() to destroy activity B first.
2) If your fragment is created in xml, you still can set arguments. In your xml, you set an id for the fragment android:id="#+id/fragment_id", then
// in activity
FragmentManager fm = getSupportFragmentManager(); // or getFragmentManager() if you don't have backward compatibility
MyFragment mFragment = fm.findFragmentById(R.id.fragment_id);
Bundle arg = new Bundle();
// put data blah blah
mFragment.setArguments(arg);
Just make sure you set the arguments before you use the fragment.
Simply said, intent is used when you pass data between calling activities; bundle is used when you want to pass data from activity to fragment.

Categories

Resources