Diseño personalizado para DialogFragment OnCreateView frente a OnCreateDialog

81

Estoy intentando crear un DialogFragment usando mi propio diseño.

He visto un par de enfoques diferentes. A veces, el diseño se establece en OnCreateDialog así: (Estoy usando Mono pero me he acostumbrado un poco a 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();
}

Este primer enfoque funciona para mí ... hasta que quiero usarlo, findViewByID. así que después de buscar en Google probé el segundo enfoque que implica anularOnCreateView

Así que comenté dos líneas de OnCreateDialogese conjunto de Diseño y luego agregué esto:

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

lo que me da un hermoso 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

Estoy perplejo.

gghuffer
fuente
Es demasiado tarde, pero aún publica el similar: stackoverflow.com/questions/21258228/…
Raghav Sharma

Respuestas:

37

Este primer enfoque funciona para mí ... hasta que quiero usar FindViewByID.

Supongo que no está alcanzando findViewById()la Vista devuelta por inflate(), intente esto:

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
fuente
1
De hecho, esto funciona. ¡Gracias! Sin embargo, todavía tengo curiosidad por saber por qué OnCreateView se bloquea.
gghuffer
2
@gghuffer Aunque esto es 4 meses tarde, no creo que esta excepción sea causada directamente por el código anterior. Es más común que las personas llamen requestFeature(...)(o algo así requestWindowFeature(Window.FEATURE_NO_TITLE);) después de agregar contenido (como ya indica el mensaje de excepción).
Griddo
54

Tuve la misma excepción con el siguiente código:

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

Debe elegir anular solo uno de onCreateView o onCreateDialog en un DialogFragment. Anular ambos resultará en la excepción: "requestFeature () debe ser llamado antes de agregar contenido".

Importante

Para obtener una respuesta completa, consulte el comentario de @TravisChristian. Como dijo, puede anular ambos, pero el problema surge cuando intenta inflar la vista después de haber creado la vista de diálogo.

Xavier Egea
fuente
32
Eso no es del todo cierto. Puede anular ambos (de hecho, DialogFragment lo dice), el problema surge cuando intenta inflar la vista después de haber creado la vista de diálogo. Todavía puede hacer otras cosas en onCreateView, como usar SavedInstanceState, sin causar la excepción.
Travis Christian
3
Igual que aquí. Necesita apoyar a ambos. Esa es la idea de usar el fragmento en línea o como un diálogo. Me parece como un insecto. Lo mejor que pude hacer es establecer el título del cuadro de diálogo, pero no tuve suerte al agregar un botón de cancelación en onCreateDialog llamando a super y configurando el título del objeto de diálogo devuelto: diálogo final Dialog = super.onCreateDialog (SavedInstanceState); dialog.setTitle (m_callback.getTitle ()); // no hubo suerte al agregar el diálogo de retorno del botón cancelar;
farid_z
33

El siguiente código proviene de la guía de Google, por lo que la respuesta es que no puede hacer como el suyo en onCreateDialog (), debe usar super.onCreateDialog () para obtener un diálogo.

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;
    }
}
Céfiro
fuente
1
¿Tienes un enlace?
Daniel Gomez Rico
@Zebphyr ¿Qué sucede si quiero usar este CustomDialogFragment como un diálogo para mi actividad, pero en el código anterior no se menciona R.layout.my_layout en el onCreateDialog()método? ¿ onCreateView()Ayudará en este caso?
CopsOnRoad
@Jack Jan, Sí, puede especificar el archivo de diseño en la llamada onCreateView ().
Zephyr
16

Aquí hay un ejemplo del uso de findViewById en un fragmento de diálogo

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


    }
Estoy con estúpido
fuente
1
Este ejemplo me funciona perfectamente. Debe poner todo su código en onCreateDialog en lugar de en onCreateView. Este código permite al usuario hacer precisamente eso además de obtener los botones. ¡Perfecto!
Brandon
10

Como dice @Xavier Egea, si tiene implementados onCreateView () y onCreateDialog (), corre el riesgo de que se bloquee "requestFeature () debe ser llamado antes de agregar contenido". Esto se debe a que se llama a AMBOS onCreateDialog () y luego onCreateView () cuando muestra () ese fragmento como un cuadro de diálogo (por qué, no lo sé). Como mencionó Travis Christian, inflate () en onCreateView () después de que se creó un diálogo en onCreateDialog () es lo que causa el bloqueo.

Una forma de implementar estas dos funciones, pero evitar este bloqueo: use getShowsDialog () para limitar la ejecución de su onCreateView () (para que no se llame a su inflate ()). De esta manera, solo se ejecuta su código onCreateDialog () cuando muestra su DialogFragment como un diálogo, pero su código onCreateView () se puede llamar cuando su DialogFragment se está utilizando como un fragmento en un diseño.

// 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
fuente
No veo el sentido de hacer esto ya que onCreateView está configurando vistas de todos modos, ¿por qué desea configurar vistas en ambos lugares? Puede mantener onCreateView como código de inflado común y que de todos modos se
inflará
@ user1530779 ¿Qué pasa con los botones? en OnCreateDialog puedo usar el constructor para configurar los botones y ¿qué se supone que debo hacer en OnCreateView para obtener los botones cuando la vista se infla en el diálogo?
Varvara Kalinina
Hmm, parece que usar el Builder me da una excepción. Entonces, ¿cuál sería la forma de configurar los botones si la vista se infla en el diálogo y no se configuran botones cuando es solo un fragmento?
Varvara Kalinina
Bueno, resulta que si llama a dialog.setView en lugar de dialog.setContentView, este enfoque funciona bien incluso si crea su diálogo con un Builder y establece botones
Varvara Kalinina
6

Si desea tener acceso fácil a las propiedades del diálogo, como el título y el botón de descartar, pero también desea usar su propio diseño, puede usar LayoutInflator con su Builder cuando anula 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
fuente