¿Mostrar diálogo del fragmento?

119

Tengo algunos fragmentos que necesitan mostrar un diálogo regular. En estos cuadros de diálogo, el usuario puede elegir una respuesta sí / no, y luego el fragmento debe comportarse en consecuencia.

Ahora, la Fragmentclase no tiene un onCreateDialog()método para anular, así que supongo que tengo que implementar los diálogos afuera, en el contenedor Activity. Está bien, pero luego es Activitynecesario informar la respuesta elegida de alguna manera al fragmento. Por supuesto, podría usar un patrón de devolución de llamada aquí, por lo que el fragmento se registra en la Activityclase con un oyente, y la Actividad informaría la respuesta a través de eso, o algo así.

Pero esto parece ser un gran lío para una tarea simple como mostrar un diálogo "simple" sí-no en un fragmento. Además, de esta manera mi Fragmentsería menos autónomo.

¿Hay alguna forma más limpia de hacer esto?

Editar:

La respuesta a esta pregunta no explica realmente en detalle cómo se debe usar DialogFragments para mostrar los diálogos de Fragments. Entonces AFAIK, el camino a seguir es:

  1. Muestre un fragmento.
  2. Cuando sea necesario, cree una instancia de DialogFragment.
  3. Establezca el Fragmento original como el destino de este DialogFragment, con .setTargetFragment().
  4. Muestre el DialogFragment con .show () del Fragmento original.
  5. Cuando el usuario elige alguna opción en este DialogFragment, notifique al Fragmento original sobre esta selección (por ejemplo, el usuario hizo clic en 'sí'), puede obtener la referencia del Fragmento original con .getTarget ().
  6. Descarte DialogFragment.
Zsombor Erdődy-Nagy
fuente
1
Su técnica funciona, excepto cuando ocurre una rotación de pantalla. Entonces tengo una fuerza cercana. ¿Algunas ideas?
Weston
@Weston echa un vistazo a la primera respuesta de Zsombor: stackoverflow.com/questions/8235080/…
mightimaus

Respuestas:

37

En su lugar, debería utilizar un DialogFragment .

mgv
fuente
9
Desafortunadamente, este enfoque es un poco más detallado que el enfoque clásico de cuadros de diálogo administrados de las revisiones anteriores de Android, pero ahora es el método preferido. Puede evitar hacer referencia a la Activitypor completo utilizando los métodos putFragmenty getFragmentde FragmentManager, lo que permite DialogFragmentque informe directamente al fragmento de llamada (incluso después de cambios de orientación).
Dave
¿Qué sucede si tiene un ListFragment que necesita mostrar Diálogos, no puede extenderlos a ambos
Marchinram
16
La subclase ListFragment usaría DialogFragments creando instancias de nuevos, no subclasificando DialogFragment. (Un DialogFragment es un diálogo implementado como un Fragmento, no un Fragmento que puede mostrar Diálogos.)
nmr
4
Agregue un fragmento para que podamos entenderlo fácilmente
Arpit Patel
35

Debo dudar con cautela de la respuesta previamente aceptada de que usar DialogFragment es la mejor opción. El propósito previsto (principal) de DialogFragment parece ser mostrar fragmentos que son diálogos en sí mismos, no mostrar fragmentos que tienen diálogos para mostrar.

Creo que usar la actividad del fragmento para mediar entre el diálogo y el fragmento es la opción preferible.

Mark D
fuente
10
Dado que el onCreateDialogenfoque de los diálogos administrados ( ) pronto quedará obsoleto, no estoy de acuerdo y diría que ese DialogFragmentes el camino a seguir.
Dave
4
La respuesta aceptada significa usar un DialogFragment en lugar de un Dialog, no en lugar de un ListFragment, AFAICS.
RMN
En realidad, un fragmento de diálogo se puede utilizar como un diálogo y un fragmento normal; consulte incrustación developer.android.com/reference/android/app/DialogFragment.html
Clive Jefferies
@CliveJefferies Puede, pero no se supone que muestre otros diálogos desde dentro. Creo que los chicos de aquí están entendiendo mal la pregunta.
Marcel Bro
@anoniim depende de cómo se utilice el fragmento de diálogo. Si se usa como un fragmento regular, está bien. Si se está utilizando como un diálogo, entonces sí, no debería mostrar otro diálogo.
Clive Jefferies
24

Aquí hay un ejemplo completo de un DialogFragment sí / no:

La clase:

