Custom Layout for DialogFragment OnCreateView vs. OnCreateDialog

JavaAndroidMonoxamarin.androidAndroid Inflate

Java Problem Overview


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.

Java Solutions


Solution 1 - Java

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 2 - Java

>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 3 - Java

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 - Java

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 - Java

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;
}

Solution 6 - Java

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongghufferView Question on Stackoverflow
Solution 1 - JavaXavier EgeaView Answer on Stackoverflow
Solution 2 - JavaSamView Answer on Stackoverflow
Solution 3 - JavaZephyrView Answer on Stackoverflow
Solution 4 - JavaI'm_With_StupidView Answer on Stackoverflow
Solution 5 - JavaSteve BView Answer on Stackoverflow
Solution 6 - JavaJason HartleyView Answer on Stackoverflow