Passing arguments from Fragment to VIewModel/reuse Fragment - android

I'm learning how to build apps using mvvm approach. In my app I have activity with two fragments and ViewPager. So what I'm trying to solve now is how to reuse fragment class, because the are basically identical, the only difference is the data that I want to observe from ViewModel depending on which fragment is displayed. So what I've done so far
stateAdapter.addFragment(BenchmarksFragment.newInstance("collections"), getString(R.string.collection));
stateAdapter.addFragment(BenchmarksFragment.newInstance("maps"), getString(R.string.map));
In activity I have adapter that creates fragments with arguments.
public static BenchmarksFragment newInstance(String value) {
final BenchmarksFragment fragment = new BenchmarksFragment();
final Bundle args = new Bundle();
args.putString(KEY, value);
fragment.setArguments(args);
return fragment;
}
Further in fragment onCreate I pass argument to VMFactory
String arg = getArguments() == null ? DEFAULT : getArguments().getString(KEY);
BenchmarksVMFactory factory = new BenchmarksVMFactory(requireActivity().getApplication(), arg);
In ViewModel constructor I simply assign this argument to variable
public BenchmarksViewModel(#NonNull Application application, String arg) {
super(application);
this.arg = arg;
Log.d("TAG", "BenchmarksViewModel: " + arg);
}
Through logs I see that iI get only argument from first fragment "collections".
So my question is how to correctly pass arguments to ViewModel and further manage the logic of displaying the right list(for "collections" fragment collectionsList and for "maps" fragment mapList) using same fragment class?
If you can advise me articles/examples to read I would appreciate that.
UPDATE
I was able to get argument from second fragment by changing
model = new ViewModelProvider(requireActivity(), factory).get(BenchmarksViewModel.class);
requireActivity() to getViewModelStore()
model = new ViewModelProvider(getViewModelStore(), factory).get(BenchmarksViewModel.class);

Related

Android fragment factory method vs constructor overloading