public class SomeDialog extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle("Title")
            .setMessage("Sure you wanna do this!")
            .setNegativeButton(android.R.string.no, new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do nothing (will close dialog)
                }
            })
            .setPositiveButton(android.R.string.yes,  new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do something
                }
            })
            .create();
    }
}

Para iniciar el diálogo:

        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        // Create and show the dialog.
        SomeDialog newFragment = new SomeDialog ();
        newFragment.show(ft, "dialog");

También puede permitir que la clase implemente onClickListener y lo use en lugar de oyentes integrados.

Devolución de llamada a la actividad

Si desea implementar la devolución de llamada, así es como se hace en su actividad:

YourActivity extends Activity implements OnFragmentClickListener

y

@Override
public void onFragmentClick(int action, Object object) {
    switch(action) {
        case SOME_ACTION:
        //Do your action here
        break;
    }
}

La clase de devolución de llamada:

public interface OnFragmentClickListener {
    public void onFragmentClick(int action, Object object);
}

Luego, para realizar una devolución de llamada desde un fragmento, debe asegurarse de que el oyente esté adjunto de esta manera:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentClickListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement listeners!");
    }
}

Y una devolución de llamada se realiza así:

mListener.onFragmentClick(SOME_ACTION, null); // null or some important object as second parameter.
Warpzit
fuente
4
Esto no explica cómo iniciarlo desde un fragmento
akohout
@raveN simplemente haría una devolución de llamada a su actividad que luego iniciaría el fragmento.
Warpzit
@Warpzit Gracias por la respuesta completa. Temo tocar el FragmentManager, con el que ya estoy haciendo cosas impías ... ¿Cómo puedo pasar los eventos onClick al Fragment (que no es de diálogo) que necesitaba la entrada del usuario? Por cierto, ¿no debería agregarse la transacción al backstack?
2014
@kaay de la actividad puede llamar a cualquier método público en el fragmento dado que necesita la nueva entrada. A partir de ahí, debería ser bastante fácil actualizar con el nuevo contenido.
Warpzit
1
@RichardLeMesurier De hecho, los fragmentos son altibajos.
Warpzit
13

Para mí, fue lo siguiente:

MyFragment:

public class MyFragment extends Fragment implements MyDialog.Callback
{
    ShowDialog activity_showDialog;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        try
        {
            activity_showDialog = (ShowDialog)activity;
        }
        catch(ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "ShowDialog interface needs to be     implemented by Activity.", e);
            throw e;
        }
    }

    @Override
    public void onClick(View view) 
    {
        ...
        MyDialog dialog = new MyDialog();
        dialog.setTargetFragment(this, 1); //request code
        activity_showDialog.showDialog(dialog);
        ...
    }

    @Override
    public void accept()
    {
        //accept
    }

    @Override
    public void decline()
    {
        //decline
    }

    @Override
    public void cancel()
    {
        //cancel
    }

}

MyDialog:

public class MyDialog extends DialogFragment implements View.OnClickListener
{
    private EditText mEditText;
    private Button acceptButton;
    private Button rejectButton;
    private Button cancelButton;

    public static interface Callback
    {
        public void accept();
        public void decline();
        public void cancel();
    }

    public MyDialog()
    {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.dialogfragment, container);
        acceptButton = (Button) view.findViewById(R.id.dialogfragment_acceptbtn);
        rejectButton = (Button) view.findViewById(R.id.dialogfragment_rejectbtn);
        cancelButton = (Button) view.findViewById(R.id.dialogfragment_cancelbtn);
        acceptButton.setOnClickListener(this);
        rejectButton.setOnClickListener(this);
        cancelButton.setOnClickListener(this);
        getDialog().setTitle(R.string.dialog_title);
        return view;
    }

    @Override
    public void onClick(View v)
    {
        Callback callback = null;
        try
        {
            callback = (Callback) getTargetFragment();
        }
        catch (ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "Callback of this class must be implemented by target fragment!", e);
            throw e;
        }

        if (callback != null)
        {
            if (v == acceptButton)
            {   
                callback.accept();
                this.dismiss();
            }
            else if (v == rejectButton)
            {
                callback.decline();
                this.dismiss();
            }
            else if (v == cancelButton)
            {
                callback.cancel();
                this.dismiss();
            }
        }
    }
}

Actividad:

public class MyActivity extends ActionBarActivity implements ShowDialog
{
    ..

