Based on the following statement from the official documentation Fragment;
All subclasses of Fragment must include a public no-argument
constructor. The framework will often re-instantiate a fragment class
when needed, in particular during state restore, and needs to be able
to find this constructor to instantiate it. If the no-argument
constructor is not available, a runtime exception will occur in some
cases during state restore.
It seems that we have to create a public no-argument constructor for any Fragment or Dialog as framework would often re-instantiate when needed. Well, the word "re-instantiate" looks very dangerous to me. Now here is my question. I have a Dialog with default constructor as Dialog(Context). It is all nice and working. In the APK release, I am getting this error
Error: This class should provide a default constructor (a public
constructor with no arguments)
Now, I have some final variables in this Dialog which I initialize on the default constructor call Dialog(Context). But now I am required to create an empty constructor for the framework. At this point, I am failing to initialize my final variables.
Well, I can sacrifice the final keyword for my variables. But one point makes me worried. If the framework re-instantiate my dialog, that means I am getting a new instance. And what happens to my old instance? Are my old variables re-created or simply swapped with the new instance?
public static YourDialogFragment newInstance(SearchInfo searchInfo) {
YourDialogFragment fragment = new YourDialogFragment();
Bundle bundle = new Bundle();
bundle.putParcelable("key", yourdata);
fragment.setArguments(bundle);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null) {
searchInfo = bundle.getParcelable(key);
}
}
And you can call your dialog fragment
YourDialogFragmentsearchDialogFragment=YourDialogFragment.newInstance(searchInfo);
searchDialogFragment.show(activity.getSupportFragmentManager, "your tag");
Related
I am trying to use filter in Fragment and implementing the dialog fragment.
This is the class that I am using
public class HomeFragment extends Fragment implements
FilterDialogFragment.FilterListener,
PostAdapter2.OnPostSelectedListener{ detail code }
this the dialogfragment based class for spinner choosing options
public class FilterDialogFragment extends DialogFragment
this method is called upon clicking the filter button, which pops up dialog for spinner options of the filter
Declared
private FilterDialogFragment mFilterDialog;
in onCreateView
mFilterDialog = new FilterDialogFragment();
Method to call
public void onFilterClicked(){
mFilterDialog.show(getSupportFragmentManager(), FilterDialogFragment.TAG);
}
after this upon selecting the spinner option and clicking apply this method is called in which mFilterListener is null which should not be the case
public interface FilterListener {
void onFilter(Filters filters);
}
FilterListener mFilterListener;
public void onSearchClicked() {
Log.d("Message", String.valueOf(mFilterListener));
if (mFilterListener != null) {
Log.d("Message", "New 55555");
mFilterListener.onFilter(getFilters());
}
dismiss();
}
please assist me to solve this problem. if anymore details are required please let me know
attach method in FilterDialogFragement
public void onAttach(Context context) {
super.onAttach(context);
Log.d("Message", "New 6666666");
Log.d("Message", String.valueOf(mFilterListener));
if (context instanceof FilterListener) {
// Log.d("Message", String.valueOf(mFilterListener));
mFilterListener = (FilterListener) context;
}
}
You are attempting to mimic this library implementation: Friendly Eats.
However, you do not copy it wholesale, mainly in that you choose to use HomeFragment which implements FilterDialogFragment.FilterListener to launch FilterDialogFragment, rather than the library's MainActivity. This is the cause of your null pointer.
This is due to how getSupportFragmentManager() works. If you look at Android's documentation for this, you will see it says
Return the FragmentManager for interacting with fragments associated with this activity. (My Bolding)
When you call mFilterDialog.show(getSupportFragmentManager(), FilterDialogFragment.TAG); inside HomeFragment, you are actually calling whatever Activity that is the parent of HomeFragment to launch the new FilterDialogFragment. You could double check this by checking if, in onAttach(Context context) inside HomeFragment, if context instanceof HomeFragment. I do not think it will return true.
To solve this, and without changing your use of HomeFragment, you could simply pass an instance of HomeFragment itself, or a separate implementation of FilterDialogFragment.FilterListener (which I'd prefer if you do not need to use anything from HomeFragment other than the listener) to your FilterDialogFragment instance on creation or before you launch it.
For example, you could create a public setter like so:
private FilterListener mFilterListener;
public void setFilterListener(FilterListener filterListener){
mFilterListener = filterListener;
}
and then in your HomeFragment onCreateView(), you do this:
mFilterDialog = new FilterDialogFragment();
//Or preferably, an anonymous/named implementing instance of the interface only.
mFilterDialog.setFilterListener(this);
Doing so would not rely on the Android framework to provide the initialisation of your field, and does not require you to either change your Activity or HomeFragment you are currently using.
I assume, that u didnt set the listener in a mFilterDialog, so thats why its null
The full text of the error is:
C:\Users\Dov\Google Drive\AndroidStudioProjects\FlagQuiz - Copy (2)\app\src\main\java\com\dslomer64\flagquiz\QuizFragment.java
Error: Fragments should be static such that they can be re-instantiated by the system, and anonymous classes are not static [ValidFragment]
To make it worse, it doesn't tell me which line the error is in. I had assumed, since it was mentioned above, that QuizFragment is at fault, but how? So I then concluded that QuizFragment was mentioned only to indicate which class the error is in.
Also, note that no line is flagged with error as the yellow square shows.
I found the word "anonymous" in 3 places in comments in the incomplete code segment below.
DialogFragment quizResults = new DialogFragment() // anonymously **********
// extend DialogFragment class
{
#Override public Dialog onCreateDialog(Bundle bundle)
{
...
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setPositiveButton
(
R.string.reset_quiz,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
resetQuiz();
}
} // end anonymous inner class *******************
);
return builder.create(); // return the AlertDialog
} // end method onCreateDialog
}; // end DialogFragment anonymous inner class ****************
Is there something wrong (as of AS 2.3.3; nothing was wrong before upgrade) with DialogFragment quizResults = new DialogFragment() or the definition of builder, which contains an anonymous inner class?
If so, why no compilation errors? And in this case, how do I fix the problem?
(I don't want to just start hacking away at code I didn't write [I received project from an author and made many modifications] since there are at least 3 conceivable starting points and maybe none address the error(s?).
nothing was wrong before upgrade
Most likely, there was. Android Studio was not complaining about your code previously, but it may not have worked properly anyway. What changed is that now Android Studio is pointing out the problem, rather than you finding out the hard way in testing.
Is there something wrong... with DialogFragment quizResults = new DialogFragment()
Yes. It's impossible to recreate the fragment.
So, when the user rotates the screen, or changes locale, or night mode kicks in, or any number of other possible configuration changes, when Android destroys the fragment and tries to recreate it, it can't. Only the lines of code in your question can recreate the fragment, and those lines of code are yours, not the framework's, and it doesn't know about them.
It is possible that you have worked around this by blocking the ordinary destroy-and-recreate cycle for the activity, via android:configChanges. That itself is usually an anti-pattern, but if you legitimately need android:configChanges and are using it properly, you should be able to suppress this Lint error.
And in this case, how do I fix the problem?
Create a regular Java class for quizResults, extending DialogFragment and including your code. Then, use that Java class.
The part that is wrong is the following:
DialogFragment quizResults = new DialogFragment() {
#Override
public Dialog onCreateDialog(Bundle bundle) {
where you are defining an anonymous subclass of DialogFragment. This is the wrong way to use Fragments as suggested by the new lint check in Android 2.3.3.
Why? Instantiating Fragments like this will cause problems if you are using an Activity's FragmentManager.
The problematic situation is as follows: when Activity#saveInstanceState(Bundle outState) is called the FragmentManager will attempt to save the state of your Fragment. When subsequently the Activity's state is restored, the FragmentManager will attempt to recreate your Fragments (using no-args constructors) and set their states to the way they were before. This is not possible if you use anonymous subclasses of Fragment.
Henec, Fragments must have a no-args constructor and the preferred way of instantiating them is with static factory methods. Instead of anonymous subclasses, use Fragment#setArguments(Bundle bundle):
inside QuizFragment.java:
public static QuizFragment instantiate(Bundle args) {
QuizFragment frag = new QuizFragment();
frag.setArguments(args);
return frag;
}
I ran into this same problem. I converted the anonymous DialogFragment class into a regular class :
public DialogFragment instantiate(Bundle args){
DialogFragment quizResults = new DialogFragment();
quizResults.setArguments(args);
Dialog aDialog = createDialog(args);
aDialog.show();
return quizResults;
}
// create an AlertDialog and return it
public Dialog createDialog(Bundle bundle){
AlertDialog.Builder builder =
new AlertDialog.Builder(getActivity());
builder.setCancelable(false);
builder.setMessage(
getResources().getString(
R.string.results, totalGuesses, (1000 / (double) totalGuesses)));
// "Reset Quiz" Button
builder.setPositiveButton(R.string.reset_quiz,
new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog,int id)
{
resetQuiz();
}
} // end anonymous inner class
); // end call to setPositiveButton
return builder.create(); // return the AlertDialog
} // end method createDialog
The original code block under onClick(View v):
///////////////////////////////////////////////////////////////////
DialogFragment quizResults = new DialogFragment()
{
// create an AlertDialog and return it
#Override
public Dialog onCreateDialog(Bundle bundle)
{
AlertDialog.Builder builder =
new AlertDialog.Builder(getActivity());
builder.setCancelable(false);
builder.setMessage(
getResources().getString(R.string.results, totalGuesses, (1000 / (double) totalGuesses)));
// "Reset Quiz" Button
builder.setPositiveButton(R.string.reset_quiz,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
resetQuiz();
}
} // end anonymous inner class
); // end call to setPositiveButton
return builder.create(); // return the AlertDialog
} // end method onCreateDialog
Was replaced with call to instantiate the DialogFragment as below:
DialogFragment quizResults = instantiate(mSavedInstanceState);
Thanks to #Commonsware and #David Rawson, I managed to make it work with static inner class for myDialogClass by changing anything the compiler griped about to static, which included several methods as well as many (every?) variable.
This posed one problem:
public static void loadNextFlag()
{
...
// display current question number--2nd and 3rd parameters are INPUT into the xml statement
questionNumberTextView.setText
(correctAnswers + 1) + //was ,
"/" + FLAGS_IN_QUIZ);
// AssetManager assets = getActivity().getAssets();
...
} // end method loadNextFlag
The line for formatting questionNumberTextView had to be changed to
questionNumberTextView.setText(
("" + (correctAnswers + 1)
"/" + FLAGS_IN_QUIZ);
because the original
questionNumberTextView.setText(getResources().getString
(R.string.question,
(correctAnswers + 1),
FLAGS_IN_QUIZ);
gave the static vs. non-static error for getResources. I just settled for not as great a format, but suitable.
I also made assets a global static variable to be assigned only once, in onCreateView.
So textbooks don't always do it right, since to do so would raise the level of the text too far above the intended audience.
I have a class WeatherFragment that extends Fragment class. I created an instance of it in the launcher activity and inflated it in a layout. Is it possible for me to to send the fragment object as an intent extra to some other activity in my project instead of creating a new instance of WeatherFragment?
Don't have a code for this. Its just an interview question.
I think you can, but it will not be good. A quick search brought me to this question with an answer that said:
You wouldn't. At most, you would follow #Ribose's answer -- pass a flag into the activity via an extra to indicate what set of fragments to create.
Your question is not so specific. This question is specific to what the OP wants, but maybe one of the answers could help you.
P.S. If you would like to experiment though, you can have your WeatherFragment implement Parcelable. Then pass it from one activity to another activity through intent. This answer will tell you how and you could do it like so (modified to extend Fragment class)
public class WeatherFragment extends implements Parcelable {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
/* everything below here is for implementing Parcelable */
// 99.9% of the time you can just ignore this
public int describeContents() {
return 0;
}
// write your object's data to the passed-in Parcel
public void writeToParcel(Parcel out, int flags) {
//code
}
// this is used to regenerate your object. All Parcelables must have a CREATOR that implements these two methods
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
// example constructor that takes a Parcel and gives you an object populated with it's values
private MyParcelable(Parcel in) {
//code
}
//other methods
}
Then, from the answer again, you can use it like so:
Intent intent = new Intent();
intent.putExtra(KEY_EXTRA, weatherFragment);
From the answer again (You really should read this answer), you get it like so:
Intent intent = getIntent();
WeatherFragment weatherFragment = (WeatherFragment) intent.getParcelableExtra(MainActivity.KEY_EXTRA);
I have not tested this so I'm not sure if it would work.
Between different Acitvities you can not since Fragment does not implement Serializable or Parcelable.
Sure you can make your Fragment implement those interfaces but this way you won't actually be passing Fragment, just some state of that Fragment which you then serialize yourself.
Within the same Activity you can have your fragment back when the Activity gets recreated if you use FragmentManager.putFragment() in onSaveState() and getFragment() in onCreate(). This is not needed usually.
Possible but I won't recommend it. But you can get the fragment object by using findFragmentById or findFragmentByTag to get the object.
Because setFragment of Facebook is working only with support.fragment, I did the following in order to overcome this when working with regular fragment:
public class NativeFragmentWrapper extends android.support.v4.app.Fragment {
private final Fragment nativeFragment;
public NativeFragmentWrapper(Fragment nativeFragment) {
this.nativeFragment = nativeFragment;
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
nativeFragment.startActivityForResult(intent, requestCode);
}
#Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
nativeFragment.onActivityResult(requestCode, resultCode, data);
}
}
and in the login page where facebook button resides, I did the following:
authButton.setFragment(new new NativeFragmentWrapper(this));
in Eclipse it worked great but Android Studio 1.0.2 is complaining:
This fragment should provide a default constructor (a public constructor with no arguments). From the Fragment documentation: 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().
So I tried this:
NativeFragmentWrapper f = new NativeFragmentWrapper();
Bundle bundle = new Bundle();
f.setArguments(bundle);
authButton.setFragment(f);
but I didn't find a way to put "this" (which is a Fragment) inside the bundle in order to retrieve it in the wrapper constructor.
I can #SuppressLint("ValidFragment") but I'm sure there is a cleaner way to do it.
The best way is to just use the Fragment class from the v4 support libraries in your app, the signatures are the same, and you just need to change the import statements.
Failing that, the solution in the other post can potentially work (haven't tried it myself), and to solve your problem, just have 2 constructors (one the default, and another that takes a Fragment parameter), and just use the right constructor (i.e. not the default) when you create the wrapper object.
When considering the case with android activity, the first method to work is its onCreate method..right?
Suppose i want to pass 2 parameters to android activity class say UserHome . For that am creating the constructor of activity class UserHome and accepting the params.
But when we are calling an activity we are not initializing the Activity class, we are just creating an intent of UserHome class.
Then how can we pass params to that activity from another activity without using intent.putExtra("keyName", "somevalue"); usage.
Experts please clarify how we can cover a situation like this.?
Not sure why you would not want to use the intent params. That is what they are there for. If you need to pass the same parameters from different places in your application, you could consider using a static constructor that builds your intent request for you.
For example:
/**
* Sample activity for passing parameters through a static constructor
* #author Chase Colburn
*/
public class ParameterizedActivity extends Activity {
private static final String INTENT_KEY_PARAM_A = "ParamA";
private static final String INTENT_KEY_PARAM_B = "ParamB";
/**
* Static constructor for starting an activity with supplied parameters
* #param context
* #param paramA
* #param paramB
*/
public static void startActivity(Context context, String paramA, String paramB) {
// Build extras with passed in parameters
Bundle extras = new Bundle();
extras.putString(INTENT_KEY_PARAM_A, paramA);
extras.putString(INTENT_KEY_PARAM_B, paramB);
// Create and start intent for this activity
Intent intent = new Intent(context, ParameterizedActivity.class);
intent.putExtras(extras);
context.startActivity(intent);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Extract parameters
Bundle extras = getIntent().getExtras();
String paramA = extras.getString(INTENT_KEY_PARAM_A);
String paramB = extras.getString(INTENT_KEY_PARAM_B);
// Proceed as normal...
}
}
You can then launch your activity by calling:
ParameterizedActivity.startActivity(this, "First Parameter", "Second Parameter");
I can see one situation where you'd be unable to use the standard method of passing the parameters via the Intent: When you're creating an activity that will be launched by another app (say, the edit activity of a Tasker plugin) and, therefore, do not have control over the Intent that will launch your activity.
It's possible to create an Activity that accepts parameters in its constructor. The trick to using it, though, is not to use it directly, but to use a derived class with a default constructor that calls super() with the appropriate arguments, as such:
class BaseActivity extends Activity
{
public BaseActivity(String param1, int param2)
{
// Do something with param1 and param2.
}
// Many more lines of awesomeness.
}
class DerivedActivity extends BaseActivity
{
public DerivedActivity()
{
super("param1", 42);
}
}
Naturally, if you need to generate the parameters to pass to BaseActivity(), you can simply replace the hard-coded values with function calls.
We can pass the value from parent activity to child activity using the bundled collection and shared preference.
1. Shared Preference
2. Bundle Collection
Passing data or parameter to another Activity Android
But you also can create very well a constructor of UserHome.
public class MainActivity extends Activity {
UserHome userHome = new UserHome(param1,param2);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
userHome.yourMethod();
}}
Why do you think that is not possible to initialize a contructor?..MainActivity is a class like any other, just that extends Activity, but also keeps the properties of a class, so that can have, constructors, methods, members.