First of all, I already know that the FragmentManager often destroys then re-creates the Fragment using the default constructor. The coders must save important things in a Bundle of arguments once in the factory method, then take them out every time the Fragment is re-created in onCreate(Bundle).
public class MyFragment extends Fragment {
private static final String MY_STRING_CONSTANT = "param";
private int mIntegerMember;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mIntegerMember= getArguments().getInt(MY_STRING_CONSTANT);
}
}
My question is, is there any difference between this:
// Inside MyFragment.java
public MyFragment() {
// No-argument constructor required by the FragmentManager.
}
public static MyFragment newInstance(int param) {
// Factory method
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putInt(MY_STRING_CONSTANT, param);
fragment.setArguments(args);
return fragment;
}
// Somewhere else
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.frame_layout, MyFragment.newInstance(123)).commit();
And this:
// Inside MyFragment.java
public MyFragment() {
// No-argument constructor required by the FragmentManager.
}
public MyFragment(int param) {
// Parameterized constructor
Bundle args = new Bundle();
args.putInt(MY_STRING_CONSTANT, param);
setArguments(args);
}
// Somewhere else
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.frame_layout, new MyFragment(123)).commit();
I can see nothing which prevents the FragmentManager from calling the no-argument constructor. And the data I save in the parameterized constructor (in a Bundle object) will be preserved and restore during onCreate(), just like when I use the factory method.
Android never directly invokes a non-default constructor (nor a factory method) - technically, it doesn't really matter which you use. You can call setArguments (in an arbitrary method, even in a constructor) any time before you add the Fragment and that bundle will be saved/restored for you if the Fragment is recreated. Views also have special constructors invoked by Android, but you can make your own with arbitrary arguments if you wish (they just won't get invoked by Android).
Code for Fragment.setArguments:
/**
* Supply the construction arguments for this fragment. This can only
* be called before the fragment has been attached to its activity; that
* is, you should call it immediately after constructing the fragment. The
* arguments supplied here will be retained across fragment destroy and
* creation.
*/
public void setArguments(Bundle args) {
if (mIndex >= 0) {
throw new IllegalStateException("Fragment already active");
}
mArguments = args;
}
Code for Fragment.instantiate:
/**
* Create a new instance of a Fragment with the given class name. This is
* the same as calling its empty constructor.
*
* #param context The calling context being used to instantiate the fragment.
* This is currently just used to get its ClassLoader.
* #param fname The class name of the fragment to instantiate.
* #param args Bundle of arguments to supply to the fragment, which it
* can retrieve with {#link #getArguments()}. May be null.
* #return Returns a new fragment instance.
* #throws InstantiationException If there is a failure in instantiating
* the given fragment class. This is a runtime exception; it is not
* normally expected to happen.
*/
public static Fragment instantiate(Context context, String fname, Bundle args) {
try {
Class<?> clazz = sClassMap.get(fname);
if (clazz == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = context.getClassLoader().loadClass(fname);
sClassMap.put(fname, clazz);
}
Fragment f = (Fragment)clazz.newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.mArguments = args;
}
return f;
} catch (ClassNotFoundException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
}
}
Fragment.instantiate is called when Android wants to create an instance of your Fragment. It simple calls through to Class.newInstance, which is a Java method to create a class using the default, zero-arg constructor. Looking at this code, there seems to be no problem with creating additional constructor and in calling setArguments within it.
As an a convention, it is typical to use a factory method when working with Fragments. Most official sample Fragment code also uses factory methods. Here are some possible reasons why:
If you are writing a custom constructor (with arguments), you will have to specify a zero-arg constructor as well. A common mistake is to create a custom constructor but forget to define a zero-arg constructor - this will lead to a crash when Android tries to invoke the zero-arg constructor when recreating your Fragment.
When creating a custom constructor, you may be tempted to directly assign the constructor arguments to fields. This is how pretty much any other Java class is written (and therefore how you will naturally want to write classes). Since Android will only invoke the zero-arg constructor on a Fragment, this data will not be available to any recreated instances. As you already know, using setArguments is the way to solve this. Even though you can do this within a constructor, using a factory method makes it more obvious that this class cannot be constructed in a normal way, reducing the possibility of committing the above mistake (or similar).
The FragmentManager implementation calls the default constructor of the Fragment. I would think that it would involve a lot of overhead to be able to determine which arguments to pass to a non-default constructor, so the Android team decided to go the Bundle route. If you do use a non-default constructor the data you pass to it, will not be kept during recreation, hence you will end up with null pointers. By using the setArguments()/getArguments() mechanism you guarantee that FragmentManager will initialize the Fragment correctly.
When you do this call:
transaction.add(R.id.frame_layout, new MyFragment(123));
it will be guaranteed that the first time will be fine. Now, lets say that the user rotates the screen (setRetainInstance() is not set), the FragmentManager will create a new instance of the Fragment by calling:
new MyFragment(); //the default constructor.
This means that all the variables that were supposed to be initialized in the non-default constructor will be null.
The docs tell us to avoid overloading Fragment's constructor, I would stick to their rules. You might get some unexpected behavior if you try to overload the constructor (even if you do setArguments() in the overloaded constructor).

Parameterised constructor for a fragment in android

