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.
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.
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.
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
I am currentley launching new activities and passing data liek this:
Intent myIntent = new Intent(c, TastePage.class);
myIntent.putExtra("taste", tempTaste);
c.startActivity(myIntent);
and then retrieving the data in the new activity with:
//get data from listview
Bundle extras = getIntent().getExtras();
String taste = extras.getString("taste");
In order to follow android design guidelines better I am implementing a navigation drawer and changing my activities into fragments. I am just a little confused on how to pass data between fragments.
For loading new fragments I am using this code:
FragmentManager man=getFragmentManager();
FragmentTransaction tran=man.beginTransaction();
Fragment_two=new BPTopTastes();
tran.replace(R.id.main, Fragment_two);//tran.
tran.addToBackStack(null);
tran.commit();
what can I implement into that code to pass data to the new fragment, and how can I retrieve that data in the new fragment?
Use a bundle:
Send data from source fragment
Fragment fragment = new Fragment();
final Bundle bundle = new Bundle();
bundle.putString("user_name", myusername);
fragment.setArguments(bundle);
And read from target fragment
Bundle args = getArguments();
if (args != null && args.containsKey("user_name"))
String userName = args.getString("user_name");
It is not the best idea but you can also use setters to set some values. You don't have to use Bundle.
public class YourFragment extends Fragment{
private String mUserName;
public void setUserName(String userName) {
mUserName = userName;
}
}
......
FragmentTransaction tran = getSupportFragmentManager().beginTransaction();
YourFragment fragment = new YourFragment();
fragment.setUserName(myUserName);
tran.add(R.id.main, fragment, "frag");
tran.addToBackStack(null);
tran.commit();
you could use Singleton Class to pass information between any component on your android process
you could access them from your services / activities / fragments ....
for example
class GlobalVars{
private GlobalVars global;
private String taste;//generate get/set
public static GlobalVars getinstance(){
if(global==null){global = new GlobalVars();}
return global}}
}
//By Calling
GlobalVars.getinstance()
//you will have a common access to the variables inside the class