jueves, 26 de abril de 2012

6.9.7.PeA - Actividad Configuración VII-Diálogos


6.9.7.- Programación en Android – Actividad Configuración VII – Diálogos

En este capítulo seguiremos añadiendo y modificando el código para la ActividadConfiguracion.java, de forma que al terminar este tutorial ya podremos añadir la fecha de nacimiento y ver el control DatePicker en acción.
Pero primeramente vamos a hablar un poco sobre los diálogos.

Los diálogos son una manera de aprovechar el espacio disponible en la pantalla del teléfono sin saturar el interfaz, ya que los datos se piden en una ventana emergente, con lo que nos evitamos muchos cambios de actividad, evitando así saturar el SO. Normalmente son pequeñas ventanas que se muestran delante de nuestra actividad y que toman el control de la interacción con el usuario.

Tipos de diálogos

La clase base para la creación de diálogos es Dialog, aunque normalmente no se hace una instancia directamente de esta clase, sino de alguna de las siguientes subclases:

Tipo de diálogo
Descripción
Dialog (Clase Base)
Es la clase base para la generación de diálogos. Las demás subclases heredan de esta.
AlertDialog
Es un diálogo que puede manejar hasta 3 botones y/o una lista de elementos seleccionables que pueden incluir botones circulares o de checkeo (Radio buttons y checkboxes).
CharacterPickerDialog
Diálogo utilizado para seleccionar carácteres especiales.
DatePickerDialog
Un diálogo que permite al usuario escoger una fecha.
ProgressDialog
Es un diálogo que muestra una rueda o una barra de progreso. Es una extensión de AlertDialog, por lo tanto también soporta botones.
TimePickerDialog
Un diálogo que permite al usuario escoger una hora.
Custom Dialogs
(Diálogos a medida)
Es una forma de crear diálogos propios o a medida. Se puede hacer extendiendo la clase Dialog o alguna de las anteriores, y definiendo un nuevo diseño (layout).


Mostrando un Diálogo

Los diálogos siempre son creados como parte de una actividad. Normalmente se crean en el método de retrollamada (callback) onCreateDialog(int). Al utilizar este callback, el SO Android maneja automáticamente el estado de cada uno de los diálogos y los enlaza a una Activity, haciendo que dicha Activity sea la propietaria del mismo. Asimismo el diálogo también hereda algunas particularidades de la Activity a la que pertenece, como puede ser el menú de opciones definido para la actividad.
Los diálogos también se pueden crear fuera del método onCreateDialog(int), y entonces no se enganchan a ninguna actividad. Si queremos posteriormente hacer que pertenezca a una actividad determinada, debemos utilizar el método setOwnerActivity(Activity).

Para mostrar el diálogo se utiliza la función showDialog(int), pasándole como argumento un int que es el identificador único del diálogo a mostrar.

Cuando se muestra por primera vez, Android llama a onCreateDialog(int), que es dónde debemos instanciar el diálogo. A esta retrollamada se le pasa el mismo identificador de diálogo que se le pasó al método showDialog(int). Después de crear el diálogo debemos retornar el objeto (diálogo) al finalizar el método.

Antes de que se muestre el diálogo, Android también llama a un método opcional: onPrepareDialog(int, Dialog). Este método sólo nos hará falta en caso de que queramos cambiar las propiedades del diálogo cada vez que se abra. Este método se llama siempre y cada una de las veces que se abra un diálogo, mientras el método onCreateDialog(int) sólo se llama la primera vez que se abre un diálogo. En caso de que no hallamos creado este método (onPrepareDialog) entonces el diálogo seguirá igual que la última vez que se abrió.

La mejor forma de definir las retrollamadas (callback) a los métodos onCreateDialog(int) y onPrepareDialog(int, Dialog), es en una sentencia de tipo switch, en la cuál se comprobará el parámetro de identificación del diálogo que se le ha pasado al método. Cada sentencia case deberá comprobar un único identificador de de diálogo para después crear y definir dicho diálogo.

Descartando un diálogo

Cuando queramos desechar un diálogo, debemos llamar al método dismissDialog(int) si lo hacemos desde la Activity o simplemente dismiss() si lo hacemos desde el propio diálogo.
Si el diálogo ha sido creado a través del método onCreateDialog(int), entonces aunque descartemos el diálogo, su estado permanece retenido por la Activity. Si decidimos que no vamos a necesitarlo más o que es importante que se borre su estado, entonces debemos llamar al método removeDialog(int), que quitará todas las referencias internas al objeto y en caso de que se este mostrando lo descartará.

Si se desea que la aplicación realice algún procedimiento cuando se desecha el diálogo, entonces debemos enlazarlo con un listener (oyente) OnDismiss. Para ello debemos crear primero el interfaz DialogInterface.OnDismissListener, que contendrá un único método: onDismiss(DialogInterface), que será llamado cuando se descarte el diálogo. Después sólo debemos pasar nuestra implementación de OnDismissListener al método setOnDismissListener().