I have the following constructor inside a fragment:-
public PlaceDialogFragment(Place place, DisplayMetrics dm){
super();
this.mPlace = place;
this.mMetrics = dm;
}
I have also tried this:-
public static final DialogFragment newInstance(Place place, DisplayMetrics dm)
{
DialogFragment fragment = new DialogFragment();
Bundle bundle = new Bundle(2);
bundle.putParcelable("Place", place);
bundle.putLong("Metrics", dm);
fragment.setArguments(bundle);
return fragment ;
}
But There is an error on bundle.putLong("Metrics", dm) line
Here Place is a class which implements the Parceable interface
But i get an error saying:-
Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead
Any suggestions how to resolve this?
The reason you should use default constructors and pass arguments as bundles is because when the system restores your fragment state, it's gonna call the default constructor and restore the bundle. If you get your parameters from the bundle, then you can restore the state correctly.
Using your current method, everything you do in your custom constructor will be lost when the fragment is recreated.
See this answer for an example.
Use setArguments instead, while transacting the fragment, pass the constructor params in bundle, then use them in fragment using getArguments()
How to use setArguments() and getArguments() methods in Fragments?
What's the point of setArguments?
http://developer.android.com/reference/android/app/Fragment.html
You added :
bundle.putLong("Metrics", dm);
Do like this:
bundle.putFloat("Metrics_density", dm.density);
Refer to DisplatMetrics documentation and add arguments separately, or add DisplayMetrics object as static in your Application memory, and use it from anywhere.
DisplayMetrics is not a Long object, not even parcelable, add relevant DisplayMetrics fields in bundle instead.
public static final PlaceDialogFragment newInstance(Place place, DisplayMetrics dm)
{
PlaceDialogFragment fragment = new DialogFragment();
Bundle bundle = new Bundle(2);
bundle.putParcelable("Place", place);
bundle.putFloat("Metrics_density", dm.density);
//bundle.putFloat("Metrics_other", dm.<other fields>);
fragment.setArguments(bundle);
return fragment ;
}
Note : Don't use public constructor with parameters.
The error message is right!
Avoid the use of Parameterized constructors to Fragment/Activity..
You can do "quick-fix" by going into Lint settings and excluding the rule + adding a default constructor. But quick fix is not the way. This will result in problem.
Consider this case, you just rotate the screen, then your fragment gets destroyed and recreated when you call super.onCreate(savedState) of your activity, which will call default constructor => this results in NullPointerException.
So respect the Android Lint, make use of setArguments() to pass the instance of Place. If Place is your model class, make it Parcelable
you can get arguments by calling getArguments() inside your fragment
Ideally, a fragment needs to reconstruct itself using only its arguments. A parameterised constructor does not work well for this as the parameters are lost in the case of a (for example) device orientation change (although you can mitigate this with a call to setRetainInstance).
Use a static method instead of a constructor to create your fragment.
e.g.
public static MyFragment newInstance() {
MyFragment f = new FragStateList_();
Bundle args = new Bundle();
args.putInt("someInt", someInt);
args.putString("someString", someString);
f.setArguments(args);
return f;
}
You should then include a default constructor so the system car re-create your fragment when it needs to.
In your updated question, you are attempting to place a DisplayMetrics objects into the bundle as a Long. The types are not compatible. Do not pass in the DisplayMetrics. Instead, try this in your fragment to get the DisplayMetrics object.
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
Ideally, a fragment needs to reconstruct itself using only its arguments. A parameterised constructor does not work well for this as the parameters are lost in the case of a (for example) device orientation change (although you can mitigate this with a call to setRetainInstance).
Like Kuffs wrote, the proper way to do this is to have a static method that calls a (default) constructor and after it is initialized, it adds your custom values. You can place arguments in that method, for example:
public static PlaceDialogFragment newInstance(Place place, DisplayMetrics dm) {
PlaceDialogFragment f = new PlaceDialogFragment(); //alternatively new Fragment()
f.mPlace = place;
f.mMetrics = dm;
return f;
}
Then from you Activity, you call it like:
PlaceDialogFragment pdf = PlaceDialogFragment.newInstance(param1, param2);

Why do I want to avoid non-default constructors in fragments?

