My app has one main Activity with a FrameLayout to load some Fragments, most of them managing http data requests.
When the connection is lost, I start a specific Fragment "NoConnectionFragment" with a text "No internet connection" and a Button "Retry".
When clicking on "Retry" I want to reload the previous Fragment, that failed to load the data, with some data to specify the state of the Fragment.
I can pass the state data in a Bundle, but how can I pass to the NoConnectionFragment data on what Fragment to reload? Is there any way to pass the Fragment directly?
How about just storing the name of the previous fragment.
ex: If FragmentA is active before NoConnectionFragment is launched, keep a static variable int the parent activity that stores this state.
static int PREV_FRAGMENT_INDEX; //index can be 0 for FragmentA, 1 for FragmentB etc
When retry button is clicked, just check this variable to launch the appropriate fragment:
onClick(View v)
{
switch(getActivity().PREV_FRAGMENT_INDEX)
{
case 0: //launch Fragment A
case 1: //launch Fragment B etc
}
}
You can't pass a fragment to another Fragment because fragments are not supposed to work that way and also fragment are managed by FragmentManagers. What you could do is to pass data from a Fragment to another.
Related
EDIT:
Hi.
I have MainActivity on which I open Fragment A with:
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.db_container, Fragment A);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragmentTransaction.commit();
This fragment uses LiveData and shows RecyclerView list.
Then after user select some category I open Fragment B with:
fragmentTransaction.hide(Fragment A);
fragmentTransaction.add(R.id.db_container, Fragment B);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragmentTransaction.commit();
This fragment also uses LiveData and shows RecyclerView list.
When the user selects a single item and wants to edit it, they is moved to EditItem Activity.
After user do some changes and saved it, the activity ends with the command: finish();
User back to Fragment B and RecyclerView list is updated.
The problem is that before the list of fragment B is updated, the list in fragment A is updated first.
Exactly code in Fragment A:
item_viewmodel.getAllCategoryModel().observe(getViewLifecycleOwner(), new Observer<List<Items>>() {
#Override
public void onChanged(List<Items> myLists) {
// Here the code is executed first before updating the list in fragment B.
}
});
So the question is.
How to only update list in Fragment B ?
Later from fragment B I will update list in Fragment A manually, using interface callback.
Optionally I want to update the List in Fragment B as first then list in Fragment A.
If it possible I don't want to remove fragment A, only hide it.
As I understand that you want to update fragment B only and then manually update fragment A.
I assume that the data you are using in your lists is shared between fragment A and B, and thats why fragment A is updated because A and B have observers on same objects.
You can use a boolean variable in A boolean shouldUpdate = true;
and make it false when you move to B.
Also inside you observer in A do check if(shouldUpdate) .
My android application have BottomNavigationBar. It contains 5 fragments. One of its fragment contain nested fragment which is mutiple step process.
first nested fragment contain next button.Second nested fragment contain previous and next button.Third nested fragment contain previous and submit button. Each fragment have different EditText.
After adding the values in first fragment, when i click next button it goes to second fragment. In second fragment when i click previous button it goes to first fragment again and same process applies to second and third fragment
My questions is:
1)When previous button in second fragment is clicked, i want all the values of EditText in first fragment as it is and when again next button of first fragment is clicked, i want all the values of EditText in second fragment as it is. Is there any way to do this?
2)I want all the EditText values of all nested fragments when user clicked on submit button in third fragment.How to do that?
Yes,
This can be achieved using two ways,
1) Fragment savedInstanceState
https://stackoverflow.com/a/17135346/7316675
2) Keep you values stored at some activity or application level, and access it on resume of fragment screen
You could use a Bundle to store the values and then restore them when you restore the fragment. You could either do this in onStop() (recommended) or onPause().
Private static final String KEY_ADDRESS = "ADDRESS" ;
#Override
Public void onstop(){
Bundles state = new Bundle ();
String address = etv1.getText().toString();
// Get more strings from the etvs
state.putString(KEY_ADDRESS, address);
// Store more strings into the bundle
setInitialSavedState(state)
}
To restore the values you use either the saved instance state the system passed to onCreate() , or pass the bundle to a self created public bundle in onCreate() and access in onResume() like so:
String address = bundle.getString(KEY_ADDRESS);
As for the results being passed you could communicate the bundles to their parent activity whenever the next or previous button is pressed and do with it as you please when submit is pressed. Learn more on How to do that from the docs or this answer on how to do that
Solution of my first question.
I have used popBackStack() method of FragmentManager. Using this i can go back to the previous fragment of stack.I have added this code in OnClickListener of previous button
FragmentManager fm = getActivity().getSupportFragmentManager();
if(fm.getBackStackEntryCount()!=0){
fm.popBackStack();
}
Solution of my second question:
use method setArguments() to set the values and getArguments() to get the values
Here is my problem area:
I have a Fragment A. Once it is attached, in its onCreateView, I load a webservice to fetch the data from the server and after that I set that data on the list view using a Base Adapter. Now on the Item Clicks of the list view I replace the Fragment A with Fragment B using replace Methods of the Fragment Transactions and addtoBackstack("FragmentA").
FragmentManager fm =getActivity().getFragmentManager();
fm.beginTransaction().replace(R.id.content_frame, Fragment B).commit();
Now here when I press back button on Fragment B, it takes me to Fragment A but the webservice again starts loading.
My Problem: I just want that when it returns to Fragment A, it should show its previous state and should not call the webservices again.
Thanks
OnCreateView for a fragment runs on the creation of the view every time it needs to be drawn. By going back you are causing the view to be recreated and hence the webservices are loading again.
I believe that if you only want the web services to load once then you could move the code to the "onCreate" method instead, but its probably a better idea to move this code to "onResume" instead and include some logic that checks whether you need to load your webservices again or not.
This way everytime the fragment is paused and then loaded again you could ensure that the fragment still has everything it needs.
(source: xamarin.com)
EDIT:
So for example you could have
#Override
public void onResume() {
super.onResume(); // Always call the superclass method first
if (data == null) { //Or list is empty?
getWebData()
}
}
I have a fragment that loads json data from server
and display it in a list.
my problem is that when I move to a different fragment on the same activity, that fragments data is being deleted.
when I go back to that fragment all the data is empty, and it's loading again from the server.
is there a way to keep the fragment alive in the background?
that's the code I use to switch fragments :
private void fragmentSwitch() {
this.getFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out)
.replace(R.id.main_fragment_container, fragment)
.commit();
}
Thanks a lot in advance.
I see 3 different options:
Your activity keeps the data, so you can switch between fragments without loosing data.
After your fragment is created you can call one method of the activity to get the data.
For example you can create a method like getJsonData() in your activity and call it from your fragment (YourActivity)getActivity().getJsonData()
You save data in SharedPreferences and access it from you fragment
You save data in Database and access it from your fragment
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.