Спеціальний макет для DialogFragment OnCreateView проти OnCreateDialog


81

Я намагаюся створити 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

Я в тупі.


Занадто пізно, але все одно розміщує подібне: stackoverflow.com/questions/21258228/…
Рагхав Шарма

Відповіді:


37

Цей перший підхід працює для мене ... поки я не хочу використовувати 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();

1
Це справді працює. Дякую! Мені все ще цікаво, чому OnCreateView падає.
gghuffer

2
@gghuffer Хоча це запізнилося на 4 місяці, я не думаю, що цей виняток безпосередньо спричинений наведеним вище кодом. Частіше люди телефонують requestFeature(...)(або щось подібне requestWindowFeature(Window.FEATURE_NO_TITLE);) після додавання вмісту (наприклад, у повідомленні про виняток уже зазначено).
Griddo

54

У мене був такий же виняток із наступним кодом:

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

Ви повинні замінити лише один з onCreateView або onCreateDialog у DialogFragment. Заміна обох призведе до винятку: "requestFeature () потрібно викликати перед додаванням вмісту".

Важливо

Повну відповідь див. У коментарі @TravisChristian. Як він сказав, ви можете замінити і те, і інше, але проблема виникає, коли ви намагаєтеся надути подання після того, як уже створили діалогове вікно.


32
Це не зовсім так. Ви можете замінити обидва (насправді так говорить DialogFragment), проблема виникає при спробі роздути подання після того, як ви вже створили діалогове вікно. Ви все ще можете робити інші речі в onCreateView, наприклад, використовувати saveInstanceState, не викликаючи винятку.
Тревіс Крістіан,

3
Те ж саме. Потрібно підтримувати обох. Це ідея або використовувати фрагмент вбудовано, або як діалогове вікно. Мені здається помилкою. Найкраще, що я міг зробити, - це встановити заголовок для діалогового вікна, але не пощастило додати кнопку скасування в onCreateDialog, викликавши super та встановивши заголовок для поверненого діалогового об'єкта: final Dialog dialog = super.onCreateDialog (savedInstanceState); dialog.setTitle (m_callback.getTitle ()); // не вдало додати діалогове вікно повернення кнопки скасування;
farid_z

33

Нижче код походить від керівництва google, тому відповідь полягає в тому, що ви не можете робити, як ваш, у 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;
    }
}

1
у вас є посилання?
Даніель Гомес Ріко

@Zebphyr Що робити, якщо я хочу використовувати цей CustomDialogFragment як діалогове вікно для своєї діяльності, але у коді вище немає згадки про R.layout.my_layout у onCreateDialog()методі. Чи onCreateView()допоможе в цьому випадку?
CopsOnRoad

@Jack Jan, Так, ви можете вказати файл макета у виклику onCreateView ().
Зефір,

16

Ось приклад використання 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();


    }

1
Цей приклад для мене ідеально працює. Ви повинні помістити весь свій код в onCreateDialog, а не з onCreateView. Цей код дозволяє користувачеві робити це, а також отримувати кнопки. Ідеально!
Брендон

10

Як каже @Xavier Egea, якщо у вас реалізовані і onCreateView (), і onCreateDialog (), ви ризикуєте отримати збій "requestFeature () перед додаванням вмісту". Це пов’язано з тим, що обидва onCreateDialog (), потім onCreateView () викликаються, коли ви показуєте () цей фрагмент як діалогове вікно (чому, я не знаю). Як згадував Тревіс Крістіан, причиною збою є інфляція () у onCreateView () після створення діалогового вікна у onCreateDialog ().

Один із способів реалізувати обидві ці функції, але уникнути цього збою: використовуйте getShowsDialog (), щоб обмежити виконання вашого onCreateView () (щоб ваш inflate () не викликався). Таким чином виконується лише ваш код onCreateDialog (), коли ви відображаєте свій DialogFragment як діалогове вікно, але ваш код onCreateView () може бути викликаний, коли ваш DialogFragment використовується як фрагмент у макеті.

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

@ user1530779 А як щодо кнопок? в OnCreateDialog я можу використовувати конструктор для встановлення кнопок і що я повинен робити в OnCreateView, щоб отримати кнопки, коли подання роздувається у діалоговому вікні?
Варвара Калініна

Хм, здається, використання конструктора дає мені виняток. Отже, яким би був спосіб налаштування кнопок, якщо подання роздувається у діалоговому вікні, а кнопки не встановлюються, коли це лише фрагмент?
Варвара Калініна

Ну, виявляється, якщо ви викликаєте dialog.setView замість dialog.setContentView, цей підхід справно працює, навіть якщо ви створюєте своє діалогове вікно за допомогою Builder і встановлюєте кнопки
Варвара Калініна

6

Якщо ви хочете мати легкий доступ до властивостей діалогового вікна, як-от заголовок та кнопка звільнення, але ви також хочете використовувати свій власний макет, ви можете використовувати LayoutInflator із вашим Builder, коли замінюєте 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();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.