Android Preferencias Rotación

“Desarrollo de Aplicaciones Móviles en Android”
Ejercicio Intermedio C: Preferencias en Android
A. Descripción
Con este ejercicio, veremos cómo utilizar preferencias en Android. Las preferencias son un recurso muy útil y muy valorado por los usuarios de las aplicaciones. Crearemos una preferencia de texto, la cual mostraremos en la pantalla principal de nuestra aplicación.

B. Implementación
Al igual que muchísimas otras partes de la implementación en Android, en las preferencias también se utilizan archivos XML para facilitar la creación de las mismas.


Para crear nuestra propia pantalla de preferencias, simplemente tenemos que crear una actividad que herede de PreferenceActivity, y desde el método onCreate() de
la Actividad cargaremos las preferencias, que no será más que un fichero XML que contiene la
estructura, claves y objetos de la pantalla de preferencias.
El ejercicio finalmente deberá quedar de la siguiente manera:
C. Pasos a seguir
1. Crear un nuevo proyecto Android en Eclipse.
2. Creamos una nueva clase Preferencias que herede de PreferenceActivity.
3. Creamos un fichero XML de preferencias (normalmente en “res/xml/”), el cual llamaremos settings.xml, y el tipo será “Preference”.
4. Añadiremos al fichero de preferencias una categoría llamada “Ajustes básicos”, con una clave (la que queramos) y una descripción. Dentro de ésta categoría incluiremos
una EditTextPreference, que es la que nos permite insertar alguna cadena de
texto en la preferencia (a la cual le deberemos dar también un título, descripción y clave).
5. Añadimos una opción en el menú para realizar la llamada a las preferencias de nuestro programa. Recordar que las preferencias no serán más que otra Actividad.
6. Mediante un objeto SharedPreferences obtenemos las preferencias del programa de la siguiente forma:
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this);
7. Obtenemos la preferencia de texto mediante su clave. Lo normal es que esa clave sea un string público en la clase de las preferencias.
String texto = p.getString( CLAVE , "Ninguna Preferencia");
8. Mostramos la preferencia en el TextView de la actividad principal.

“Desarrollo de Aplicaciones Móviles en Android”
Ejercicio Intermedio E: Rotación
A.Descripción
Parece que ya llevamos mucho tiempo con esto, pero la verdad es que hay un lado de
los smartphones (en concreto los que llevan Android como sistema operativo) que no hemos descubierto, y ésos son los sensores de movimiento. Estos sensores detectan cómo estamos utilizando nuestro dispositivo en las manos, y son capaces de saber si están boca arriba, boca
abajo, siendo usados de lado (landscape) o de pie (portrait). Por supuesto, hay más
cosas, pero no nos precipitemos.
El objetivo de este ejercicio es primero entender el principio de la rotación. Hasta ahora, hemos estado haciendo aplicaciones destinadas a ser usadas de forma que la pantalla del teléfono sea más alta que ancha (de pie o portrait), que es como hemos utilizado nuestros teléfonos desde siempre. Sin embargo, hay ocasiones en las que querríamos utilizar nuestros teléfonos de lado, de forma que la pantalla sea más ancha que alta. Un ejemplo sería para ver un videoclip, o incluso para poder jugar a un videojuego. Una vez hayamos entendido
el principio de la rotación, nuestro objetivo será adaptarnos a él.
Android soporta la rotación, y es más, la maneja de forma automática. Es decir, sin nosotros hacer ningún cambio en nuestras aplicaciones, éstas ya soportan funcionar en landscape en vez de en portrait. Veámoslo.
B. Implementación