Los diálogos también se pueden cancelar, este caso ocurre cuando el usuario pulsa el botón atrás para cerrar el diálogo o si el diálogo llama explícitamente al método cancel(). Cuándo se cancela un diálogo todavía se llama a OnDismissListener, pero si queremos diferenciar si ha sido cancelado expresamente en vez de desechado con normalidad, entonces debemos de registrar el listener DialogInterface.OnCancellListener con setOnCancellListener().

Resumen

Vamos a ver una tabla con un resumen de algunas de las opciones:

Ciclo de vida del diálogo
Función
Descripción
showDialog
Para mostrar una instancia del diálogo. Se le pasa un identificador del diálogo.
dismissDialog
Para dejar de mostrar el diálogo, rechazarlo o descartarlo. El diálogo permanece en el "cache" de diálogos de la aplicación, si se vuelve a llamar a showDialog entonces el diálogo se carga desde el cache.
removeDialog
Para quitar un diálogo del cache. Ya no se guarda para usos futuros, en caso de llamar posteriormente a showDialog entonces el diálogo tiene que volver a ser creado.
Definiendo el diálogo
Función
Descripción
onCreateDialog
Los diálogos deben ser definidos de antemano y cada uno tiene un identificador (int). Se debe sobreescribir este método para que devuelva la instancia correcta para el identificador pasado como parámetro. Sólo se llama una vez para crear el diálogo.
Inicializando el diálogo
Función
Descripción
onPrepareDialog
Método opcional que tenemos que sobreescribir en caso de que queramos alterar el contenido del diálogo. Se llama cada vez que se llama al método showDialog.

Ahora que tenemos un poco de información sobre los diálogos, vamos a proceder a añadir uno a nuestra ActividadConfiguración.

Añadiendo un diálogo DatePickerDialog

Para añadir un diálogo a nuestra actividad, debemos realizar una serie de pasos:
1.-Debemos crear un identificador único para nuestro diálogo.
2.-Debemos de implementar el método onCreateDialog(). Y dentro de él añadir una clase interna que contendrá el listener para OnDateSetListener.
3.-Debemos de implementar el método onPrepareDialog(), ya que alteraremos los datos iniciales mostrados en el mismo.
4.-Debemos cambiar el método onClickBoton_PonerFecha, para que muestre nuestro diálogo.
5.-Realizar otros pequeños cambios al código.

#1 – Creando un identificador para nuestro diálogo

Para añadir un identificador para nuestro diálogo, simplemente añadimos una variable miembro a nuestra clase ActividadConfiguración:

/**Identificador para el diálogo de la fecha */
static final int ID_DIALOGO_FECHA = 0;


Este identificador está fuera de cualquier método. Aprovechamos también para añadir una variable miembro, que será la que tenga el valor de la fecha de nacimiento como un long:

/**
* El valor aunque no se inicialize será 0L
* Utilizamos long en vez del wrapper (Long) ya que no vamos a *utilizar sus funciones.
* En caso de querer utilizarlas se utilizaria:Long fecha = *Long.valueOf(mFechaDeNacimiento);
*/
long mFechaDeNacimiento;


#2 – Implementando el método onCreateDialog y el listener OnDateSetListener

Vamos a implementar el método onCreateDialog, para ello abrimos el menú pinchando con el botón derecho sobre el código y vamos a Codigo fuente->Alterar temporalmente/implementar métodos, y ahí marcamos el método onCreateDialog (int id). Este método quedaría como sigue:

protected Dialog onCreateDialog(int id)
{

/**Obtenemos la referencia al textView de la fecha de nacimiento:*/
final TextView TV_Fecha = (TextView) findViewById(R.id.tV_EstadoFecha);
/**
* Variable para obtener el día actual
*/
Calendar fechaDeHoy = Calendar.getInstance();
//-----------------------------------------------
//Listener para el onDateSet
class fecha_OnDateSetListener implements
DatePickerDialog.OnDateSetListener
{
/** OJO: El orden que tienen los parámetros en DatePicker
* no es el mismo que el orden que se utiliza en Time.set
* En Time.set no se verifican los valores, con lo cual en caso
* de no ser correctos nos fallará la conversión de toMillis
*/
@Override
public void onDateSet(DatePicker view, int año, int mes, int dia)
{
//Creamos una variable que contendrá la fecha de nacimiento entrada por el usuario.
Time fechaDeNacimiento = new Time();
//Hacemos que dicha fecha coja los valores que hay en el Datepicker (dia, mes, año)
fechaDeNacimiento.set(dia, mes, año);
//fechaDeNacimiento.normalize(true);
//TODO: Deberíamos comparar estos valores con la fecha actual:
//Convertimos la fecha a un long (a millisegundos)
mFechaDeNacimiento = fechaDeNacimiento.toMillis(true);

//Cambiamos el TextView de la fecha para que ponga este valor.
TV_Fecha.setText(DateFormat.format(
res.getString(R.string.formatoFecha),
mFechaDeNacimiento));
//TODO Deberíamos guardar la fecha.
TV_Fecha.setTextColor(res
.getColor(R.color.color_EditText_Configuracion));

}
}
//------------------------------------------------

switch (id)
{
case ID_DIALOGO_FECHA:
{
//No son necesarios los corchetes. Es una costumbre para verlo mejor.
//TODO: AQui retornamos un DatePickerDialog

//Creamos el DatePickerDialog
DatePickerDialog dialogoFecha = new DatePickerDialog(this,
new fecha_OnDateSetListener(),
fechaDeHoy.get(Calendar.YEAR),
fechaDeHoy.get(Calendar.MONTH),
fechaDeHoy.get(Calendar.DAY_OF_MONTH));
//Devolvemos la instancia del diálogo.
return dialogoFecha;

}
}
return null;
}

