I currently have an app that relies heavily on Intents and the extras given to them before starting the activity. The extras are used when calling a webservice which in turn supplies the content that needs to be shown
I am trying to convert that model, to one where I have a static Fragment (lets call it Player) at the bottom of my screen, and another Fragment (lets call it Content) above it which will show the main content. By selecting options on the main screen other content will be shown by replacing the Content Fragment.
But, these new Fragments are currently the Intents that rely on the extras so heavily.
Is there a way to replace a Fragment by a new one, but still be able to add extras to it?
If so, let's say I have the following piece of code:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_holder, new MusicAlbumList(), "albumlist");
ft.commit();
How would I add the extras to the MusicAlbumList?
If that's not possible, how will I get the data that's currently being passed through extras into my new Fragment before it force closes due to missing essential data?
Or you can do this
MusicAlbumList fragment = new MusicAlbumList();
Bundle args = new Bundle();
args.putString("StringName","Value here");
fragment.setArguments(args);
then do your replace stuff. Then in the fragments onStart or onCreate call this.getArguments(); to pull the bundle, then get your extras out of there.
Change the constructor of MusicAlbumList from default constructor to one with arguments like new MusicAlbumList(int arg1, ...) and pass the values you want to set through the constructor
Related
Is that compulsory to set data using bundle and set argument?
What is wrong Here?
MyFragment frag = new MyFragment ()
frag.setData(mSchoolData);
//add to back stack stuff.
by using the setArgs() you will ensure that this Fragment can be recreated due to lifecycle event...while by passing arguments with your own setters it may not work properly under certain circustances. That is why it is absolutely recommended to either uset Args OR use Intent extras, these will always be automatically provided by the system if the fragment gets recreated.
I am still new to Android programming, i did search for checking the difference between Bundle in Activity and in Fragment and what are the use cases of these.
Please give me the difference with exact use case.
Thanks for your help :)
It is a different bundle.
When you create activity you get system or custom bundle (with save data).
When you create fragment you can put same date from activity (ex same id) but this will be a new bundle.
If you want same data in frament like super.onSaveInstanceState(savedInstanceState); in activity, you need to return this bundle to activity, and for restoring you must parse this data in activity and put to 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.
Is there a way to send a bundle to a fragment without actually putting that fragment into a container? I ask because I want to take a few Strings from an EditTexts input field and then call them in another fragment at a later time.
I have code like this
Bundle bundle = new Bundle();
Fragment fragment = new Assessment_Fragment();
bundle.putString("company", companyName);
bundle.putString("project", projectName);
fragment.setArguments(bundle);
getFragmentManager().beginTransaction()
.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out)
.replace(R.id.header_fragment_container, fragment).commit();
In this code I pass the bundle with the transaction but I also replace the existing fragment in the "header_fragment_container". I don't want to do that. I just want to pass the bundled data to the specified fragment. When that fragment is eventually called I will receive the bundle. Is this possible?
Is this possible?
Sure. Just delete the last Java statement (getFragmentManager()....).
Now, as to whether it is sensible for you to create a fragment, then never add it to the fragment manager (either with a UI or not), that's another matter. Java supports lots of classes; Fragment is just one of them. If you need a generic data container, choose something lighter weight, such as just the Bundle itself.
So I've learned that I need an empty constructor in order for my fragments not to crash on reinitialization. My problem is that I use lists of data for my fragments when they are initialized (at least some of them). So what would be a good way to start new fragments with a list of data. Should I in the OnCreate() make a getData method which gets the data from some other source or what would be a proper approach?
Feeding the bundle with data really wouldn't be a very good approach as I have a lot of data.
So let's take a case (I understand it tons better that way).
When a user clicks on a button the fragment is started. What I used to do was creating a new fragment this way:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.center_container, new DetailFragment(item));
fragmentTransaction.addToBackStack(DETAIL_TAG);
fragmentTransaction.commit();
Then in my fragment:
public DetailFragment(EventItem item) {
mItem = item;
mPlaces = Database.getContainerPlaces(getActivity()).getValidItems();
}
I can't give all the data to a bundle, so that wouldn't work. So what should I do?
A: Should I initialize the fragment with the empty constructor and then from my activity use setters to set the data directly in the fragment? However, won't I be missing data if the user presses home, Android closes the fragment and the user later returns?
B: Should I initialize the fragment with factory pattern and call setRetainInstance(true), give the fragment a key for identifying the data, and then letting the fragment fetch the data needed in onCreateView from some third source?
C: Should I just make an empty constructor and then in onCreate() fetch the data needed for the fragment?
It should be noted that the app is locked in portrait so the issue is primarily with maintaining the objects when Android closes and the user restarts.
So what would be a good way to start new fragments with a list of data.
Use the factory pattern and the "arguments" Bundle, such as:
package com.commonsware.empublite;
import android.os.Bundle;
public class SimpleContentFragment extends AbstractContentFragment {
private static final String KEY_FILE="file";
protected static SimpleContentFragment newInstance(String file) {
SimpleContentFragment f=new SimpleContentFragment();
Bundle args=new Bundle();
args.putString(KEY_FILE, file);
f.setArguments(args);
return(f);
}
#Override
String getPage() {
return(getArguments().getString(KEY_FILE));
}
}
If you are retaining your fragment instance, you should be able to get away with just using ordinary setters to put stuff in data members. The "arguments" Bundle is retained as part of configuration changes, so for non-retained instances, this is the way to ensure your setup data is retained if the user rotates the screen, etc.