For passing arguments to fragments, I see the best practice is to use a newInstance() method as such:
public static MyFragment newInstance(int myInt) {
MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("myInt", myInt);
myFragment.setArguments(args);
return myFragment;
}
and this would be instantiated as such:
int myInt = 123;
MyFragment myFragment = MyFragment.newInstance(myInt);
If I need to pass more variables then I would have to append whatever I'm adding to the newInstance() method. Example, if I'm also passing a string then my newInstance() method would look something like:
public static MyFragment newInstance(int myInt, String myStr) {
// code to put the int AND the string into the bundle
}
Now my question is: what's the difference between the above and below method, which passes the bundle?
public static MyFragment newInstance(Bundle args) {
MyFragment myFragment = new MyFragment();
myFragment.setArguments(args);
return myFragment;
}
instantiated by:
int myInt = 123;
Bundle args = new Bundle();
args.putInt("myInt", myInt);
MyFragment myFragment = MyFragment.newInstance(args);
I have been using the latter method so I can keep the parameter for newInstance() short. I have never seen an example using the latter method and I'm wondering if there is something wrong with it.
A bundle can hold multiple values.
Your first code can only send an integer value to the fragment, using a bundle you can send all you need in a single call.
Thats the main difference.
Hope this helps.
Related
Hi I'm learning now about Fragments and I have some doubts.
V1:
So the correct way to create a new Fragment should be with "newInstance" instead of a constructor and would be something like this:
Fragment fragment = MyFragment.newInstance(variable);
And the code on the class "MyFragment" should be this:
private static final String ARG_PARAM1 = "param1";
private int variable;
public MyFragment() {
//Empty constructor
}
New instance will receive the param and will put them on a Bundle and set the argument for the class
public static mi_fragment newInstance(Int param1) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putInt(ARG_PARAM1, param1);
fragment.setArguments(args);
return fragment;
}
Then after setting it the method onCreate will pick up the argument:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
variable = getArguments().getInt(ARG_PARAM1);
}
V2:
But I still can't see the problem with using the constructor and setting the arguments programatically:
Bundle bundle = new Bundle();
bundle.putInt("key", variable);
Fragment fragment = new MyFragment();
fragment.setArguments(bundle);
Then I pick up the argument on the method onCreate:
public void onCreate(#Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = this.getArguments();
if(bundle!=null){
variable = getArguments().getInt("key");
}
}
So I decided to search on the internet about this and I found a good answer but I still can't seem to understand it or when you could use it.
the way to pass stuff to your Fragment so that they are available after a Fragment is recreated by Android is to pass a bundle to the setArguments method.
This Bundle will be available even if the Fragment is somehow recreated by Android.
So my conclusion is that you keep the bundle alive and using the newInstance could make you return to the last fragment for example. But probably I'm wrong so I need help.
Question: What's the main difference between both and how do you benefit from newInstance ? An example.
When I first started android development, it was drilled into my head that creating a fragment should always be accomplished through a static method you create that uses the fragments default constructor, assigns arguments through a bundle, sets the bundle on the fragment and then returns it. For example:
public static MyFragment newInstance() {
MyFragment frag = new MyFragment();
Bundle args = new Bundle();
args.putInt("lifesAnswer", 42);
frag.setArguments(args);
return frag;
}
To my vague understanding, the reason we do this is because android will now implicitly handle restoring the fragment and its arguments without any additional work should that outcome happen and restoring is necessary.
Now what if I used the fragments interface in here to set an arbitrary value that could be passed into newInstance() as a parameter. For example:
public static MyFragment newInstance(int someValue) {
MyFragment frag = new MyFragment();
frag.setInstanceValue(someValue); // using a public setter
return frag;
}
for sake of clarity, setInstanceValue() is just your standard setter like:
public void setInstanceValue(int value) {
this.value = value;
}
Is this approach perfectly legal and safe to do. For context in practice here, I have a fragment that will be reused multiple times in a viewpager and all that is different about it is some text and the containers background image. I could just pass the resolved integer values and set them in the bundle for example passing R.String.my_string then setting that in the bundle and using it later but it got me thinking. Is it acceptable to implement the second approach or should I always use the first approach.
You can do something like that:
class YourFragment extends Fragment {
String valueOne;
String valueTwo;
public static YourFragment create(String valueOne, String valueTwo) {
YourFragment fragment = new YourFragment();
fragment.valueOne = valueOne;
fragment.valueTwo = valueTwo;
return fragment;
}
}
There is no need of using setters here.
Or use FragmentArgs https://github.com/sockeqwe/fragmentargs library.
Is it possible to have two different constructors for the same Fragment ?
Here's my case : I have Fragment_A and Fragment_B which are almost identically, except one gets an Integer and the other a String.
Can I have something like this :
public static VideoListFragment newInstanceA(String filterString) {
VideoListFragment myFragment = new VideoListFragment();
Bundle args = new Bundle();
args.putString(FILTER, filterString);
myFragment.setArguments(args);
return myFragment;
}
public static VideoListFragment newInstanceB(String subjectId) {
VideoListFragment myFragment = new VideoListFragment();
Bundle args = new Bundle();
args.putString(POSITION, subjectId);
myFragment.setArguments(args);
return myFragment;
}
If this is possible then how am I going to get in onCreate() the argument ? I have somehow to check if getArguments()` contains FILTER String or POSITION Integer.
You cannot use an exact same method signature to distinguish between different input behaviors. However, you can extend input parameters to make it more versatile. Something like this would help:
public class VideoListFragment extends Fragment {
public static VideoListFragment newInstance(String type, String val) {
VideoListFragment myFragment = new VideoListFragment();
Bundle args = new Bundle();
args.putString(type, val);
myFragment.setArguments(args);
return myFragment;
}
}
and later use it as below:
VideoListFragment frag1 = VideoListFragment.newInstance(FILTER, "value"); //for Filter
VideoListFragment frag2 = VideoListFragment.newInstance(POSITION, "value");//for Position
Android docs discourages the use of constructors while working with fragments; you should use bundle instead.
I want to pass realtime object from fragment to fragment.
I dont want to pass data.
I dont found way to do that.
The only way that I think is by intent but I am not sure.
i read about Singleton pattern,interfaces and Bundle,Global varible.
I would like to see example if it is possible to use share/pass object from one fragment to secend fragment. many Thanks.
Passing real objects between activities or fragments can be achieved through implementing model beans Parcelable or Serializable interface.
Parcelable: A Parcel is similar to a Bundle, but is more sophisticated and can support more complex serialization of classes. Applications can implement the Parcelable interface to define application-specific classes that can be passed around, particularly when using Services.
You can see how to implement Parcelable interface in this article here.
public class Foo implements Parcelable {
...
}
Serializable: A serializable interface is java standard serialization. You can read more about it here.
public class Foo implements Serializable {
private static final long serialVersionUID = 0L;
...
}
Suppose you have a class Foo implements Parcelable properly, to put it into Intent in an Activity:
Intent intent = new Intent(getBaseContext(), NextActivity.class);
Foo foo = new Foo();
intent.putExtra("foo", foo);
startActivity(intent);
To get it from intent in another activity:
Foo foo = (Foo) getIntent().getExtras().getParcelable("foo");
** EDIT **
As the original question asked for Fragments, then this is how it works:
Fragment fragment = new Fragment();
Foo foo = new Foo();
Bundle bundle = new Bundle();
bundle.putParcelable("Foo", foo);
fragment.setArguments(bundle);
To get it from bundle in another fragment:
Foo foo = (Foo) bundle.getParcelable("Foo");
Hope this helps!
Pass object from one Fragment to another Fragment can be acheived by using these simple steps:
First of all, you have to make your model class as Parcelable:
class Car implements Parcelable
Then implements the override functions of Parcelable class: e-g
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
}
From First Fragment:
// Set data to pass
MyFragment fragment = new MyFragment(); //Your Fragment
Car car = new Car(); // Your Object
Bundle bundle = new Bundle();
bundle.putParcelable("carInfo", car) // Key, value
fragment.setArguments(bundle);
// Pass data to other Fragment
getFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment)
.commit();`
On Second Fragment
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = this.getArguments();
if (bundle != null) {
Car receivedCar = bundle.getParcelable("carInfo"); // Key
}
}
Hope it will help :)
There is another method you can pass object by just making a simple setter function.
From your current fragment make object of target fragment. Your target fragment should have a setter function like
public void setObjectFunction(Object obj){this.mObj = obj}
YourFragment yourFragment = new YourFragment();
Object passingObj = new Object();
yourFragment.setObjectFunction(passingObj);
Then
getFragmentManager()
.beginTransaction()
.replace(R.id.container, yourFragment)
.commit();`
I'm sure this question has been asked lots of times but didn't find any useful answer.
I'm trying to implement Fragment which can be use in my app for X times (yes yes..even 100 and more..)
I want to create my fragment only once and in other times to pass bundle and make lifecycle do what he needs to do with the bundle data.
So far so good? So what i've found is a nice implementation from google document:
public static class MyFragment extends Fragment {
public MyFragment() { } // Required empty constructor
public static MyFragment newInstance(String foo, int bar) {
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putString(ARG_FOO, foo);
args.putInt(ARG_BAR, bar);
f.setArguments(args);
return f;
}
You can then access this data at a later point:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
// Use initialisation data
}
}
So what's wrong here? First, the Fragment MUST be an inner class. Second, if the class is static, and the function newInstance(...) is static, why they always create new instance without checking (By keeping a static member of the fragment) if it's not null like a Singleton ?
Assuming my fragment will be added many times to my fragment manager, can I use my fragment class as full Singleton? it means that each time I'll call:
public class MyFragment extends Fragment {
private static MyFragment sInstance;
public MyFragment() { } // Required empty constructor
public static MyFragment newInstance(String foo, int bar) {
if (sInstance==null) {
sInstance = new MyFragment();
}
Bundle args = new Bundle();
args.putString(ARG_FOO, foo);
args.putInt(ARG_BAR, bar);
f.setArguments(args);
return f;
}
}
Than use the new/exist fragment to make the transaction.
Hope I make myself clear, and didn't write some nonsense here :)
Thanks very much for your help