Comenzaremos descargándonos el ejercicio inicial del Subversion. Al abrirlo y
ejecutarlo, veremos que se trata de un layout para un traductor de idiomas. Nada especial,
salvo por el truco que nos permite ahorrarnos varios LinearLayouts en favor de un solo
RelativeLayout con una vista invisible que utilizamos para saber dónde está el centro con respecto a la altura de la pantalla. También nos encontramos con los Spinners, más conocidos como “ComboBox” o selector. Esencialmente, esconden una lista que pasa a primer plano cuando el usuario quiere elegir otro elemento, y son perfectos para casos como éste.
También veréis un TextWatcher unido al EditText del texto fuente en el código, pero
explicaremos su funcionamiento más adelante.
Lo realmente interesante viene ahora, cuando pulsemos las teclas Control + F12. Es
aquí cuando nuestro emulador de Android realiza una rotación, y vemos perfectamente que la
aplicación sigue funcionando, sin tener que cerrarse.
Entonces nos preguntamos, ¿cómo ha resuelto Android este problema? Sencillo. Ha
seguido nuestras instrucciones al pie de la letra: las vistas ocupan toda la anchura, y respetan utilizar la mitad superior de la pantalla para el texto de entrada y la mitad superior para el de salida.


Una vez visto esto, la pregunta sería: ¿hace falta todo un ejercicio de nivel intermedio
para esta tarea? La verdad es que no. En nuestro código inicial no hay ningún concepto nuevo,
es simplemente el layout, los Strings y la Actividad, que no tiene ningún elemento
novedoso. ¿Y entonces? ¿Para qué estamos aquí?
B---1. Interfaces específicas
Hay una gran infinidad de cosas que no podremos dar en este pequeño curso, y en este apartado utilizaremos un concepto que no tendremos tiempo de explicar al completo, pero que al menos os resultará familiar llegado el caso.
En fin, ¿qué es eso de Interfaces específicas? Veréis, tal y como tenemos la aplicación,
la interfaz es perfecta para el caso en el que la pantalla esté de pie, o portrait. Es cómoda e
intuitiva para este caso. Sin embargo cuando giramos el dispositivo, es horrible, porque los cuadros para introducir texto, que son lo más importante, tienen muy poco espacio, y resulta muy incómodo de utilizar. ¿Por qué? Porque esta interfaz ha sido diseñada para una pantalla alta, no ancha; estamos malgastando la gran anchura que tenemos, y lo estamos pagando en el poco espacio que hay para introducir texto. ¿Cómo lo solucionamos? Muy sencillo, haciendo
otro layout.
En la carpeta res guardamos los layouts, en una carpeta con el mismo nombre. La
carpeta layout es la carpeta a la que accede el sistema operativo para cargar la aplicación por defecto, cuando no encuentra nada mejor. Como no hay nada específico para ningún caso, siempre escoge el mismo layout. Cambiemos eso.
Lo primero que debemos hacer es crear una nueva carpeta dentro de res, que
llamaremos “layout-land”. Esta carpeta tiene un nombre pre---definido, y es la carpeta a la
que irá Android cuando tenga que cargar un layout en modo landscape. Dentro de esta carpeta copiaremos el fichero main.xml que ya tenemos de la carpeta layout, y ejecutamos la aplicación. ¿No se aprecia ningún cambio, verdad? Eso es porque el layout
específico para landscape es exactamente igual que el de portrait. Arreglémoslo.

Como en landscape tenemos mucha anchura, lo ideal sería, en vez de dividir la pantalla en dos por altura, hacerlo por anchura. Iremos vista a vista realizando los cambios pertinentes. Empezando por
screenCenter,, debemos cambiar android:layout_centerVertical por  android:layout_centerHorizontal.
El siguiente es el primer Spinner, al que debemos añadirle
android:layout_toLeftOf=@id/screenCenter”.      
En el primer EditText debemos quitarle la sentencia android:layout_above y añadirle la misma que añadimos al Spinner, diciéndole que debe situarse a la izquierda de
screenCenter.

A continuación nos quedan el segundo Spinner y el segundo EditText. Al
Spinner debemos decirle primero que se sitúe en la parte superior de la pantalla, luego debemos añadirle

