DialogFragment OnCreateView 与 OnCreateDialog 的自定义布局

Posted

技术标签:

【中文标题】DialogFragment OnCreateView 与 OnCreateDialog 的自定义布局【英文标题】:Custom Layout for DialogFragment OnCreateView vs. OnCreateDialog 【发布时间】:2012-10-26 18:15:02 【问题描述】:

我正在尝试使用我自己的布局创建一个 DialogFragment。

我见过几种不同的方法。有时布局是在 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

我被难住了。

【问题讨论】:

为时已晚,但仍在发布类似内容:***.com/questions/21258228/… 【参考方案1】:

下面的代码我也有同样的异常:

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;    
    

您必须在 DialogFragment 中选择仅覆盖一个 onCreateView 或 onCreateDialog。覆盖两者将导致异常:“在添加内容之前必须调用requestFeature()”。

重要

如需完整答案,请查看@TravisChristian 评论。正如他所说,您确实可以覆盖两者,但是当您在创建对话框视图后尝试膨胀视图时,问题就来了。

【讨论】:

这并不完全正确。您可以覆盖两者(实际上 DialogFragment 是这样说的),当您在创建对话框视图后尝试膨胀视图时会出现问题。您仍然可以在 onCreateView 中执行其他操作,例如使用 savedInstanceState,而不会导致异常。 这里也一样。两者都需要支持。这就是使用片段内联或作为对话框的想法。对我来说似乎是一个错误。我能做的最好的事情是设置对话框的标题,但没有运气通过调用 super 并为返回的对话框对象设置标题来在 onCreateDialog 中添加取消按钮: final Dialog dialog = super.onCreateDialog(savedInstanceState); dialog.setTitle(m_callback.getTitle()); // 没有运气添加取消按钮返回对话框;【参考方案2】:

第一种方法适用于我......直到我想使用 FindViewByID。

我猜你没有将findViewById() 限定为inflate() 返回的视图,试试这个:

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

return b.create();

【讨论】:

这确实有效。谢谢!我仍然很好奇 OnCreateView 为什么会崩溃。 @gghuffer 虽然这晚了4个月,但我不认为,这个异常是由上面的代码直接引起的。更常见的是,人们在添加内容(如 Exception 消息已声明的内容)后调用 requestFeature(...)(或类似 requestWindowFeature(Window.FEATURE_NO_TITLE);)。【参考方案3】:

以下代码来自google guide,所以答案是你不能像你在onCreateDialog()中那样做,你必须使用super.onCreateDialog()来获得一个对话框。

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;
    

【讨论】:

你有链接吗? @Zebphyr 如果我想将此 CustomDialogFragment 用作我的活动的对话框,但在上面的代码中,onCreateDialog() 方法中没有提到 R.layout.my_layout 怎么办。 onCreateView() 会在这种情况下提供帮助吗? @Jack Jan,是的,您可以在 onCreateView() 调用中指定布局文件。【参考方案4】:

这是在对话框片段中使用 findViewById 的示例

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


    

【讨论】:

这个例子非常适合我。您必须将所有代码放入 onCreateDialog 而不是来自 onCreateView。此代码允许用户执行此操作以及获取按钮。完美!【参考方案5】:

正如@Xavier Egea 所说,如果您同时实现了 onCreateView() 和 onCreateDialog(),您将面临“必须在添加内容之前调用 requestFeature()”崩溃的风险。这是因为当您将该片段显示为对话框时,会同时调用 onCreateDialog() 和 onCreateView()(为什么,我不知道)。正如 Travis Christian 所提到的,在 onCreateDialog() 中创建对话框后 onCreateView() 中的 inflate() 是导致崩溃的原因。

实现这两个功能的一种方法,但要避免这种崩溃:使用 getShowsDialog() 来限制 onCreateView() 的执行(因此不会调用您的 inflate())。这样,当您将 DialogFragment 显示为对话框时,只会执行您的 onCreateDialog() 代码,但是当您的 DialogFragment 用作布局中的片段时,可以调用您的 onCreateView() 代码。

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

【讨论】:

我看不出这样做的意义,因为 onCreateView 无论如何都在配置视图,为什么你想在两个地方都配置视图,你可以将 onCreateView 保留为常见的膨胀代码,并且无论如何都会在对话框中膨胀 @user1530779 按钮呢?在 OnCreateDialog 中,我可以使用构建器设置按钮,当视图在对话框中膨胀时,我应该在 OnCreateView 中做什么来获取按钮? 嗯,似乎使用 Builder 给了我例外。那么如果视图在对话框中膨胀并且当它只是一个片段时没有设置任何按钮,那么设置按钮的方法是什么? 好吧,事实证明,如果您调用 dialog.setView 而不是 dialog.setContentView,即使您使用 Builder 创建对话框并设置按钮,这种方法也可以正常工作【参考方案6】:

如果您想轻松访问对话框属性,例如标题和关闭按钮,但又想使用自己的布局,则可以在覆盖 onCreateDialog 时将 LayoutInflator 与您的 Builder 一起使用。

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

【讨论】:

以上是关于DialogFragment OnCreateView 与 OnCreateDialog 的自定义布局的主要内容,如果未能解决你的问题,请参考以下文章

Dialogfragment 未清除

Android:DialogFragment.dismissInternal 处 DialogFragment.dismissAllow 处的 NullPointerException

DialogFragment.getDialog 返回 null

为啥要使用 DialogFragment?

从 DialogFragment 回调片段

允许 DialogFragment 的外部触摸