Custom Layout for DialogFragment OnCreateView vs. OnCreateDialog

76,091

Solution 1

This first approach works for me... until I want to use FindViewByID.

I would guess that you are not scoping findViewById() to the View returned by inflate(), try this:

View view = i.inflate(Resource.Layout.frag_SelectCase, null);
// Now use view.findViewById() to do what you want
b.setView(view);

return b.create();

Solution 2

I had the same exception with the following code:

public class SelectWeekDayFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
        .setMessage("Are you sure?").setPositiveButton("Ok", null)
        .setNegativeButton("No way", null).create();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.week_day_dialog, container, false);

        return view;    
    }
}

You must choose to override only one of onCreateView or onCreateDialog in a DialogFragment. Overriding both will result in the exception: "requestFeature() must be called before adding content".

Important

For complete answer check the @TravisChristian comment. As he said, you can override both indeed, but the problem comes when you try to inflate the view after having already creating the dialog view.

Solution 3

Below code comes from google guide, so the answer is that you could not do like yours in onCreateDialog(), you must use super.onCreateDialog() to get a dialog.

public class CustomDialogFragment extends DialogFragment {
    /** The system calls this to get the DialogFragment's layout, regardless
        of whether it's being displayed as a dialog or an embedded fragment. */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the layout to use as dialog or embedded fragment
        return inflater.inflate(R.layout.purchase_items, container, false);
    }

    /** The system calls this only when creating the layout in a dialog. */
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // The only reason you might override this method when using onCreateView() is
        // to modify any dialog characteristics. For example, the dialog includes a
        // title by default, but your custom layout might not need it. So here you can
        // remove the dialog title, but you must call the superclass to get the Dialog.
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        return dialog;
    }
}

Solution 4

Here's an example of using findViewById in a Dialog Fragment

public class NotesDialog extends DialogFragment {

        private ListView mNotes;
       private RelativeLayout addNote;

        public NotesDialog() {
            // Empty constructor required for DialogFragment
        }



        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {

            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

            View view = getActivity().getLayoutInflater().inflate(R.layout.note_dialog, null);
            mNotes = (ListView) view.findViewById(R.id.listViewNotes);
            addNote = (RelativeLayout) view.findViewById(R.id.notesAdd);

            addNote.setOnClickListener(new View.OnClickListener(){
                 @Override
                 public void onClick(View v){


                     getDialog().dismiss();

                     showNoteDialog();
                 }
             });

            builder.setView(view);

            builder.setTitle(bandString);


            builder.setNegativeButton("Cancel",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                          getDialog().dismiss();
                        }
                    }
                );


           return  builder.create();


    }

Solution 5

As @Xavier Egea says, if you have both onCreateView() and onCreateDialog() implemented, you run the risk of getting the "requestFeature() must be called before adding content" crash. This is because BOTH onCreateDialog() then onCreateView() are called when you show() that fragment as a dialog (why, I don't know). As Travis Christian mentioned, the inflate() in onCreateView() after a dialog was created in onCreateDialog() is what causes the crash.

One way to implement both these functions, but avoid this crash: use getShowsDialog() to limit execution of your onCreateView() (so your inflate() is not called). This way only your onCreateDialog() code is executed when you are displaying your DialogFragment as a dialog, but your onCreateView() code can be called when your DialogFragment is being used as a fragment in a layout.

// Note: if already have onCreateDialog() and you only ever use this fragment as a 
// dialog, onCreateView() isn't necessary
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (getShowsDialog() == true) {  // **The key check**
        return super.onCreateView(inflater, container, savedInstanceState);
    } else {
        View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_alarm_dialog, null);    
        return configureDialogView(view);
    }
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{ 
    // Return custom dialog...
    Dialog dialog = super.onCreateDialog(savedInstanceState); // "new Dialog()" will cause crash

    View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_alarm_dialog, null);    
    configureDialogView(view);
    dialog.setContentView(view);

    return dialog;
}

// Code that can be reused in both onCreateDialog() and onCreateView()
private View configureDialogView(View v) {      
    TextView myText = (TextView)v.findViewById(R.id.myTextView);
    myText.setText("Some Text");

    // etc....

    return v;
}
Share:
76,091
gghuffer
Author by