android:layout_alignParentTop=truequitarle android:layout_below                    añadirle android:layout_toRightOf=@id/screenCenter”
para que se sitúe a la derecha. De esta forma, el Spinner se colocará en la parte superior y se quedará a la derecha de nuestro marcador. Por último ya el EditText, al que le debemos quitar la
sentencia android:layout_above y añadirle  android:layout_toRightOf,  
, como
al segundo Spinner. Tras estos cambios, el resultado será el siguiente:
B---2. La Rotación no es perfecta
Ahora nos toca andar con un poquito más de ojo. Y es que, aunque parece que todo va bien, no es así. Revisando el código de initConfig(), y de la Actividad en general, encontramos la variable x. Es un String que, ayudándose de un TextWatcher que hemos asociado con el EditText que recoge el texto fuente, se actualiza con el contenido de éste
último. ¿Por qué? La respuesta la encontraremos al arrancar la aplicación, y al ver el LogCat
detrás. Si empezamos a escribir en el primer EditText, veremos en el LogCat que el valor de la variable x se está actualizando constantemente. ¿Perfecto, no? El TextWatcher es, sin duda, una herramienta muy útil, pero no debemos ser muy agresivos con ella. Cada vez que se
produce algún cambio en el EditText, se llaman a los tres métodos de todos los TextWatcher que tenga asociado el EditText (no es un listener, es un watcher, y podemos ponerle muchos) Si estos métodos no son rápidos, el usuario se dará cuenta al
escribir muchos caracteres juntos, ya que el retraso se irá acumulando hasta que la aplicación
obligue al usuario a parar de escribir.
Siguiendo ahora con nuestro ejercicio, rotemos el emulador. ¿Qué ocurre? Nada, aparentemente; el contenido del EditText se ha guardado, ¿no? Veamos el Log. ¿qué vemos? ¿hay algo extraño? Sí, Los métodos onCreate() e initConfig() han vuelto a ejecutarse, y el valor de x se perdió y volvió a recuperarse automáticamente. ¿Qué ha pasado?
Vayamos por partes. Para empezar, cuando un dispositivo Android rota, lo que ocurre es que la Actividad se destruye y vuelve a crearse; por eso se llaman a los métodos onCreate() e initConfig(); porque es como si acabáramos de arrancar la aplicación.
¿Y qué pasa con la variable x? Como toda la Actividad es destruida, x se pierde, pero se
recupera porque las vistas están preparadas para guardar su contenido de forma automática. Cuando la vista recupera su contenido (lo cambia), se ejecuta el watcher que actualiza a la variable x. Sin embargo, esto no siempre funciona como deseamos, y depende de la situación.
La norma es considerar que las vistas no guardan información, y que debemos hacerlo
nosotros mismos. ¿Cómo lo vamos a hacer? Lo primero es desquitarnos del TextWatcher,
ya que no nos hace falta en este caso; sólo queremos guardar en x el texto que hay en el EditText cuando éste vaya a rotar, porque mientras el usuario utiliza la aplicación no hay peligro, ¿no? Lo siguiente es, efectivamente, buscar la forma de guardar el contenido del
EditText en x justo antes de que se pierda y, conseguir recuperarlo cuando se reconstruya
la Actividad.
Para ello, lo primero que haremos es comentar todo el código del watcher, e impedir que sea añadido a nuestro EditText de texto fuente. Ahora mismo, la variable x no se actualizaría al escribir, pero esto no es un problema. Lo que nos hace falta para solucionar este problema es acudir al método onSaveInstanceState() de la Actividad. Este método se ejecuta cuando la Actividad pase a estar en estado detenida (onStop()), y se diferencia de la llamada anterior en que se nos pasa un objeto de tipo Bundle. Un Bundle es una clase que contiene información, y es utilizada por el sistema operativo para guardar el estado de la aplicación. ¿Qué queremos decir con estado? Muy sencillo. Supongamos que estamos realizando algo importante en una aplicación, y de repente recibimos una llamada de teléfono.
En ese momento, la aplicación desaparecerá y se quedará en memoria, esperando. Al terminar la llamada, podemos volver a la aplicación y todo estará allí. Sin embargo, esto ocurre suponiendo que el sistema operativo no ha necesitado eliminar nuestra aplicación de la
memoria por falta de ésta; si lo hubiera tenido que hacer, los datos se habrían perdido, a menos que hayamos guardado los datos de usuario en el método onPause(). Al volver, la
aplicación estaría como nueva, como recién arrancada, porque los datos se habrían guardado
en el onPause().
Y entonces, ¿para qué sirve el onSaveInstanceState() y su bundle? Para que el sistema operativo nos lo dé, y recuperemos el estado de la aplicación aunque nos haya borrado de la memoria, permitiéndonos actuar como si la aplicación nunca se hubiese cerrado,
cosa que seguro les gustará a nuestros usuarios. Éste es el código a añadir en la Actividad, bajo
el initConfig():

