Dialogs

Podríamos definir a un dialogo como una pequeña ventana que normalmente se utiliza para que el usuario tome una decisión antes de proceder con el siguiente paso. Se utiliza como base la clase "Dialog" y se debe evitar crear instancias directamente, por lo que tendremos que utilizar una de las siguientes subclases:
  • AlertDialog - permite mostrar un titulo, tres botones como máximo, una lista con elementos seleccionables o incluso un diseño personalizado.
  • DatePickerDialog - interface predefinida que permite al usuario seleccionar una fecha.
  • TimePickerDialog - permite al usuario seleccionar una hora.

Es recomendable utilizar la clase "DialogFragment" para contener el dialogo ya que nos proporciona todos los controles para su creación y también nos permite gestionar la apariencia del dialogo. Hay que tener en cuenta que dicha clase se agrego en Android 3.0 (API 11) por lo que si nuestro proyecto tiene un nivel de API 10 o inferior, tendremos que hacer uso de la biblioteca de compatibilidad v4.

Toda la información de este articulo disponible en: Dialogs


1. Estructura


Un dialogo se divide en 3 zonas:
  • Icono y Titulo - es opcional y permite mostrar un icono y un titulo en la cabecera del dialogo.
  • Área de contenido - es la zona mas importante del dialogo por ser el lugar donde se muestra la información. Esta información puede ser un simple mensaje, una lista o incluso un diseño personalizado.
  • Botones de acción - de manera opcional se pueden agregar tres botones como máximo.



2. Construcción

Tal como se comentaba al principio del articulo, necesitamos crear una clase que extienda de "DialogFragment" y dentro del método "onCreateDialog()" construiremos el dialogo mediante la clase "AlertDialog.Builder". Para ello tenemos que seguir unos pasos:
  1. Crear la instancia de "AlertDialog.Builder"
  2. Configurar el dialogo a través de los métodos predefinidos
  3. Obtener el dialogo mediante el método "create()"

El siguiente ejemplo muestra la creación de un dialogo básico correspondiente a la imagen del punto 1:
public class FmDialogoBasico extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
  
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        
        builder.setTitle("Titulo");
        builder.setIcon(R.drawable.ic_launcher);
        
        builder.setMessage("Area de contenido");
        
        builder.setPositiveButton("Posivito", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
             
            }
        });
        builder.setNeutralButton("Neutral", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
             
            }
        });
        builder.setNegativeButton("Negativo", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
          
            }
        });
        
        return builder.create();
    }
 
}
En este caso se añade un titulo y un icono mediante los métodos "setTitle()" y "setIcon()". En el área de contenido simplemente mostramos un mensaje a través del método "setMessage()". Y por ultimo añadimos los tres posibles botones mediante los métodos "setPositiveButton(), "setNeutralButton()" y "setNegativeButton()".

Para terminar tendremos que devolver un objeto Dialog mediante el método "create()". De este modo cada vez que se cree una instancia del fragmento y se llame al método "show()", el dialogo sera visible en la pantalla del usuario.


2.1 Añadir botones de acción

En el punto anterior hemos comentado los métodos que se utilizan para añadir botones a nuestro dialogo, estos métodos nos permiten crear tres tipos de respuesta:

  • Positiva
  • Negativa
  • Neutral

Dichos métodos nos piden como parámetro un titulo y la interface "DialogInterface.OnclickListener" que sera la encargada de manejar los eventos del botón. Recordamos que como máximo podemos utilizar tres botones o ninguno.


2.2 Añadir una lista

En el punto 2 hemos visto como añadir un mensaje en el área de contenido del dialogo mediante el método "setMessage()". En este caso vamos a ver como se puede añadir una lista pero primero tenemos que saber los tipos de lista que podemos añadir a un dialogo:
  1. Una lista con una sola opción seleccionable
  2. Una lista con una opción persistente (radio-buttons)
  3. Una lista con varias opciones persistentes (checkboxes)


2.2.1 Lista con una sola opción

Para crear este tipo de vista se utiliza el método "setItems()" que nos pide como parámetros: un array con los valores de la lista y la interface "DialogInterface.OnclickListener" que sera la encargada de manejar los eventos de la lista. A continuación vamos a ver un ejemplo:
final String[] colores = getResources().getStringArray(R.array.colors_array);

builder.setItems(colores, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        Toast.makeText(getActivity(), colores[which], Toast.LENGTH_SHORT).show();
    }
});

Si hemos configurado todo bien el resultado puede ser el siguiente:



2.2.2 Lista con una opción persistente

Este tipo de listas muestra una opción seleccionada por defecto. Para su creación utilizaremos el método "setSingleChoiceItems()" que nos pide como parametros: un array con los valores de la lista, un valor Integer que sera el utilizado para establecer la opción por defecto y la interface "DialogInterface.OnclickListener" encargada de manejar los eventos de la lista. Vamos a verlo en un ejemplo:
final String[] colores = getResources().getStringArray(R.array.colors_array);

builder.setSingleChoiceItems(colores, single, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        Toast.makeText(getActivity(), colores[single=which], Toast.LENGTH_SHORT).show();
        dialog.dismiss();
    }
});

Comentar que la variable "single" es la encargada de almacenar y establecer la opción de la lista. A parte sino añadimos ningún botón a este tipo de dialogo deberemos cerrarlo mediante el método "dismiss()". Si todo esta bien configurado podremos obtener lo siguiente:



2.2.3 Lista con varias opciones persistentes

