对话框片段 OnCreateView 和 OnCreateDialog 的自定义布局

我正在尝试使用我自己的布局创建一个对话框片段。

我见过几种不同的方法,有时候在 OnCreateDialog 中设置布局如下: (我正在使用 Mono,但是我已经有点习惯 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();
}

第一种方法对我有效... ... 直到我想使用 findViewByID. 因此,在谷歌了一下之后,我尝试了第二种方法,它涉及到覆盖 OnCreateView

因此,我注释掉了设置布局的 OnCreateDialog的两行,然后添加了以下内容:

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

这给了我一个可爱的错误:

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

我被难住了。

78755 次浏览

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

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.

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

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

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




}

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