public void onSaveInstanceState(Bundle savedInstanceState) {
x = sourceText.getText().toString();
savedInstanceState.putString("x", x);

}

Probemos ahora. ¿Qué ocurre? ¿Por qué no se guarda el contenido del EditText
como antes?
Al sobre---escribir el método onSaveInstanceState() le hemos dicho a Android
que la responsabilidad de guardar el estado de la aplicación es nuestra, cosa que será
necesaria cuando tengamos que guardar información sobre el estado de la Actividad, como pueden ser instancias (variables) de clases Java. Esto significa que las vistas ya no intentan guardar sus datos y recuperarlos automáticamente; ahora tenemos que hacerlo nosotros.

Para finalizar con el ejercicio, y dejarlo tal y como estaba antes, necesitamos modificar un par de líneas dentro de initConfig(). Justo antes de inicializar x, debemos ejecutar un
if. Si la variable savedInstanceState no es null, debemos inicializar x recuperando su
valor del Bundle savedInstanceState con el método getString(), pasándole
“x” como argumento y asignar el texto del EditText sourceText a x. Si
savedInstanceState es null, inicializamos x normalmente.

¿Qué significa el argumento de tipo String que estamos utilizando? Dado que podemos guardar muchísimas cosas en el bundle, incluso del mismo tipo, necesitamos identificar cada una. Para hacerlo sencillo, hemos utilizado como identificador o llave (key) el string “x”. Además de Strings, podemos guardar todos los tipos básicos como ints, booleans, bytes, floats, doubles, etc. Pero aún más importante es el hecho de que podemos guardar clases nuestras, clases Java, si éstas implementan la interfaz Serializable. Debido a que esto último se escapa de los contenidos de nuestro curso, no
podemos explicarlo en detalle, pero animamos a los alumnos a investigarlo.
C. Conclusión
La rotación es un aspecto esencial de cualquier aplicación Android, y puede volverse más complejo de lo que pueda parecer, hasta el punto de que hay aplicaciones que se distribuyen sin permitir que el usuario rote la pantalla, quedándose siempre en la misma
posición, para así evitar tener que buscar una forma rápida e indolora que resuelva el
problema.
D. Opcionales
Como siempre, dejamos algunas cosas para que las hagáis e investiguéis vosotros por
vuestra cuenta:
--- Existe un atributo que se le puede dar a una Actividad desde el manifest, y que permite bloquear una Actividad para que se quede fija en una posición, o portrait o landscape. Investigarlo y probarlo.
--- Guardar también el contenido de los dos Spinners y del texto destino en el bundle,
y restaurarlos correctamente





Android Multiples actividades con finalizacion

Desarrollo de Aplicaciones Móviles en Android” Ejercicio Intermedio B: Múltiples actividades con finalización.

A. Descripción
El ejercicio se realizará sobre el código del ejercicio 2 resuelto. Ahora se le añadirán dos nuevos botones en la segunda actividad, uno que cierre esa segunda actividad y otro que cierre esa actividad, y la anterior (mediante algún “mensaje” a la primera).


B. Implementación
El código de los objetos Button como vimos en anteriores ejercicios, se declara de la siguiente manera:

Button boton = (Button) findViewById (R.id.Button);
boton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
// código de la función

}
});