Es muy similar al anterior pero en este caso se utiliza el método "setMultiChoiceItems()", como segundo parámetro en vez de un Integer aquí se utiliza un array booleano para establecer las opciones guardadas y como interface para manejar los eventos de la lista se utiliza "DialogInterface.OnMultiChoiceClickListener". Por otro lado también es recomendable añadir dos botones: uno para aceptar los cambios y otro para cancelar los cambios:
final String[] cls = getResources().getStringArray(R.array.colors_array);

loadItems(bol);

builder.setMultiChoiceItems(cls, bol, new DialogInterface.OnMultiChoiceClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
    
    }
});
  
builder.setPositiveButton(R.string.aceptar, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int id) {
        saveItems(bol);
    }
});
         
builder.setNegativeButton(R.string.cancelar, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int id) {
              
    }
});

Normalmente se utiliza la interface "DialogInterface.OnMultiChoiceClickListener" para manejar los cambios en las opciones de la lista. Pero yo me he decantado por crear dos métodos personalizados:
"loadItems()" para cargar las opciones guardadas y "saveItems()" para guardar las opciones cuando el usuario acepte el dialogo.

Deberemos tener en cuenta que la variable "bol" va almacenar en todo momento cualquier cambio en las opciones aunque el usuario cancele el dialogo.

El resultado puede ser el siguiente:



2.3 Añadir un diseño personalizado

Primero tendremos que crear nuestro diseño personalizado en un archivo de recursos layout xml. Una vez creado tendremos que inflar ese diseño a través de un objeto "LayoutInflater" y para terminar estableceremos el nuevo diseño al dialogo mediante el método "setView()". Vamos a ver el ejemplo:
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.dialog_custom, null);

final EditText username = (EditText) view.findViewById(R.id.username);
final EditText password = (EditText) view.findViewById(R.id.password);

builder.setView(view);

De manera opcional también podremos utilizar los métodos vistos anteriormente para añadir un titulo, un icono o incluso botones. Si hemos configurado todo bien el resultado puede ser el siguiente:




3. Mostrar un dialogo

Para mostrar el dialogo es necesario crear una instancia de nuestro DialogFragment y a continuación llamar al método "show()" que nos pide como parámetros un objeto "FragmentManager" y una etiqueta para identificar al dialogo.

Para crear el objeto "FragmentManager" utilizaremos el método "getFragmentManager()" (para un nivel de API 11 o superior) o "getSupportFragmentManager()" (para un nivel de API 10 o inferior). Por otro lado la etiqueta normalmente la utiliza el sistema para guardar y restaurar el estado del dialogo aunque también la podremos utilizar nosotros para obtener un identificador del dialogo a través del método "findFragmentByTag()".

A continuación se muestra un ejemplo para visualizar un dialogo:
DialogFragment newFragment = new MiDialogFragment();
newFragment.show(getSupportFragmentManager(), "etiqueta");



4. Devolver eventos del dialogo

En alguna ocasión puede ser necesario compartir determinados eventos del dialogo con la actividad o fragmento que inicio dicho dialogo.

Para ello tendremos que crear una interface en el dialogo y registrar cada tipo de evento. Una vez configurada la interface sera necesario implementarla en el componente que inicie el dialogo para así establecer la comunicación entre el dialogo y el componente que lo inicio.

Vamos a verlo en un ejemplo, empezaremos creando la interface en el dialogo:
public interface DialogListener {
    public void onDialogPositiveClick(DialogFragment dialog);
    public void onDialogNegativeClick(DialogFragment dialog);
}
    
DialogListener mListener;
En este caso solo vamos a registrar los clicks en los botones del dialogo, por lo tanto creamos un método para cada botón. A continuación creamos un objeto de dicha interface para registrar los eventos.

El siguiente paso es configurar el método "onAttach()" para asegurarnos de que el componente que inicia el dialogo implemente dicha interface:
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (DialogListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + 
                       " debe implementar DialogListener");
    }
}

Y por ultimo tendremos que registrar los eventos cuando tengan lugar, por lo tanto configuramos los dos botones y registramos su click:
builder.setPositiveButton(R.string.aceptar, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int id) {
        mListener.onDialogPositiveClick(FmDialogoEventos.this);
    }
});
        
builder.setNegativeButton(R.string.cancelar, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int id) {
        mListener.onDialogNegativeClick(FmDialogoEventos.this);
    }
});

Una vez creada la interface y completamente configurada, tenemos que implementarla en el componente encargado de iniciar el dialogo. Por lo tanto al implementar la inteface en el componente (ya sea fragmento o actividad)  nos obliga a sobreescribir los dos métodos establecidos:
@Override
public void onDialogPositiveClick(DialogFragment dialog) {
    Toast.makeText(MenuPrincipal.this, "ACEPTAR", Toast.LENGTH_SHORT).show();
}
 
@Override
public void onDialogNegativeClick(DialogFragment dialog) {
    Toast.makeText(MenuPrincipal.this, "CANCELAR", Toast.LENGTH_SHORT).show();
}



5. Mini aplicación

Todo lo explicado en el articulo esta en forma de aplicación que podemos descargar en el siguiente enlace:

DESCARGAR: EJEMPLO DIALOG


3 comentarios:

  1. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  2. Si es un fragment el que inicia el dialog, da fallo al ejecutar la aplicación porque salta la excepción del onAttach del dialog (la que nosotros mismo escribimos). Si no me equivoco, habría que implementar la interfaz (y sus métodos) en la actividad y usar bundles o sharedPreferences para pasarle información al fragment que llama al dialog.

    ResponderEliminar
  3. Hola!!! Antes que nada gracias por el aporte! Una consulta...
    Cuando registramos el click del evento, en la parte de codigo siguiente:
    mListener.onDialogPositiveClick(FmDialogoEventos.this);

    De donde sale el FmDialogoEventos????

    Gracias desde ya.Saludos!

    ResponderEliminar