lunes, 30 de abril de 2012

6.9.8.PeAndroid-Actividad Configuracion VIII-Dialogos personalizados


6.9.8.- Programación en Android – Actividad Configuración VIII – Diálogos personalizados

En este capítulo veremos como crear nuestros propios diálogos personalizados. Existen dos formas básicas para realizar los diálogos a medida, una forma consiste en heredar de la clase base Dialog, y después añadir un layout personalizado, botones en caso de necesitarlos, etc. Otra de las formas, consiste en utilizar la subclase AlertDialog, y en concreto AlertDialog.Builder, que nos provee de un mecanismo fácil para crearlo, y es la que utilizaremos en este primer ejemplo.

Pasos para crear un diálogo personalizado.

Los pasos son semejantes a los que tenemos que realizar para crear un diálogo, y la única diferencia apreciable es que en este caso debemos de crear un layout a medida que será el utilizado por el diálogo.

1.- Tenemos que diseñar un layout personalizado para el diálogo.
2.- Debemos definir un identificador de diálogo único para este diálogo.
3.- Debemos actualizar el método onCreateDialog() de forma que construya y retorne el objeto de nuestro diálogo personalizado.
4.- Debemos lanzar el diálogo con el método showDialog().

Además nuestro diálogo necesitará nuevos recursos, que añadiremos antes de comenzar a crear el layout, pero primeramente veamos como vamos a diseñar nuestro diálogo:

Diseño del diálogo

Nuestro diálogo necesitará dos campos de entrada del texto (EditText), uno para la introducción de la clave y el segundo para volver a introducirla y verificar que es correcta. Además de ello necesitará los unos TextView que serán los que pongan los títulos a dichas cajas de edición.
Veamos como sería el diseño:




Una vez que tenemos un boceto del diseño vamos a comenzar a añadir los recursos que utilizaremos:

Nombre
Tipo
Valor
str_ClavesIguales
string
Claves iguales.
str_ClavesDistintas
string
Las claves son distintas.\nPor favor, vuelva a escribirlas.
tl_Clave_Reentrar
string
Re-entrar clave:
tl_VerClave
string
Mostrar clave.
color_OK
color
#0F0

Podríamos añadir otras cadenas, pero como las podemos aprovechar de anteriores recursos pues entonces simplificamos.

#1 - Creando el layout
A estas alturas no deberíamos de tener problema para poder crear un nuevo fichero XML en la carpeta dónde están los layouts, al que llamaremos: dialogo_poner_clave.xml.
Añadiremos los siguientes objetos:
Un linearLayout, que será la base de nuestro diálogo:

Propiedad
Valor
Id
@+id/LL_DialogoClave
Orientation
Vertical
Layout height
match_parent
Layout width
match_parent

Después añadiremos un control TextView:

Propiedad
Valor
Id
@+id/tV_Clave
Text
@string/tl_Clave
Text Color
@color/color_Etiqueta_Configuracion
Text Size
@dimen/dim_Etiqueta_Configuracion
Layout height
wrap_content
Layout width
fill_parent

Debajo de este TextView creamos un EditText:

Propiedad
Valor
Id
@+id/eT_Clave1
Input type
textPassword
Text Color
@color/color_EditText_Configuracion
Max Lines
1
Text Size
@dimen/dim_EditText_Configuracion
Layout height
wrap_content
Layout width
fill_parent

Una vez realizada esta pareja de TextView y EditText, podemos copiar ambos y pegarlos debajo, y les asignaremos los siguientes valores:
Al control TextView:

Propiedad
Valor
Id
@+id/tV_Clave_Reentrar
Text
@string/tl_Clave_Reentrar
Text Color
@color/color_Etiqueta_Configuracion
Text Size
@dimen/dim_Etiqueta_Configuracion
Layout height
wrap_content
Layout width
fill_parent

Y al control EditText:

Propiedad
Valor
Id
@+id/eT_Clave2
Input type
textPassword
Text Color
@color/color_EditText_Configuracion
Max Lines
1
Text Size
@dimen/dim_EditText_Configuracion
Layout height
wrap_content
Layout width
fill_parent

Después debemos añadir un último TextView, que será el que muestre al usuario si las dos claves que ha introducido coinciden o no:

Propiedad
Valor
Id
@+id/tV_EstadoClave
Gravity
center_horizontal
Text
@string/str_ClavesDistintas
Text Color
@color/color_Error
Text Size
@dimen/dim_Etiqueta_Configuracion
Layout height
wrap_content
Layout width
wrap_content
Layout gravity
center_horizontal

Una vez realizado esto ya podemos comenzar a escribir el código para nuestro diálogo.

Creando el código

#2 – Añadir el identificador del diálogo

Procedemos a añadir a nuestra clase ActividadConfiguración, el siguiente identificador:
/**Identificador para el diálogo de la clave */
static final int ID_DIALOGO_CLAVE = 1;

Además, aprovechamos para crear una variable miembro, que será la que almacene el valor de la clave para almacenarla posteriormente en el método onPause().
//Clave
String mstrClave;



