Reputation: 1427
The answer / an example can be found at the bottom of this post. The way this answer has been reached, can be found in this answer and its comments.
I've got a dialogFragment
, which has two editText
input fields. On a specific buttonClick, I want this dialogFragment
to be created, and the two editText
fields to be filled with text. For this purpose, I created a simple method inside the dialogFragment
's class.
public void presetFields(String nameField, String tagField) {
nameInputField.setText(nameField);
tagInputField.setText(tagField);
}
Problem is, that both nameInputField
and tagInputField
get initialised inside the onCreateDialog
method.
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = getActivity().getLayoutInflater();
final View v_iew = inflater.inflate(R.layout.fragment_inputdialog, null);
nameInputField = (EditText) v_iew.findViewById(R.id.inputdialogname);
tagInputField = (EditText) v_iew.findViewById(R.id.inputdialogtag);
}
I thought that would be no problem at all, since I have a similar construction running for a fragment. Only difference is that the editText
's get initialised in an onCreateView
instead of an onCreateDialog
.
This is the onClick
code that shows the dialogFragment
and calls the method to set the nameInputField
and tagInputField
fields.
editButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stuffManagerInputDialogFragment.show(getFragmentManager(), "TEST");
stuffManagerInputDialogFragment.presetFields(nameTextViewContent, tagTextViewContent);
}
});
When I click the editButton
, this is the log I get with the NPE
03-02 16:04:25.120 509-509/com.example.tim.timapp I/art: Not late-enabling -Xcheck:jni (already on)
03-02 16:04:25.190 509-509/com.example.tim.timapp W/System: ClassLoader referenced unknown path: /data/app/com.example.tim.timapp-2/lib/x86_64
03-02 16:04:25.400 509-524/com.example.tim.timapp D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
03-02 16:04:25.520 509-524/com.example.tim.timapp I/OpenGLRenderer: Initialized EGL, version 1.4
03-02 16:04:25.570 509-524/com.example.tim.timapp W/EGL_emulation: eglSurfaceAttrib not implemented
03-02 16:04:25.570 509-524/com.example.tim.timapp W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0x7fceb5b27dc0, error=EGL_SUCCESS
03-02 16:04:28.660 509-509/com.example.tim.timapp D/TEST: 2131493026
03-02 16:04:30.310 509-509/com.example.tim.timapp D/AndroidRuntime: Shutting down VM
03-02 16:04:30.310 509-509/com.example.tim.timapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.tim.timapp, PID: 509
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.EditText.setText(java.lang.CharSequence)' on a null object reference
at com.example.fragments.MainFragments.DialogFragments.StuffManagerInputDialogFragment.presetFields(StuffManagerInputDialogFragment.java:149)
at com.example.fragments.MainFragments.VariableFragments.StuffManagerVariableFragment$2.onClick(StuffManagerVariableFragment.java:87)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
03-02 16:04:33.140 509-509/com.example.tim.timapp I/Process: Sending signal. PID: 509 SIG: 9
I'm not an expert by far, but what I make of this error, is that nameTextView
and tagTextView
are null
at the moment that I'm trying to set text to them, which makes me think the method gets called before the fragment is created.
How can I make sure that both editText
's are properly initialised before I try to call something on it?
I have removed some bits of code for easy reading. If something seems to be missing, it probably is. Please let me know, so I can add it.
If you require more code, please also let me know.
All credits go to George Mulligan for this one.
So some stuff has changed from the original approach. First of all, the presetFields
method has been removed. The text that should populate the editText
fields is now passed to the fragment as an argument.
The onClick
method now looks like this:
editButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
nameTextViewContent = nameTextView.getText().toString();
tagTextViewContent = tagTextView.getText().toString();
Bundle args = new Bundle();
args.putString("name", nameTextViewContent);
args.putString("tag", tagTextViewContent);
stuffManagerInputDialogFragment.setArguments(args);
stuffManagerInputDialogFragment.show(getFragmentManager(), "TEST");
}
});
So without the presetFields
method, we have no way to set the editText
fields to the text we want. This has been added to the onCreateDialog
method, as can be seen below.
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = getActivity().getLayoutInflater();
final View v_iew = inflater.inflate(R.layout.fragment_inputdialog, null);
nameInputField = (EditText) v_iew.findViewById(R.id.inputdialogname);
tagInputField = (EditText) v_iew.findViewById(R.id.inputdialogtag);
if (getArguments() != null) {
nameInputField.setText(getArguments().getString("name"));
tagInputField.setText(getArguments().getString("tag"));
}
}
The null-check for getArguments()
has been added because I sometimes need to call this dialogFragment
without filling the editText
fields. When this happens, it means there are no arguments set to the fragment, which would result in a NPE when calling getArguments().getString("String");
That should be it. I do however have a reputation for being stupid, so it might very well be possible I overlooked something, that should be included in this explanation. If so, please let me know, so I can add it.
Upvotes: 0
Views: 156
Reputation: 11903
What I make of this error, is that nameTextView and tagTextView are null at the moment that I'm trying to set text to them, which makes me think the method gets called before the fragment is created.
That is almost correct. The Fragment
is created but onCreateDialog()
has not been called yet because showing the dialog happens asynchronously after the call to show(...)
.
You can fix this by adding the two preset Strings as arguments to the DialogFragment
.
You can then assign the EditText
fields the correct value directly in the onCreateDialog
method by using getArguments()
on the DialogFragment
.
Here is what this looks like:
public MyDialogFragment extends DialogFragment {
private static final String ARG_NAME = "name";
private static final String ARG_TAG = "tag";
public MyDialogFragment newInstance(String name, String tag) {
Bundle args = new Bundle();
args.putString(ARG_NAME, name);
args.putString(ARG_TAG, tag);
MyDialogFragment frag = new MyDialogFragment();
frag.setArguments(args);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = getActivity().getLayoutInflater();
final View v_iew = inflater.inflate(R.layout.fragment_inputdialog, null);
nameInputField = (EditText) v_iew.findViewById(R.id.inputdialogname);
tagInputField = (EditText) v_iew.findViewById(R.id.inputdialogtag);
nameInputField.setText(getArguments().getString(ARG_NAME, ""));
tagInputField.setText(getArguments().getString(ARG_TAG, ""));
}
}
Then the new click event handler.
editButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stuffManagerInputDialogFragment = MyDialogFragment
.newInstance(nameTextViewContent, tagTextViewContent);
stuffManagerInputDialogFragment.show(getFragmentManager(), "TEST");
}
});
Upvotes: 1
Reputation: 35661
Don't try to manipulate your dialog immediately after calling show.
If you want to set default values for fields within your dialog, pass them to the dialog as arguments and initialise them during the dialog creation.
e.g
public class MyFragment extends Fragment {
/**
* Static factory method that takes an int parameter,
* initializes the fragment's arguments, and returns the
* new fragment to the client.
*/
public static MyFragment newInstance(int index) {
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
}
from: http://www.androiddesignpatterns.com/2012/05/using-newinstance-to-instantiate.html
Upvotes: 0