    @Override
    public void showDialog(DialogFragment dialogFragment)
    {
        FragmentManager fragmentManager = getSupportFragmentManager();
        dialogFragment.show(fragmentManager, "dialog");
    }
}

Diseño de DialogFragment:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/dialogfragment_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:text="@string/example"/>

    <Button
        android:id="@+id/dialogfragment_acceptbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/dialogfragment_textview"
        android:text="@string/accept"
        />

    <Button
        android:id="@+id/dialogfragment_rejectbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_alignLeft="@+id/dialogfragment_acceptbtn"
        android:layout_below="@+id/dialogfragment_acceptbtn"
        android:text="@string/decline" />

     <Button
        android:id="@+id/dialogfragment_cancelbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_rejectbtn"
        android:layout_below="@+id/dialogfragment_rejectbtn"
        android:text="@string/cancel" />

     <Button
        android:id="@+id/dialogfragment_heightfixhiddenbtn"
        android:layout_width="200dp"
        android:layout_height="20dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_cancelbtn"
        android:layout_below="@+id/dialogfragment_cancelbtn"
        android:background="@android:color/transparent"
        android:enabled="false"
        android:text=" " />
</RelativeLayout>

Como dialogfragment_heightfixhiddenbtnmuestra el nombre , simplemente no pude encontrar una manera de arreglar que la altura del botón inferior se redujera a la mitad a pesar de decirlo wrap_content, así que agregué un botón oculto para que se "corte" por la mitad. Perdón por el truco.

EpicPandaForce
fuente
1
+1 el conjunto de referencia con setTargetFragment()es recreado correctamente por el sistema cuando reinicia el conjunto Actividad / Fragmento después de la rotación. Entonces, la referencia apuntará al nuevo objetivo automáticamente.
Richard Le Mesurier
Sin embargo, me daría un puñetazo en la nariz ahora por no usar ButterKnife en el pasado. Use ButterKnife, hace que su manejo de la vista sea mucho más bonito.
EpicPandaForce
Y puede usar Otto para enviar eventos de Fragmento a Actividad, de modo que no necesite la interfaz para adjuntar magia. Ejemplo de Otto aquí: stackoverflow.com/a/28480952/2413303
EpicPandaForce
@EpicPandaForce Por favor, ¿puede agregar la interfaz / clase "ShowDialog"? Es lo único que falta en tu ejemplo.
ntrch
@ntrch fuepublic interface ShowDialog { void showDialog(DialogFragment dialogFragment); }
EpicPandaForce
3
 public void showAlert(){


     AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
     LayoutInflater inflater = getActivity().getLayoutInflater();
     View alertDialogView = inflater.inflate(R.layout.test_dialog, null);
     alertDialog.setView(alertDialogView);

     TextView textDialog = (TextView) alertDialogView.findViewById(R.id.text_testDialogMsg);
     textDialog.setText(questionMissing);

     alertDialog.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
             dialog.cancel();
         }
     });
     alertDialog.show();

}

donde .test_dialog es de xml personalizado

Alex Zaraos
fuente
2

Yo mismo soy un principiante y, sinceramente, no pude encontrar una respuesta satisfactoria que pudiera entender o implementar.

Así que aquí hay un enlace externo que realmente me ayudó a lograr lo que quería. Es muy sencillo y fácil de seguir.

http://www.helloandroid.com/tutorials/how-display-custom-dialog-your-android-application

ESTO LO QUE INTENTÉ LOGRAR CON EL CÓDIGO:

Tengo una MainActivity que aloja un Fragmento. Quería que apareciera un cuadro de diálogo en la parte superior del diseño para solicitar la entrada del usuario y luego procesar la entrada en consecuencia. Ver una captura de pantalla

Esto es lo que se ve en onCreateView de mi fragmento

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.fragment_home_activity, container, false);

    Button addTransactionBtn = rootView.findViewById(R.id.addTransactionBtn);

    addTransactionBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Dialog dialog = new Dialog(getActivity());
            dialog.setContentView(R.layout.dialog_trans);
            dialog.setTitle("Add an Expense");
            dialog.setCancelable(true);

            dialog.show();

        }
    });

Espero que te ayude

Avísame si hay alguna confusión. :)

Junaid Aziz
fuente
0
    public static void OpenDialog (Activity activity, DialogFragment fragment){

    final FragmentManager fm = ((FragmentActivity)activity).getSupportFragmentManager();

    fragment.show(fm, "tag");
}
Elmar Fazlagic
fuente
3
por favor agregue alguna explicación a su respuesta
RealCheeseLord