#3 – Actualizar el método onCreateDialog

Debemos modificar el método onCreateDialog para que controlar si es nuestro identificador de diálogo y actuar en consecuencia. Esto lo hacemos añadiendo otra sentencia case:
case ID_DIALOGO_CLAVE:
{//Codigo aqui}

Y ahora vamos a ver como crear un diálogo personalizado utilizando la clase AlertDialog.Builder:
//#creamos un inflater para rellenar nuestro layout
//Vale cualquiera de las dos maneras:
//LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

LayoutInflater inflater = (LayoutInflater) getLayoutInflater();

//Ahora rellenamos un objeto View con nuestro custom layout
final View view_dialogoClave = inflater.inflate(
R.layout.dialogo_poner_clave, (ViewGroup) findViewById(R.id.LL_DialogoClave));

LayoutInflater es una clase que se utiliza para instanciar el contenido XML a sus correspondientes objetos view. Nunca se usa directamente, sino que se utilizan los métodos getLayoutInflater() o getSystemService(string) con los cuales se obtiene una instancia estandar que ya está enganchada a nuestro contexto actual y configurada actualmente para nuestro dispositivo.
Después se utiliza el método inflate() para llenar la vista con los datos de nuestro fichero XML.

A partir de aquí ya podemos crear las referencias para acceder a los controles EditText, accediendo a ellos a través de la View que acabamos de crear:

//Creamos referencias para acceder a nuestras EditText:
final EditText clave1 = (EditText) view_dialogoClave
.findViewById(R.id.eT_Clave1);
final EditText clave2 = (EditText) view_dialogoClave
.findViewById(R.id.eT_Clave2);
final TextView mensajeEstadoClave = (TextView) view_dialogoClave.findViewById(R.id.tV_EstadoClave);

Ya podemos comenzar a crear oyentes (listeners) para los eventos de nuestro diálogo, comenzaremos por añadir un TextWatcher, que si se añade a un objeto editable entonces se llamará a sus métodos cada vez que el texto cambie. Aunque podemos crear una clase anónima, en este caso he optado por hacerla interna/anidada, ya que así podemos utilizar este mismo listener para los dos controles EditText, además de que personalmente me gusta más así ;)

//Listener para las EditText de la clave
// Se añade así para poder añadir este listener a las dos EditText
class clave_CambioTextoListener implements TextWatcher
{
@Override
public void afterTextChanged(Editable s)
{
// TODO Auto-generated method stub
//En este método debemos comprobar si las claves son iguales:
String strClave1 = clave1.getText().toString();
String strClave2 = clave2.getText().toString();

//Ahora comprobamos si las claves son correctas o no:
if (strClave1.equals(strClave2))
{
//Si las claves son iguales:
mensajeEstadoClave.setText(R.string.str_ClavesIguales);
mensajeEstadoClave.setTextColor(res.
getColor(R.color.color_OK));
}
else
{
//Si las claves son distintas:
mensajeEstadoClave
.setText(R.string.str_ClavesDistintas);
mensajeEstadoClave.setTextColor(res
.getColor(R.color.color_Error));
}
}
@Override
public void beforeTextChanged(CharSequence s, int start,
int count, int after) {
// TODO Auto-generated method stub
}
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count)
{
// TODO Auto-generated method stub
}
}

En este listener lo que hacemos es verificar si el texto introducido es igual en ambas EditText, y actualizar el mensaje acorde con ello.
Ahora enlazamos este listener a los controles:

/** Creamos el listener para cuando cambie el texto en
* el EditText de clave2.
*/
clave1.addTextChangedListener(new clave_CambioTextoListener());
clave2.addTextChangedListener(new clave_CambioTextoListener());

Ahora cada vez que cambie el texto de cualquiera de estos controles se llamará a nuestro listener.

Creando el AlertDialog

Una vez que tenemos los oyentes (listeners) preparados, ya podemos crear nuestro diálogo:

//Ahora ya podemos unir nuestra View a un AlertDialog.builder
AlertDialog.Builder constructor = new AlertDialog.Builder(this);
constructor.setView(view_dialogoClave);
//Titulo de nuestro diálogo:
constructor.setTitle(R.string.tl_PonerClave);

Después de construir el constructor, al utilizar el método setView(), lo que hacemos es hacer que el contenido de nuestro diálogo sea lo que hay en dicha View. Después con setTitle() lo que hacemos es poner un título a nuestro diálogo.

Añadiendo el código para los botones
Nuestra vista no tiene botones, y para que los tenga debemos de crearlos, junto con su respectivo listener para realizar las acciones pertinentes en caso de pulsarlos.
Vamos a añadir un botón “negativo”, y con él creamos anónimamente su oyente:

constructor.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog,
int which)
{
//En caso de pulsar el botón cancelar cerramos el diálogo ActividadConfiguracion.this.removeDialog(ID_DIALOGO_CLAVE);
}
});