Para cerrar una actividad se utiliza el método finish().
Una subactividad es una actividad que devuelve un resultado. Cuando termina una subactividad se llama al manejador onActivityResult de la actividad padre desde la que
fue arrancada. Además, cualquier actividad registrada en el manifiest puede ser invocada
como subactividad. Se arranca una subactividad con startActivityForResult, pasando como parámetro el intent y un código de petición que se utilizará a la vuelta para identificar
qué subactividad es la que ha terminado. Un ejemplo:

private static final int SHOW_SUBACTIVITY = 1;
Intent intent = new Intent(this, MyOtherActivity.class);
startActivityForResult(intent, SHOW_SUBACTIVITY);

Para que una actividad devuelva un resultado, en la subactividad se llama al método setResult() antes de la llamada a finish(). El método setResult() acepta dos parámetros: un código de resultado: Activity.RESULT_OK, Activity.RESULT_CANCELED o cualquier entero; y un Intent con datos devueltos: URI
a los contenidos devueltos (un contacto, número de teléfono, fichero media,...) y una colección de Extras con información adicional.

Intent result = new Intent();
result.putExtra(SUMA, suma); setResult(RESULT_OK, result); finish();

Para la recuperación de los resultados en la actividad padre se redefine el manejador
onActivityResult, que recibe los siguientes parámetros:
Código de petición: el código que se utilizó para arrancar la subactividad.
Código de resultado: el código que devuelve la subactividad. Si se aborta anormalmente la subactividad devuelve Activity.RESULT_CANCELED
Datos: Intent con los resultados devueltos por la subactividad.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SUMA_REQUEST) {
if (resultCode == RESULT_OK) {
int suma = data.getIntExtra(SUMA, 0);
}
}

}


Los “layouts” que contienen el diseño deberán quedar de la siguiente manera para éste ejercicio:


C. Pasos a seguir

1. Cargar el código del proyecto Ejercicio Intermedio B – Multiples actividades con finalizacion -> Inicio en Eclipse.
2. Modificar el fichero res/layout/actividad2.xml para que sea idéntico al explicado más arriba, insertando dos objetos Button. NOTA:En los Button modificaremos el valor de la propiedad
Layout width  estableciéndola a “match_parent” (indica que utilizará todo el ancho de la pantalla).
3. Crear el listener de la función onClick, para el primero objeto Button (“Cierra ésta actividad”) de la segunda actividad (Utilizar la función “findViewById ()” para obtener el Button al que nos queremos referir), el que finaliza dicha actividad. NOTA: Método finish().
4. Crear el listener de la función onClick, para el segundo objeto Button (“Cierra ésta y la anterior actividad”) de la segunda actividad (Utilizar la función “findViewById
()” para obtener el Button al que nos queremos referir), añadiéndole un boolean
que deberá devolver a la actividad “padre”, tal que “true” indique que deba finalizar
dicha actividad padre.
5. Sobreescribir el método onActivityResult de la actividad principal para que si el request_code es el de la actividad2, y el result_code es RESULT_OK, obtenga el valor boolean que devolvió la actividad2 y si éste valor es “true”, finalice
la actividad

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == SUB_ACTIVIDAD) {
if (resultCode == RESULT_OK) {

}
}
}

6. Cambiar la llamada startActivity(), por startActivityForResult()
para que la actividad principal “espere” un resultado.
Ejercicio Intermedio A: Múltiples Actividades
El ejercicio consistirá en la creación de dos actividades. La primera actividad contendrá un campo de texto y un botón. Al pulsar sobre este botón, el control pasa a la segunda actividad mostrando el texto que se insertó en el cuadro de texto de la primera actividad.
A. Descripción
Se trata de familiarizar al alumno con los cambios de actividades, añadir funciones a los objetos y el paso de datos entre actividades mediante Bundle, entre otros.
Para añadir una función a un objeto Button, debemos establecer un “listener” en la función onClick() de Button, y se realiza mediante la función setOnClickListener(), pasándole un objeto que implemente OnClickListener, y que tenga el método onClick. Un ejemplo:

