Timmiej93
Timmiej93

Reputation: 1427

How should I make sure that the onCreate(View/Dialog) has finished running, so everything is properly initialised?

Answer

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.


Original question

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.


Complete answer

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

Answers (2)

George Mulligan
George Mulligan

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

Kuffs
Kuffs

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

Related Questions