Hemos creado una clase interna para el listener de DatePickerDialog.OnDateSetListener, pero podíamos haberla creado anónima. En ese listener lo que hacemos es coger los datos del diálogo, convertirlos a un long para almacenarlos en la variable mFechaDeNacimiento y después ponemos el TextView de la fecha con el formato adecuado.

Asimismo podemos ver que en la sentencia switch hacemos uso de la clase Calendar para obtener la fecha actual que es la que se le para al diálogo para ser mostrada en caso de que no sea válida o que no tengamos una fecha.

#3 – Implementación del método onPrepareDialog

Ahora debemos seguir los mismos pasos que antes para añadir la implementación del método onPrepareDialog, el cuál quedará de la siguiente manera:

/**
* onPrepareDialog: Este método inicializa los diálogos
* a sus valores iniciales.
*/
@Override
protected void onPrepareDialog(int id, Dialog dialog)
{
// TODO Auto-generated method stub
super.onPrepareDialog(id, dialog);

switch (id)
{
case ID_DIALOGO_FECHA:
{
//Añadimos el código para controlar la aparición de este diálogo
/** Estos tres ints guardaran la fecha actual o la guardada para
* cuando se llame al diálogo con updateDate
*/
int dia, mes, año;
/** Diálogo DatePicker
* (dialog): viene como parámetro para este método */
DatePickerDialog dialogoFecha = (DatePickerDialog) dialog;
//Comprobamos si hay una fecha en la variable: mFechaDeNacimiento
if (mFechaDeNacimiento != 0)
{
//Si hay Fecha: ponemos en el diálogo los valores de la fecha:
/**#1 Creamos un objeto para poder descomponer el Long en
* Dia, mes, hora...
*/
Time fechaDeNacimiento = new Time();
/** Y la inicializamos*/
fechaDeNacimiento.set(mFechaDeNacimiento);
//Actualizamos la fecha para cuando hagamos la llamada
dia = fechaDeNacimiento.monthDay;
mes = fechaDeNacimiento.month;
año = fechaDeNacimiento.year;
}
else
{
//En caso de que no exista una fecha, ponemos la fecha actual:
//Obtenemos una instancia del calendario:
Calendar fechaDeHoy = Calendar.getInstance();
dia = fechaDeHoy.get(Calendar.DAY_OF_MONTH);
mes = fechaDeHoy.get(Calendar.MONTH);
año = fechaDeHoy.get(Calendar.YEAR);
}
//Ahora actualizamos la fecha del DatePicker:
dialogoFecha.updateDate(año, mes, dia);
//Y volvemos
return;
}
}
}

En este método también comprobamos si está siendo llamado para nuestro diálogo, y en ese caso comprobamos el valor de mFechaDeNacimiento para ver si tiene un valor válido, en caso de ser así cogemos los valores del día, mes y año, en caso contrario cogemos los valores del día actual, y despues llamamos al método updateDate del diálogo.

#4 – Modificar el método onClickBoton_PonerFecha
Alteramos este método y quitamos el mensaje toast que teníamos y ponemos la siguiente línea:

showDialog(ID_DIALOGO_FECHA);

Ahora cada vez que se pulse en este botón se mostrará nuestro diálogo.

#5 – Otros cambios al código

También debemos de realizar cambios en el método inicializaFecha, de forma que coja el valor de mFechaDeNacimiento para mostrar en la TextView.
Y por último debemos modificar el método onPause para que grabe el dato:

mEditorFicheroConf.putLong(PREFERENCIAS_JUEGO_FECHA_NACIMIENTO,
mFechaDeNacimiento);

Y ya deberíamos de poder ejecutar el programa y ver como funciona nuestro diálogo.

El código para el proyecto está en el siguiente enlace: aquí.


Ver. 1.0 – Revisión 25/04/2012

No hay comentarios:

Publicar un comentario