Button boton = (Button) findViewById (R.id.Button);
boton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
// código de la función
        } 

});

Cuando queremos realizar una llamada a una actividad, utilizamos los Intents. “Un
intent es la descripción abstracta de una operación que se va a llevar a cabo”, o dicho de otro modo, un Intent es una clase que permite especificar una Activity a ejecutar, llamando a uno de los métodos de la clase Activity con ese Intent de parámetro. Así si queremos realizar la llamada a otra actividad haremos algo como lo siguiente:

Intent i = new Intent (contexto, NuevaActividad.class);
startActivity(i);

NOTA: La variable contexto, se refiere al contexto en el que está siendo utilizado.
La clase Bundle contiene tipos primitivos y objetos de otras clases, y se utiliza para pasar datos entre diferentes actividades. Para que una actividad pase datos, utilizamos la función putExtra(…). Un ejemplo:

Intent myIntent = new Intent (contexto, NuevaActividad.class);
myIntent.putExtra("com.feu.ull.cursoandroid.Value", "Hola, Juan!");
// Par clave/valor startActivity(myIntent);

Y en la otra actividad, recibimos el valor así:

Bundle bundle = getIntent().getExtras();
if (bundle != null){
data = bundle.getString("com.feu.ull.cursoandroid.Value");
}


// data contend ahora el valor Hola, Juan!”





B. Implementación
El código que se ofrece al usuario tiene 2 actividades creadas (Actividad1 y Actividad2). Ambas actividades estarán vacías, con sólo el código que viene a continuación:

public class Actividad1 extends Activity {
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.actividad1);

}
}

public class Actividad2 extends Activity {
/** Called when the activity is first created. */

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.actividad2);

}
}

Los layouts que tienen el “diseño” de las actividades (R.layout.actividad1         y R.layout.actividad2) estarán vacíos, conteniendo sólo el LinearLayout donde el alumno tendrá que insertar los diferentes TextView, EditText y Button donde corresponda.
Se pretende la creación de 2 actividades diferentes, cuya “vista” será la siguiente:



La primera actividad permite la inserción de un texto en el EditText, y al pulsar sobre el botón “Otra Actividad” y aparezca la segunda actividad con un TextView conteniendo el texto insertado en el EditText de la primera actividad.
C. Pasos a seguir
1. Cargar el código del proyecto Ejercicio Intermedio A – Multiples actividades -> Inicial
en Eclipse.
2. Modificar el fichero res/layout/actividad1.xml para que sea idéntico al explicado más arriba, insertando un EditText y un Button. NOTA: Debemos modificar algunas propiedades de los mismos. En el Button cambiar la propiedad Text y asignarle el texto “Otra actividad”, mientras que en el
EditText le asignaremos esta variable a vacío. En ambos también modificaremos el valor de la propiedad Layout
width estableciéndola a “match_parent” (antiguo “fill_parent”) (indica que utilizará todo el ancho de la pantalla).
3. Modificar el fichero res/layout/actividad2.xml para que sea idéntico al explicado más arriba, insertando un TextView.

NOTA: Deberemos modificar la propiedad Text y asignarle a la misma “No me llegan datos”.
4. Crear listener de la función onClick, para el objeto Button de la primera actividad.
Utilizar la función “findViewById()“ para obtener el Button al que nos queremos referir.
5. Obtener dentro del listener anterior el texto que se encuentra en el EditText.
NOTA: Mirar las funciones getText() y toString().
6. Pasar el String obtenido del paso anterior a la siguiente actividad, y realizar la
llamada a la misma. NOTA: Debemos incluir en el A ndr oid.mani fest que existe la actividad Actividad2.

Intent i = new Intent (getApplicationContext(), Actividad2.class);
i.putExtra("clave", texto);

startActivity(i);

7. Obtener en la segunda actividad el texto pasado desde la primera.
NOTA: bundle.getString (clave)
8. Establecer el texto de TextView al obtenido anteriormente. NOTA: setText()