Como vemos, en caso de pulsar este botón quitamos el diálogo y lo destruimos, para que la próxima vez que vuelva a aparecer se tenga que reconstruir.

Ahora veremos el botón “positivo”:

constructor.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog,
int which)
{
String strClave1 = clave1.getText().toString();
String strClave2 = clave2.getText().toString();
//Cogemos la clave y la almacenamos en la variable miembro:
if (strClave1.equals(strClave2))
{
//OJO FIXME : Podemos guardar aqui:
mstrClave = strClave1;
TextView TV_Clave = (TextView) findViewById(R.id.tV_EstadoClave);
TV_Clave.setTextColor(res .getColor(R.color.color_EditText_Configuracion));
TV_Clave.setText(res .getString(R.string.str_HayClave));
//Eliminamos el diálogo despues de almacenar la clave.
ActividadConfiguracion.this.removeDialog(ID_DIALOGO_CLAVE);
}
else
{
// NO GUARDAMOS LA CLAVE NI SALIMOS:
// ACTUALIZAMOS el TextView por si no lo pone correctamente
// Si las claves son distintas:
mensajeEstadoClave .setText(R.string.str_ClavesDistintas);
mensajeEstadoClave.setTextColor(res
.getColor(R.color.color_Error));
}
}
});

Creo que el código se explica por si sólo, lo que hacemos es obtener las cadenas de los controles edittext y comprobamos si son iguales, en caso de serlo damos la clave por establecida y almacenamos su valor. En caso contrario pues seguimos sin clave.

A partir de aquí sólo nos falta crear el AlertDialog y retornarlo:

//Creamos el diálogo:
AlertDialog dialogoClave = constructor.create();

//Devolvemos el objeto diálogo:
return dialogoClave;

Realizadas estas tareas, hemos acabado con el código de onCreateDialog(), así que ahora tenemos pendiente modificar el método onPause() para guardar la clave en caso de que exista, mostrar el diálogo cuando pulsemos el botón correspondiente, y inicializar el textview cuando se cargan los datos en inicializaClave().

El método inicializaClave()
El método quedará de la siguiente manera, apenas cambia una línea con respecto al código que ya teníamos escrito:

private void inicializaClave()
{
//#3 Cargamos los datos para la Clave.
TextView TV_Clave = (TextView) findViewById(R.id.tV_EstadoClave);
if (mFicheroConfig.contains(PREFERENCIAS_JUEGO_CLAVE))
{
//Ponemos "Hay clave" en la clave, ya que no vamos
// a mostrar la clave.
//Y Cambiamos el color del texto:
TV_Clave.setTextColor(res
.getColor(R.color.color_EditText_Configuracion));
TV_Clave.setText(res.getString(R.string.str_HayClave));
//Guardamos la clave en la variable miembro:
mstrClave = mFicheroConfig
.getString(PREFERENCIAS_JUEGO_CLAVE, null);
}
else
{
//En caso de que no exista la clave en el fichero:
// Ponemos el string de No hay clave, y el color de error.
TV_Clave.setText(res.getString(R.string.str_NoHayClave));
TV_Clave.setTextColor(res.getColor(R.color.color_Error));
}
}

Mostrar el diálogo

El método onClickBoton_PonerClave() debe cambiarse para mostrar el diálogo, con lo que suprimimos el mensaje toast que teníamos anteriormente y lo sustituimos por una llamada a showDialog():

public void onClickBoton_PonerClave(View vista)
{
showDialog(ID_DIALOGO_CLAVE);
}

Guardar los datos
Volvemos a editar el método onPause() para guardar la clave:
//ATENCIÓN, se ha cambiado lo siguiente:
//Guardamos el valor de la clave En caso de ser distinto de null
// if (!mstrClave.equals(null)) //
if (mstrClave!=null)
{
mEditorFicheroConf.putString(PREFERENCIAS_JUEGO_CLAVE, mstrClave);
}

Creo que no es necesario indicar que la llamada al método putString debe hacerse antes de ejecutar el commit() (o el apply()), de lo contrario no se guardarán los valores.

Y hasta aquí hemos llegado con el capítulo de hoy. Si ejecutamos el programa, veremos que al poner la clave, aunque sean distintas, el diálogo desaparece. Esto ocurre porque hemos creado nuestro diálogo personalizado a partir de la clase AlertDialog y utilizando su Builder(). Para evitar que se oculte la ventana podríamos crear nuestro diálogo personalizado a partir de la clase base: Dialog. Si lo hacemos así debemos de crear todo, en el layout deberíamos de crear los botones, y después crear sus listeners y actuar en consecuencia. En otro capítulo veremos como modificarlo para crearlo de esta manera.



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

1 comentario:

  1. Se debe cambiar dónde pone:
    //Clave
    String mstrClave;

    por

    //Clave
    String mstrClave="";

    Ya que si no se inicializa el objeto no se puede acceder al método .equals() posterior y producirá una excepción.
    Asimismo también debemos cambiar la comprobación del if, ya que ahora nunca va a ser null.

    ResponderEliminar