Hi I was looking at the following Fragments example on the android site.
http://developer.android.com/guide/components/fragments.html#Example
I would like to know why certain methods are performed.
Why for instance, in the detailsFragment is the following method performed:
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
Could you not also simply instantiate the DetailsFragment and use a setter method to set index instead. Bypassing the whole setArguments.
What's the point of using setArguments in the first place? Could you not just use setters and getters?
You can use getters and setters, but by passing in a bundle you don't need to write that code, since it's already there. Also, I believe that these arguments are automatically passed in again if the screen orientation changes, which also makes life easier.
Essentially, setArguments and getArguments is just a design pattern that Google suggests you follow:
Every fragment must have an empty constructor, so it can be
instantiated when restoring its activity's state. It is strongly
recommended that subclasses do not have other constructors with
parameters, since these constructors will not be called when the
fragment is re-instantiated; instead, arguments can be supplied by the
caller with setArguments(Bundle) and later retrieved by the Fragment
with getArguments().
http://developer.android.com/reference/android/app/Fragment.html
I take that to include setters which are needed for your Fragment to operate as well. Then again - there's nothing forcing you to do it this way, and as you know - it's not the only way things could be made to work.
Just to add to Matthew's answer: he quoted correctly that Fragments need to have an empty constructor, so that the framework can re-instantiate them when needed.
It is fine to use getters and setters, but as the framework may destroy and re-create your Fragment, you must ensure to not lose those parameters.
This must be done via Fragment.onSaveInstanceState(). The saved stated will be passed back to you as the parameter savedInstanceState in Fragment.onCreate(), Fragment.onCreateView() and several other methods.
Using Fragment.setArguments() is (in most cases, I assume) easier, at the framework will automatically preserve the arguments and thus will do most of the work for you.
Setters may be the way to go for parameters you supply to the Fragment initially and which the fragment may adjust itself over time. Dealing with the savedInstanceState alone may be easier in this case than dealing with savedInstanceState and arguments - where you have to make a decision which is the valid parameter.
public void setArguments (Bundle args)
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 (may be the text in bold was missing from official
documentation previously)
Fragments.setArguments(Bundle args)
In addition setters can be misused. If updateSomeOtherStuff() will change some view this will crash.
public class MyFragment extends Fragment {
void setData(someData){
this.someData = someData;
updateSomeOtherStuff()
}
}
If one passing in a bundle it is not possible to misuse the setter and you will always know that this will be set within the lifecycle methods.
Related
I have a fragment that is always visible. I don't understand why I should use bundles to pass data to it from activity.
Most of the questions here recommend this method of passing data:
Bundle bundle=new Bundle();
bundle.putString("name", "From Activity");
Fragmentclass fragobj=new Fragmentclass();
fragobj.setArguments(bundle);
I prefer creating Fragment object in OnCreate function of activity and then use this object to display fragment(FragmentTransaction.add). As I have refence to this fragment I can create create function showName() in it and call it from activity like that:
myFragment.showName("name");
Is there anything wrong with this approach?
The Android documentation states:
Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().
That's why it's better to use a bundle and set the parameters of the Fragment this way, it's easier for the system to restore its values when the fragment is re-instantiated.
Now, I wouldn't use myFragment.showName("name"); because you don't know if the lifecycle of the fragment has already finished (attached to the activity and inflated the views), so instead, I would call the showName("name") in the onActivityCreated or onViewCreated callbacks.
Applications should generally not implement a constructor. The first place application code can run where the fragment is ready to be used is in onAttach(Activity), the point where the fragment is actually associated with its activity. Some applications may also want to implement onInflate(Activity, AttributeSet, Bundle) to retrieve attributes from a layout resource, though should take care here because this happens for the fragment is attached to its activity.
There's nothing wrong with this approach for setting one off data values, you just need to be careful to make sure that the view that you want to set your name on actually exists at the point that the showName method is called.
Part of the reason that using Bundles to pass information is popular is that they can hold all types of data using keys and also they can easily be used to pass view states around during device rotation. Ultimately it's a matter of preference and exactly what your use case is.
When app is in background, the Fragment can recreated (eg: by change the theme (light/dark), language, ...).
So if you dont pass data use Bundle to Fragment, your Fragment will not have this data when it recreated
I know that we can pass data to Fragment using setArguments method.
But I also test that if I add a public field in the target fragment and set it from the other fragment, I can also correctly get the field value.
Then why should we use Bundle as the communication bridge?
Is there any performance issue that directly setting the fragment field?
public class SecondFragment extends Fragment {
//can we directly set this field rather than using setArguments(Bundle b)?
public List<String> strings;
}
I think one of the main reasons are that if Android recreate the fragment for various reasons outside of your code, it then calls an empty public constructor of your fragment, so that's why it's recommended to use Bundle. Or at least that's what I've been told.
I'm starting using fragments in android but I have doubts: 1.- is necessary using this part of code (newInstance, setArguments)?, if so, why?
public static class DetailsFragment extends Fragment {
/**
* Create a new instance of DetailsFragment, initialized to
* show the text at 'index'.
*/
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
http://developer.android.com/reference/android/os/Bundle.html
2.-if this code would have an OnCreate method, when will this be called?
1) Why is newInstance needed?
newInstance methods are needed because the framework encourages you to only ever have one constructor, taking no arguments. Having more than one constructor is somewhat frowned upon.
It needs this constructor to be public, so that it can recreate you Fragments when, for example, you undergo a configuration change. The same Bundle that you passed to setArguments on the original will be given to the copy too (as well as a savedInstanceState Bundle from onSaveInstanceState).
Doing this for all fragments, including ones that have no arguments leads to consistent usage of fragments and ease of maintenance. Namely, do make sure to always pass an arguments Bundle, even if it's empty, so that you don't have to check getArguments() for null.
2) onCreate in the Fragment lifecycle.
The Fragment lifecycle is officially called "nested" but when adding new fragments I like to think of it as "playing catch up". For example, if your Activity has started (has had its onStart called), your Fragment will get a rapid succession of onAttach, onCreate, onActivityCreated, onCreateView, onStart in essence catching up to the Activity's lifecycle. You can enable debugging with FragmentManager.enableDebugLogging(true); to track the lifecycle changes - look for moveto (forward direction) and movefrom (rewinding the cycle - destroying/detaching the fragment).
That said, onCreate is kinda easy to predict - since you're probably first creating the fragment after onCreate of the Activity, it would be called almost immediately when the transaction executes.
However, if you undergo a configuration change, the Fragments are destroyed with the old Activity and then created before the new Activity (it happens in that super.onCreate(savedInstanceState) call that you have in your Activity) - onCreate of all the fragments will be called before the Activity's onCreate returns. Once it returns, the Fragments' onActivityCreated will be called and from then on the fragments and activity will move in lockstep. This is all unless you have setRetainInstance(true), of course.
I hope this clears it up. I strongly suggest enabling debugging and just playing around with the fragments to get a better feeling for their lifecycles.
If you want to pass arguments to a fragment, the new instance method is necessary.
You pass arguments this way, because fragments can be in a detached state, and when they are re added may need to be reinstantiated. Android will then call the newInstance method again with the same arguments, and your fragment will work correctly.
If you passed your arguments to an onCreate method (which is called only when the fragment is first created), then when a fragment is reinstantiated, it wouldn't get those arguments.
Looking at the documentation of DialogFragment, one sees the static newInstance method to initialize a new alert dialog fragment. My question is, why not use a constructor to do so, like this:
public MyAlertDialogFragment(int title) {
Bundle args = new Bundle();
args.putInt("title", title);
setArguments(args);
}
Isn't this exactly the same or does it differ somehow? What's the best approach and why?
If you create a DialogFragment that receives objects through the constructor, you will have problems when android recreates your fragment.
This is what will happen:
your code creates the dialog calling the constructor you have created and passing some arguments as dependencies.
your dialog runs, and uses the dependencies that you passed though the constructor
the user closes the app
time passes, and android kills the fragment to free memory
the user opens the app again
android will recreate your dialog, this time using the default constructor. No arguments will be passed!
Your dialog will be in a undesired state. It may try to use instance variables that you expected to pass through the constructor, but as they are not there you'll get a null pointer exception.
To avoid this, you need not to rely on the constructor to establish the dependencies, but in in Bundles (arguments and saved instances). That may force you to implement Parcelable in some classes, which sucks.
EDIT: you can reproduce Android killing the app (step 4) by enabling the "don't maintain Activities" in the Development settings. That's the way to easily test it.
Android relies on Fragments having a public, zero-argument constructor so that it can recreate it at various times (e.g. configuration changes, restoring the app state after being previously killed by Android, etc.).
If you do not have such a constructor (e.g. the one in the question), you will see this error when it tries to instantiate one:
Fragment$InstantiationException: Unable to instantiate fragment
make sure class name exists, is public, and has an empty constructor that is public
Arguments given to it by Fragment.setArguments(Bundle) will be saved for you and given to any new instances that are (re)created. Using a static method to create the Fragment simply provides an easy way to setup the required arguments whilst maintaining a zero-argument constructor.
If you overload the constructor with MyAlertDialogFragment(int title), the Android system may still call the default MyAlertDialogFragment() constructor if the Fragment needs to be recreated and the parameter is then not passed.
Because when android is recreating a fragment, it always uses the empty constructor, and by using newInstance() you can set data that fragment uses when recreating, for example when the screen is rotated
for example:
public static FragmentExample newInstance(Parcelable uri) {
FragmentExample fragmentExample = new FragmentExample();
Bundle bundle = new Bundle();
bundle.putParcelable("Uri", uri);
fragmentExample.setArguments(bundle);
return fragmentExample;
}
I have this piece of code:
public static DateDialogFragment newInstance(Context context, DateDialogFragmentListener listener) {
DateDialogFragment dialog = new DateDialogFragment();
mContext = context;
mListener = listener;
/*I dont really see the purpose of the below*/
Bundle args = new Bundle();
args.putString("title", "Set Date");
dialog.setArguments(args);
return dialog;
}
Pretty self explanatory, but what I don't understand is what the point of giving it a Bundle. I never really make use of it I guess. The Android Doc's explanation of this (for Fragments) is here:
http://developer.android.com/reference/android/app/Fragment.html#setArguments(android.os.Bundle)
What exactly does a construction argument mean? Since its never used, I fail to see the use of it. Any explanation much appreciated. Thanks.
It is simply a generic mechanism for you to attach data values that you might want to use to configure the Fragment or otherwise read during operation, similar to how you might pass extras in a Bundle to a new Activity via it's Intent.
I do agree, however, that since a Fragment can be instantiated using its constructor, where an Activity cannot, the usefulness of the API may seem compromised because you can just as easily configure a Fragment using setter methods and member variables inside of newInstance() before returning the instance. For example, your code could implement a method on the Fragment called setTitle() that you could call, and you wouldn't need to pass that as an argument. However, arguments do provide a nice way of storing this information as key/value data if that model fits your application.
One key distinction about the arguments of a Fragment is that they are retained as part of the saved instance state. So if your UI rotates or some other change requires the Fragment to be recreated, the arguments Bundle attached will be retained and handed back to the new instance.
HTH