85

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.

Ritesh Gune
  • 16,050
  • 6
  • 41
  • 70
gghuffer
  • 1,079
  • 1
  • 9
  • 11
  • Its too late but still posting the similiar : http://stackoverflow.com/questions/21258228/how-to-add-a-fragment-to-a-layout-of-a-dialogfragment/30890121#30890121 – Raghav Sharma Dec 28 '16 at 12:43

6 Answers6

55

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.

Xavier Egea
  • 4,448
  • 3
  • 23
  • 39
  • 34
    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. – Travis Christian Apr 18 '13 at 19:37
  • 3
    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; – farid_z May 07 '13 at 16:25
38

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();
tontonGG
  • 48
  • 5
Sam
  • 84,460
  • 18
  • 172
  • 171
  • 1
    This does indeed work. Thanks! I'm still curious as to why OnCreateView crashes though. – gghuffer Nov 06 '12 at 18:56
  • 2
    @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). – Griddo Mar 18 '13 at 08:10
33

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;
    }
}
Zephyr
  • 5,408
  • 31
  • 32
17

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();


    }
I'm_With_Stupid
  • 1,002
  • 4
  • 16
  • 35
  • 1
    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! – Brandon Feb 25 '15 at 16:03
11

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;
}
Xavier Egea
  • 4,448
  • 3
  • 23
  • 39
Steve B
  • 1,146
  • 16
  • 16
  • 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 – user1530779 Jul 30 '15 at 10:55
  • @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 Oct 03 '16 at 13:07
  • 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 Oct 03 '16 at 13:28
  • 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 – Varvara Kalinina Oct 03 '16 at 15:44
7

If you want to have easy access the dialog properties, like the title and the dismiss button, but you also want to use your own layout, you can use a LayoutInflator with your Builder when you override onCreateDialog.

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = getActivity().getLayoutInflater();
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setMessage("Message!")
        .setTitle(this.dialogTitle)
        .setView(inflater.inflate(R.layout.numpad_dialog, null))
        .setPositiveButton(R.string.enter, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                // Clicked 'Okay'
            }
        })
        .setNegativeButton(R.string.dismiss, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                // Clicked 'Cancel'
            }
        });
    return builder.create();
}
Jason Hartley
  • 2,328
  • 1
  • 27
  • 39