I have seen different ways to create a Fragment. Can somebody clear me up whats the difference between the methods and what is the best way and when to use which.
new MyFragment() I myself use this, because it seemed natural to me.
MyFragment.instantiate(Context context, String fname, Bundle args) This looks like a custom static method to create a Fragment but i've never seen it used.
My.Fragment.newInstance() This one is in an Android Developer Example.
What's each options purpose?
The most difference is when to use each sample:
1- Creates a new fragment object each time called.
2- Same as calling the empty constructor, but, if you set the fragment to setRetainInstance(true), this will not work if you use the empty constructor.
3- My.Fragment.newInstance(), method to get the single instance from a static fragment, If you have a static class extending fragment, you can create a Method to return a new fragment or the current fragment, its the singleton pattern.
4- Inflate from xml, same as calling the empty contructor, the Android FrameWork take care of attachement to the view, but the instance will be kept with the hole activity lifecycle, needs more memory and cannot be reused multiple time.
Related
While I was coding, I wanted to use findViewById method to find a view that cant access in the current view but can be accessed via the MainActivity. So two options came to my mind. One is creating a static method from that object in the MainActivity class and access the static object. The second method is to create a static object form MainActivity class itself(this) and access the findViewById method by calling the static object. Please answer the method I should use.
And apart from that, it got me thinking that whether an Android developer should come across this type of scenario or whether I have done some improper coding to access findViewById method in MainActivity while I was in a different view.
You can take a look at the code in the below repo.
https://github.com/chrish2015/ExpenseTrackerLatest
Thanks
If you are inside a class that is neither a Context nor an Activity and you need to use a method which exists inside the activity or context, then simply pass the activity as a parameter to that class and take an instance to that activity inside your class.
public class MyAdapter extends ArrayAdapter { // this is not activity
private Activity mActivity; // activity is a member of this class.
public MyAdapter(Activity activity, List<String> data) {
mActivity = activity;
}
public View getView(...) {
// if you need to use findViewById:
View view = mActivity.findViewById(R.id.some_id);
}
}
Don't use any of your two methods.
I might be misunderstanding your first sentence, but just to be sure, are you asking for a way to access a View that exists in the MainActivity, while you're inside of a Fragment?
If that's what you're asking, then yes, as an Android Developer, there will definitely be moments where we come across this scenario. However, the solution is definitely NOT by making your Views or Context static.
This is one of the easiest ways to cause bugs to appear throughout your app, with a very high chance to cause memory leaks too. Here's an Article from Google talking about memory leaks related to keeping a reference to a Context: https://android-developers.googleblog.com/2009/01/avoiding-memory-leaks.html
Rather than your two options, there are better solutions that developers typically use.
First of all, keep in mind that you should NOT be directly accessing any Views from outside of your current layout... meaning, that if you're in a second Activity, you don't directly access Views from the first Activity, or if you're in a Fragment, you don't directly access Views that belong to it's FragmentActivity.
Instead, you let the Activity or Fragment handle it's own Views.
So for example, if you're in another Activity and you want to update some data in the previous Activity, you can take advantage of an Activity's startActivityForResult() and onActivityResult() to obtain the data necessary to update the Activity immediately upon returning to the app.
For Fragments, there's actually a tutorial from the Android Documentation that describes a very good way to communicate between other Fragments: https://developer.android.com/training/basics/fragments/communicating
This method is to use interfaces as a callbacks, so another Fragment or the Activity will be able to receive data and update it's Views within it's own layout.
So for your case, if you're using Fragments and an Activity, you can easily have your fragments and activities communicate to each other in a safer and more reliable way.
Also, make sure you read up more on static and it's effects on your code, especially the side effects on Android components. Do not carelessly use static without considering some of the effects it might cause, because that would cause an endless amount of trouble to your code.
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 am building a tab interface using Action bar and fragment. I would need assistance in sending data from container activity to the fragment.
To elaborate, I have job object in container activty. And I have created few tabs based on the information in job object (like company details, experience details etc). I need to pass the job object to these fragments so that it can display respective information.
I have created container activity and tab fragments. I would need an example on how to pass the object across them. I cannot use intent.putExtra. Can I access parent container's object from fragment?
Any help shall be appreciated.
Thanks.
Make the method in your activity, e.g getJob that will return the Job object and its information
MyActivity extends Activity{
Job mJob;
public Job getJob(){
return this.mJob;
}
}
then in your Fragment you do this:
MyFragment extends Fragment{
#Override
public void onActivityCreated(){
super.onActivityCreated();
((MyActivity)this.getActivity()).getJob();
}
}
use getActivity and the method getJob(); to get the object
There are multiple ways of achieving this.
Make a static variable to hold your data and access that data from inside the fragments - this is the most fast but it creates bad design patterns if used improperly.
A way of Fragment-to-Fragment communication possible through the parent Activity is posted here: http://developer.android.com/training/basics/fragments/communicating.html You can use the sample code to just do a Activity - Fragment data send.
The top voted answer here: Accessing instance of the parent activity? mentions a way to avoid using static data (1.) and contains source code examples using ActivityGroup
"If you need access to some values in your First activity without
making a static reference to it, you could consider putting your
activities in an ActivityGroup."
What you choose is your preference, these are just a few options!
Edit: I'm not sure if number 3 will work with fragments since I haven't tested a method similar to it, the example is Activity - Activity communication.
I have implemented a DialogFragment class which shows a dialog and call a custom listener when its button is pressed.
Since I want to reuse this class in many projects I need to pass the listener when I create the fragment. However I don't know how I could do it.
I know I should not pass the listener in the constructor because Android could call the default constructor if it has to recreate the fragment and I cannot pass the listener via Bundle arguments because Bundle does not support it.
The unique solution I found is make the activity implement the custom listener and check it in onAttach method. The problem is that this way would limit the implementing classes to the owner activity and maybe it does not fit to the application.
Does you find a better solution?
Thanks!
You normally have a static newInstance() method to create your fragment and pass in any values your fragment might need. That's the method to pass your listener to.
Have a look here where a newInstance() method is also used as an example:
http://developer.android.com/reference/android/app/Fragment.html
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.