gghuffer

I'm actually a chemist...

Updated on July 12, 2022

Comments

  • gghuffer
    gghuffer almost 2 years

    I'm trying to create a DialogFragment using my own Layout.

    I've seen a couple different approaches. Sometimes the layout is set in OnCreateDialog like this: (I'm using Mono but I've gotten somewhat used to Java)

    public override Android.App.Dialog OnCreateDialog (Bundle savedInstanceState)
    {
        base.OnCreateDialog(savedInstanceState);
        AlertDialog.Builder b = new AlertDialog.Builder(Activity);
            //blah blah blah
        LayoutInflater i = Activity.LayoutInflater;
        b.SetView(i.Inflate(Resource.Layout.frag_SelectCase, null));
        return b.Create();
    }
    

    This first approach works for me... until I want to use findViewByID. so after a bit of googling I tried the second approach which involves overriding OnCreateView

    So I commented out two lines of OnCreateDialog that set the Layout and then added this:

    public override Android.Views.View OnCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View v = inflater.Inflate(Resource.Layout.frag_SelectCase, container, false);
            //should be able to use FindViewByID here...
        return v;
    }
    

    which gives me a lovely error:

    11-05 22:00:05.381: E/AndroidRuntime(342): FATAL EXCEPTION: main
    11-05 22:00:05.381: E/AndroidRuntime(342): android.util.AndroidRuntimeException: requestFeature() must be called before adding content
    

    I'm stumped.

  • gghuffer
    gghuffer over 11 years
    This does indeed work. Thanks! I'm still curious as to why OnCreateView crashes though.
  • Gerrit-K
    Gerrit-K about 11 years
    @gghuffer Although this is 4 months late, I don't think, that this Exception is directly caused by the code above. It's more common that people call requestFeature(...) (or something like requestWindowFeature(Window.FEATURE_NO_TITLE);) after adding content (like the Exception message already states).
  • Travis Christian
    Travis Christian about 11 years
    That's not entirely true. You can override both (in fact the DialogFragment says so), the problem comes when you try to inflate the view after having already creating the dialog view. You can still do other things in onCreateView, like use the savedInstanceState, without causing the exception.
  • farid_z
    farid_z almost 11 years
    Same here. Needs to support both. That's the idea of either using the fragment inline or as a dialog. Seems like a bug to me. The best I could do, is set the title for the dialog but no luck adding a cancel button in onCreateDialog by calling super and setting title to the returned dialog object: final Dialog dialog = super.onCreateDialog(savedInstanceState); dialog.setTitle(m_callback.getTitle()); // no luck adding cancel button return dialog;
  • Brandon
    Brandon about 9 years
    This example works for me perfectly. You must put all your code into the onCreateDialog rather than from the onCreateView. This code allows the user to do just that as well as get the buttons. Perfect!
  • user1530779
    user1530779 over 8 years
    I don't see the point in doing this since onCreateView is configuring views anyways why do you wanna configure views at both places you can keep onCreateView as common inflating code and that will anyways be inflated in dialog
  • Varvara Kalinina
    Varvara Kalinina over 7 years
    @user1530779 What about buttons? in OnCreateDialog i can use the builder to set the buttons and what am I supposed to do in OnCreateView to get the buttons when the view is inflated in dialog?
  • Varvara Kalinina
    Varvara Kalinina over 7 years
    Hmm, seems like using the Builder gives me exception. So what would be the way to setup buttons if the view is inflated in dialog and set no buttons when it's just a fragment?
  • Varvara Kalinina
    Varvara Kalinina over 7 years
    Well, it turns out that if you call dialog.setView instead of dialog.setContentView this approach works fine even if you create your dialog with a Builder and set buttons
  • Daniel Gomez Rico
    Daniel Gomez Rico over 6 years
    do you have a link?
  • CopsOnRoad
    CopsOnRoad over 6 years
    @Zebphyr What if I want to use this CustomDialogFragment as a Dialog for my activity, but in the code above there is no mention of R.layout.my_layout in onCreateDialog() method. Will onCreateView() will help in this case?
  • Zephyr
    Zephyr over 6 years
    @Jack Jan, Yes, you can specify the layout file in the onCreateView() call.