Układ niestandardowy dla DialogFragment OnCreateView w porównaniu z OnCreateDialog

81

Próbuję utworzyć DialogFragment przy użyciu mojego własnego układu.

Widziałem kilka różnych podejść. Czasami układ jest ustawiany w OnCreateDialog w następujący sposób: (używam Mono, ale trochę przyzwyczaiłem się do 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();
}

To pierwsze podejście działa dla mnie ... dopóki nie chcę go użyć, findViewByID. więc po chwili googlowania wypróbowałem drugie podejście, które obejmuje nadpisywanieOnCreateView

Skomentowałem więc dwie linie OnCreateDialogtego zestawu Układu, a następnie dodałem to:

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

co daje mi piękny błąd:

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

Jestem zdumiony.

gghuffer
źródło
Jest już za późno, ale wciąż publikuję podobne: stackoverflow.com/questions/21258228/ ...
Raghav Sharma

Odpowiedzi:

37

To pierwsze podejście działa dla mnie ... dopóki nie chcę używać FindViewByID.

Przypuszczam, że nie sięgasz findViewById()do widoku zwróconego przez inflate(), spróbuj tego:

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

return b.create();
Sam
źródło
1
To rzeczywiście działa. Dzięki! Nadal jestem ciekawy, dlaczego OnCreateView ulega awarii.
gghuffer
2
@gghuffer Chociaż jest to 4 miesiące spóźnienia, nie sądzę, aby ten wyjątek był bezpośrednio spowodowany przez powyższy kod. Częściej zdarza się, że ludzie dzwonią requestFeature(...)(lub coś podobnego requestWindowFeature(Window.FEATURE_NO_TITLE);) po dodaniu treści (na przykład komunikat o wyjątku już stwierdza).
Griddo
54

Miałem ten sam wyjątek z następującym kodem:

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

Musisz wybrać nadpisanie tylko jednego z onCreateView lub onCreateDialog w DialogFragment. Zastępowanie obu spowoduje wyjątek: „requestFeature () należy wywołać przed dodaniem treści”.

Ważny

Pełną odpowiedź znajdziesz w komentarzu @TravisChristian. Jak powiedział, możesz rzeczywiście przesłonić oba, ale problem pojawia się, gdy spróbujesz zawęzić widok po utworzeniu już widoku dialogowego.

Xavier Egea
źródło
32
To nie do końca prawda. Możesz nadpisać oba (w rzeczywistości tak mówi DialogFragment), problem pojawia się, gdy próbujesz zawyżać widok po utworzeniu widoku okna dialogowego. Nadal możesz robić inne rzeczy w onCreateView, na przykład używać saveInstanceState, bez powodowania wyjątku.
Travis Christian
3
To samo tutaj. Musi wspierać oba. To jest idea użycia fragmentu w linii lub jako okna dialogowego. Wydaje mi się, że to błąd. Najlepsze, co mogłem zrobić, to ustawić tytuł okna dialogowego, ale bez powodzenia dodając przycisk anulowania w onCreateDialog, wywołując super i ustawiając tytuł dla zwróconego obiektu okna dialogowego: final Dialog dialog = super.onCreateDialog (saveInstanceState); dialog.setTitle (m_callback.getTitle ()); // nie udało się dodać okna dialogowego powrotu do przycisku anulowania;
farid_z
33

Poniższy kod pochodzi z przewodnika Google, więc odpowiedź jest taka, że ​​nie możesz polubić swojego w onCreateDialog (), musisz użyć super.onCreateDialog (), aby uzyskać okno dialogowe.

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;
    }
}
Zefir
źródło
1
czy masz link?
Daniel Gomez Rico
@Zebphyr A co, jeśli chcę użyć tego CustomDialogFragment jako okna dialogowego do mojej aktywności, ale w powyższym kodzie nie ma wzmianki o R.layout.my_layout w onCreateDialog()metodzie. Czy onCreateView()pomoże w tym przypadku?
CopsOnRoad
@Jack Jan, Tak, możesz określić plik układu w wywołaniu onCreateView ().
Zephyr
16

Oto przykład użycia findViewById we fragmencie okna dialogowego

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


    }
Jestem z głupkiem
źródło
1
Ten przykład działa dla mnie idealnie. Musisz umieścić cały kod w onCreateDialog, a nie z onCreateView. Ten kod pozwala użytkownikowi zrobić to samo, a także uzyskać przyciski. Doskonały!
Brandon,
10

Jak mówi @Xavier Egea, jeśli masz zaimplementowane zarówno onCreateView (), jak i onCreateDialog (), istnieje ryzyko wystąpienia awarii „requestFeature ()”, która musi zostać wywołana przed dodaniem zawartości. Dzieje się tak, ponieważ ZARÓWNO onCreateDialog (), jak i onCreateView () są wywoływane, gdy pokazujesz () ten fragment jako okno dialogowe (dlaczego, nie wiem). Jak wspomniał Travis Christian, inflate () w onCreateView () po utworzeniu okna dialogowego w onCreateDialog () powoduje awarię.

Jeden ze sposobów na zaimplementowanie obu tych funkcji, ale uniknięcie tej awarii: użyj getShowsDialog (), aby ograniczyć wykonywanie onCreateView () (aby funkcja inflate () nie była wywoływana). W ten sposób tylko kod onCreateDialog () jest wykonywany podczas wyświetlania DialogFragment jako okna dialogowego, ale kod onCreateView () można wywołać, gdy DialogFragment jest używany jako fragment w układzie.

// 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;
}
Steve B.
źródło
Nie widzę sensu robienia tego, ponieważ onCreateView i tak konfiguruje widoki, dlaczego chcesz skonfigurować widoki w obu miejscach, w których możesz zachować onCreateView jako wspólny kod inflacyjny, który i tak zostanie zawyżony w oknie dialogowym
user1530779
@ user1530779 A co z przyciskami? w OnCreateDialog mogę użyć konstruktora, aby ustawić przyciski i co mam zrobić w OnCreateView, aby uzyskać przyciski, gdy widok jest zawyżony w oknie dialogowym?
Varvara Kalinina
Hmm, wygląda na to, że korzystanie z Buildera stanowi wyjątek. Więc jaki byłby sposób na skonfigurowanie przycisków, jeśli widok jest zawyżony w oknie dialogowym i nie ustawia żadnych przycisków, gdy jest to tylko fragment?
Varvara Kalinina
Cóż, okazuje się, że jeśli wywołasz dialog.setView zamiast dialog.setContentView to podejście działa dobrze, nawet jeśli utworzysz okno dialogowe za pomocą Buildera i ustawisz przyciski
Varvara Kalinina
6

Jeśli chcesz mieć łatwy dostęp do właściwości okna dialogowego, takich jak tytuł i przycisk odrzucenia, ale chcesz również użyć własnego układu, możesz użyć LayoutInflator w swoim Konstruktorze, gdy zastępujesz 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
źródło