I am creating an app with Fragments and in one of them, I created a non-default constructor and got this warning:
Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead
Can someone tell me why this is not a good idea?
Can you also suggest how I would accomplish this:
public static class MenuFragment extends ListFragment {
public ListView listView1;
Categories category;
//this is my "non-default" constructor
public MenuFragment(Categories category){
this.category = category;
}....
Without using the non-default constructor?
It seems like none of the answers actually answer "why use bundle for passing parameters rather than non default constructors"
The reason why you should be passing parameters through bundle is because when the system restores a fragment (e.g on config change), it will automatically restore your bundle.
The callbacks like onCreate or onCreateView should read the parameters from the bundle - this way you are guaranteed to restore the state of the fragment correctly to the same state the fragment was initialised with (note this state can be different from the onSaveInstanceState bundle that is passed to the onCreate/onCreateView)
The recommendation of using the static newInstance() method is just a recommendation. You can use a non default constructor but make sure you populate the initialisation parameters in the bundle inside the body of that constructor. And read those parameters in the onCreate() or onCreateView() methods.
Make a bundle object and insert your data (in this example your Category object). Be careful, you can't pass this object directly into the bundle, unless it's serializable.
I think it's better to build your object in the fragment, and put only an id or something else into bundle. This is the code to create and attach a bundle:
Bundle args = new Bundle();
args.putLong("key", value);
yourFragment.setArguments(args);
After that, in your fragment access data:
Type value = getArguments().getType("key");
That's all.
Your Fragment shouldn't have constructors because of how the FragmentManager instantiates it.
You should have a newInstance() static method defined with the parameters you need, then bundle them and set them as the arguments of the fragment, which you can later access with the Bundle parameter.
For example:
public static MyFragment newInstance(int title, String message) {
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle(2);
bundle.putInt(EXTRA_TITLE, title);
bundle.putString(EXTRA_MESSAGE, message);
fragment.setArguments(bundle);
return fragment ;
}
And read these arguments at onCreate:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
title = getArguments().getInt(EXTRA_TITLE);
message = getArguments().getString(EXTRA_MESSAGE);
//...
}
This way, if detached and re-attached, the object state can be stored through the arguments, much like bundles attached to Intents.
If you use parameter for some class. try this
SomeClass mSomeInstance;
public static final MyFragment newInstance(SomeClass someInstance){
MyFragment f = new MyFragment();
f.mSomeInstance = someInstance;
return f;
}
I think, there is no difference between static constructor and two constructors (empty and parametrized one that stores arguments into a Fragment's arguments bundle), most probably, this rule of thumb is created to reduce probability of forgetting to implement no-arg constructor in Java, which is not implicitly generated when overload present.
In my projects I use Kotlin, and implement fragments with a primary no-arg constructor and secondary constructor for arguments which just stores them into a bundle and sets it as Fragment arguments, everything works fine.
If fragment uses non-default constructors after configuration changing the fragment will lose all data.

Passing objects in to Fragments

I've been working with lots of Fragments recently and have been using two distinct methods of passing in objects to the Fragments, but the only difference that I can see is that in the approach taken by FragmentOne below, the object you pass in must implement the Serializable interface (and everything associated with that).
Are there any benefits to using one over the other?
public class FragmentOne extends Fragment {
public static final String FRAGMENT_BUNDLE_KEY =
"com.example.FragmentOne.FRAGMENT_BUNDLE_KEY";
public static FragmentOne newInstance(SomeObject someObject) {
FragmentOne f = new FragmentOne();
Bundle args = new Bundle();
args.putSerializable(FRAGMENT_BUNDLE_KEY, someObject);
f.setArguments(args);
return f;
}
public SomeObject getSomeObject() {
return (SomeObject) getArguments().getSerializable(FRAGMENT_BUNDLE_KEY);
}
}
and
public class FragmentTwo extends Fragment {
SomeObject mSomeObject;
public static FragmentTwo newInstance(SomeObject someObject) {
FragmentTwo fragment = new FragmentTwo();
fragment.setSomeObject(someObject);
return fragment;
}
public void setSomeObject(SomeObject someObject) {
mSomeObject = someObject;
}
}
There are 3 ways to pass objects to a fragment
They are:
Passing the object through a setter is the fastest way, but state will not be restored automatically.
setArguments with Serializable objects is the slowest way (but okay for small objects, I think) and you have automatic state restoration.
Passing as Parcelable is a fast way (prefer it over 2nd one if you have collection of elements to pass), and you have automatic state restoration.
http://developer.android.com/reference/android/os/Parcelable.html
for Collection such as List :
I wanted to share my experience.
you need to implement Parcelable
Just use the putParcelableArrayList method.
ArrayList<LClass> localities = new ArrayList<LClass>;
...
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(KEY_LClass_LIST, localities);
fragmentInstance.setArguments(bundle);
return fragmentInstance;
And retrieve it using...
localities = savedInstanceState.getParcelableArrayList(KEY_LCLass_LIST);
So, unless you need the custom ArrayList for some other reason, you can avoid doing any of that extra work and only implement Parcelable for your Locality class.

Best practice for instantiating a new Android Fragment

I have seen two general practices to instantiate a new Fragment in an application:
Fragment newFragment = new MyFragment();
and
Fragment newFragment = MyFragment.newInstance();
The second option makes use of a static method newInstance() and generally contains the following method.
public static Fragment newInstance()
{
MyFragment myFragment = new MyFragment();
return myFragment;
}
At first, I thought the main benefit was the fact that I could overload the newInstance() method to give flexibility when creating new instances of a Fragment - but I could also do this by creating an overloaded constructor for the Fragment.
Did I miss something?
What are the benefits of one approach over the other? Or is it just good practice?
If Android decides to recreate your Fragment later, it's going to call the no-argument constructor of your fragment. So overloading the constructor is not a solution.
With that being said, 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.
So, for example, if we wanted to pass an integer to the fragment we would use something like:
public static MyFragment newInstance(int someInt) {
MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("someInt", someInt);
myFragment.setArguments(args);
return myFragment;
}
And later in the Fragment onCreate() you can access that integer by using:
getArguments().getInt("someInt", 0);
This Bundle will be available even if the Fragment is somehow recreated by Android.
Also note: setArguments can only be called before the Fragment is attached to the Activity.
This approach is also documented in the android developer reference: https://developer.android.com/reference/android/app/Fragment.html
The only benefit in using the newInstance() that I see are the following:
You will have a single place where all the arguments used by the fragment could be bundled up and you don't have to write the code below everytime you instantiate a fragment.
Bundle args = new Bundle();
args.putInt("someInt", someInt);
args.putString("someString", someString);
// Put any other arguments
myFragment.setArguments(args);
Its a good way to tell other classes what arguments it expects to work faithfully(though you should be able to handle cases if no arguments are bundled in the fragment instance).
So, my take is that using a static newInstance() to instantiate a fragment is a good practice.
There is also another way:
Fragment.instantiate(context, MyFragment.class.getName(), myBundle)
While #yydl gives a compelling reason on why the newInstance method is better:
If Android decides to recreate your Fragment later, it's going to call
the no-argument constructor of your fragment. So overloading the
constructor is not a solution.
it's still quite possible to use a constructor. To see why this is, first we need to see why the above workaround is used by Android.
Before a fragment can be used, an instance is needed. Android calls YourFragment() (the no arguments constructor) to construct an instance of the fragment. Here any overloaded constructor that you write will be ignored, as Android can't know which one to use.
In the lifetime of an Activity the fragment gets created as above and destroyed multiple times by Android. This means that if you put data in the fragment object itself, it will be lost once the fragment is destroyed.
To workaround, android asks that you store data using a Bundle (calling setArguments()), which can then be accessed from YourFragment. Argument bundles are protected by Android, and hence are guaranteed to be persistent.
One way to set this bundle is by using a static newInstance method:
public static YourFragment newInstance (int data) {
YourFragment yf = new YourFragment()
/* See this code gets executed immediately on your object construction */
Bundle args = new Bundle();
args.putInt("data", data);
yf.setArguments(args);
return yf;
}
However, a constructor:
public YourFragment(int data) {
Bundle args = new Bundle();
args.putInt("data", data);
setArguments(args);
}
can do exactly the same thing as the newInstance method.
Naturally, this would fail, and is one of the reasons Android wants you to use the newInstance method:
public YourFragment(int data) {
this.data = data; // Don't do this
}
As further explaination, here's Android's Fragment Class:
/**
* Supply the construction arguments for this fragment. This can only
* be called before the fragment has been attached to its activity; that
* is, you should call it immediately after constructing the fragment. The
* arguments supplied here will be retained across fragment destroy and
* creation.
*/
public void setArguments(Bundle args) {
if (mIndex >= 0) {
throw new IllegalStateException("Fragment already active");
}
mArguments = args;
}
Note that Android asks that the arguments be set only at construction, and guarantees that these will be retained.
EDIT: As pointed out in the comments by #JHH, if you are providing a custom constructor that requires some arguments, then Java won't provide your fragment with a no arg default constructor. So this would require you to define a no arg constructor, which is code that you could avoid with the newInstance factory method.
EDIT: Android doesn't allow using an overloaded constructor for fragments anymore. You must use the newInstance method.
Some kotlin code:
companion object {
fun newInstance(first: String, second: String) : SampleFragment {
return SampleFragment().apply {
arguments = Bundle().apply {
putString("firstString", first)
putString("secondString", second)
}
}
}
}
And you can get arguments with this:
val first: String by lazy { arguments?.getString("firstString") ?: "default"}
val second: String by lazy { arguments?.getString("secondString") ?: "default"}
I disagree with yydi answer saying:
If Android decides to recreate your Fragment later, it's going to call
the no-argument constructor of your fragment. So overloading the
constructor is not a solution.
I think it is a solution and a good one, this is exactly the reason it been developed by Java core language.
Its true that Android system can destroy and recreate your Fragment. So you can do this:
public MyFragment() {
// An empty constructor for Android System to use, otherwise exception may occur.
}
public MyFragment(int someInt) {
Bundle args = new Bundle();
args.putInt("someInt", someInt);
setArguments(args);
}
It will allow you to pull someInt from getArguments() latter on, even if the Fragment been recreated by the system. This is more elegant solution than static constructor.
For my opinion static constructors are useless and should not be used. Also they will limit you if in the future you would like to extend this Fragment and add more functionality to the constructor. With static constructor you can't do this.
Update:
Android added inspection that flag all non-default constructors with an error.
I recommend to disable it, for the reasons mentioned above.
I'm lately here. But somethings I just known that might help you a bit.
If you are using Java, there is nothing much to change. But for Kotlin developers, here is some following snippet I think that can make you a basement to run on:
Parent fragment:
inline fun <reified T : SampleFragment> newInstance(text: String): T {
return T::class.java.newInstance().apply {
arguments = Bundle().also { it.putString("key_text_arg", text) }
}
}
Normal call
val f: SampleFragment = SampleFragment.newInstance("ABC")
// or val f = SampleFragment.newInstance<SampleFragment>("ABC")
You can extend the parent init operation in child fragment class by:
fun newInstance(): ChildSampleFragment {
val child = UserProfileFragment.newInstance<ChildSampleFragment>("XYZ")
// Do anything with the current initialized args bundle here
// with child.arguments = ....
return child
}
Happy coding.
Best practice to instance fragments with arguments in android is to have static factory method in your fragment.
public static MyFragment newInstance(String name, int age) {
Bundle bundle = new Bundle();
bundle.putString("name", name);
bundle.putInt("age", age);
MyFragment fragment = new MyFragment();
fragment.setArguments(bundle);
return fragment;
}
You should avoid setting your fields with the instance of a fragment. Because whenever android system recreate your fragment, if it feels that the system needs more memory, than it will recreate your fragment by using constructor with no arguments.
You can find more info about best practice to instantiate fragments with arguments here.
Since the questions about best practice, I would add, that very often good idea to use hybrid approach for creating fragment when working with some REST web services
We can't pass complex objects, for example some User model, for case of displaying user fragment
But what we can do, is to check in onCreate that user!=null and if not - then bring him from data layer, otherwise - use existing.
This way we gain both ability to recreate by userId in case of fragment recreation by Android and snappiness for user actions, as well as ability to create fragments by holding to object itself or only it's id
Something likes this:
public class UserFragment extends Fragment {
public final static String USER_ID="user_id";
private User user;
private long userId;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
userId = getArguments().getLong(USER_ID);
if(user==null){
//
// Recreating here user from user id(i.e requesting from your data model,
// which could be services, direct request to rest, or data layer sitting
// on application model
//
user = bringUser();
}
}
public static UserFragment newInstance(User user, long user_id){
UserFragment userFragment = new UserFragment();
Bundle args = new Bundle();
args.putLong(USER_ID,user_id);
if(user!=null){
userFragment.user=user;
}
userFragment.setArguments(args);
return userFragment;
}
public static UserFragment newInstance(long user_id){
return newInstance(null,user_id);
}
public static UserFragment newInstance(User user){
return newInstance(user,user.id);
}
}
use this code 100% fix your problem
enter this code in firstFragment
public static yourNameParentFragment newInstance() {
Bundle args = new Bundle();
args.putBoolean("yourKey",yourValue);
YourFragment fragment = new YourFragment();
fragment.setArguments(args);
return fragment;
}
this sample send boolean data
and in SecendFragment
yourNameParentFragment name =yourNameParentFragment.newInstance();
Bundle bundle;
bundle=sellDiamondFragments2.getArguments();
boolean a= bundle.getBoolean("yourKey");
must value in first fragment is static
happy code
Ideally we shouldn't pass anything in the fragment constructor, fragment constructor should be empty or default.
Now the second question is, what if we want to pass interface variable or parameters-
We should use Bundle to pass data.
For Interface we can putParceble in bundle and make that interface implement parceble
If possible we can implement that interface in activity and in fragment we can initialize listener in OnAttach where we have context[ (context) Listener].
So that during configuration change (e.g. Font change) the Activity recreation listener won't go uninitialize and we can avoid a null pointer exception.
Best way to instantiate the fragment is use default Fragment.instantiate method or create factory method to instantiate the the fragment
Caution: always create one empty constructor in fragment other while restoring fragment memory will throw run-time exception.
You can use smth like this:
val fragment = supportFragmentManager.fragmentFactory.instantiate(classLoader, YourFragment::class.java.name)
because this answer now is Deprecated
Create an instance of the fragment using kotlin code.
Write in activity
val fragment = YourFragment.newInstance(str = "Hello",list = yourList)
Write in fragment
fun newInstance(str: String, list: ArrayList<String>): Fragment {
val fragment = YourFragment()
fragment.arguments = Bundle().apply {
putSerializable("KEY_STR", str)
putSerializable("KEY_LIST", list)
}
return fragment
}
Using the same fragment retrieve the data from bundle
val str = arguments?.getString("KEY_STR") as? String
val list = arguments?.getSerializable("KEY_LIST") as? ArrayList<String>
setArguments() is useless. It only brings a mess.
public class MyFragment extends Fragment {
public String mTitle;
public String mInitialTitle;
public static MyFragment newInstance(String param1) {
MyFragment f = new MyFragment();
f.mInitialTitle = param1;
f.mTitle = param1;
return f;
}
#Override
public void onSaveInstanceState(Bundle state) {
state.putString("mInitialTitle", mInitialTitle);
state.putString("mTitle", mTitle);
super.onSaveInstanceState(state);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
if (state != null) {
mInitialTitle = state.getString("mInitialTitle");
mTitle = state.getString("mTitle");
}
...
}
}
I believe I have a much simpeler solution for this.
public class MyFragment extends Fragment{
private String mTitle;
private List<MyObject> mObjects;
public static MyFragment newInstance(String title, List<MyObject> objects)
MyFragment myFrag = new MyFragment();
myFrag.mTitle = title;
myFrag.mObjects = objects;
return myFrag;
}

Categories

Resources