Kitabı oku: «El gran libro de programación avanzada con Android», sayfa 2
2.5. Apariciones y desapariciones
Para conseguir que un texto aparezca haciéndose más visible o nítido, animaremos la propiedad alpha, que controla la transparencia o, más correctamente, la ausencia de transparencia. Es decir, la opacidad. Un valor igual a cero indica transparencia total, o invisibilidad. Un valor igual a uno es opacidad total. En la siguiente actividad hacemos aparecer y desaparecer un texto animando la transparencia. En primer lugar, creamos un fichero xml para la animación.
Usamos el mismo layout de los ejemplos anteriores y modificamos ligeramente el programa para obtener la actividad que se detalla a continuación. En la figura 2.5. se muestra el resultado de la animación.
Figura 2.5. Animación haciendo aparecer un texto.
2.6. Series de animaciones
Es posible encadenar varias animaciones en un AnimationSet. Para ello, utilizamos la etiqueta set en un fichero xml. En el siguiente ejemplo encadenamos una aparición, una dilatación, una rotación y una traslación. Por defecto, todas las animaciones comienzan simultáneamente, pero se puede indicar el momento en que debe comenzar cada una mediante la etiqueta startOffset. En este caso, cada animación comienza tres segundos después de la que la precede.
De nuevo, el fichero de la actividad es similar a los anteriores, igual que el layout. En la figura 2.6. se muestra el resultado.
Figura 2.6. Serie de animaciones.
2.7. Animaciones con Java
Las animaciones interpoladas se pueden programar enteramente en Java sin utilizar ficheros de animación xml. Una animación es un objeto de una de las clases AlphaAnimation, ScaleAnimation, RotateAnimation o TraslateAnimation. En el constructor se indican todas las propiedades que hemos visto en xml. Una serie de animaciones es un objeto de la clase AnimationSet. Cada animación individual se añade a la serie mediante serie.addAnimation(animacion). Hay que recordar que la repetición de animaciones con setRepeatMode() no funciona con series.
En el siguiente ejemplo se realiza en Java la misma serie de animaciones de la sección anterior, usando el mismo layout. En la figura 2.7. se muestra el resultado.
Nótese que la posición del centro de escalado, indicada en xml con pivotx=“50%”, se especifica en Java mediante estos dos parámetros:
El primero es una constante que indica que la coordenada es relativa al objeto que se está animando. Dicha coordenada se especifica mediante el siguiente parámetro, que toma un valor entre 0 y 1.
Figura 2.7. Serie de animaciones programadas con Java.
2.8. AnimationListener
La interfaz AnimationListener permite implementar acciones que se «disparan» cada vez que una animación se repite, o cuando finaliza o se inicia. Esto permite modificar el contenido del objeto View durante la animación o enlazar varias animaciones. Para ello, hay que definir la animación como un «oyente» mediante
La clase que implementa AnimationListener debe definir los métodos onAnimationRepeat, onAnimationEnd, onAnimationStart.
En el siguiente ejemplo, una primera animación hace aparecer un contador 10 veces. Nuestra actividad implementa AnimationListener incrementando el contador cada vez que la animación se repite y, al finalizar esta, ejecuta una segunda animación de escala escribiendo un mensaje final. Usamos el mismo layout de los ejemplos anteriores. En la figura 2.8. se muestra el resultado.
Figura 2.8. Animación usando la interfaz AnimationListener.
2.9. Animación de un layout
Una misma animación puede aplicarse a todo un layout. En este caso, se animarán todos los objetos View del layout en secuencia, cada uno con un retraso. Partiremos de la siguiente animación, una traslación que hace aparecer un elemento por la derecha.
A continuación, definimos la animación del layout mediante layoutAnimation en el siguiente fichero xml. En este caso, especificamos el retraso o delay en la animación de cada elemento, expresado como una fracción de la duración de la animación, y el orden o animationOrder en el que se aplicarán las sucesivas animaciones, que puede ser normal, reverse o random. Finalmente, el fichero de la animación se especifica como el recurso anim/animacion.
Figura 2.9. Animación de un layout mediante LayoutAnimation.
Finalmente, aplicamos la animación al layout en el fichero main.xml mediante la etiqueta android:layoutAnimation. El fichero Java de nuestra aplicación layoutAnimationActivity no necesita modificarse con respecto al creado por defecto. En la figura 2.9. se muestra el resultado de la animación.
2.10. Animación de un layout en Java
La animación del layout anterior se ha realizado en su totalidad mediante recursos de animaciones almacenados en ficheros xml. La misma animación se puede hacer usando solo código Java. Para ello, hay que seguir los siguientes pasos:
1 Definir la animación que debe aplicarse a cada View, como hicimos anteriormente.
2 Construir un controlador de animaciones, que es un objeto de la clase LayoutAnimationController, pasándole la animación como parámetro.
3 Ejecutar el método setLayoutAnimation del layout que queremos animar, pasándole el controlador anterior como parámetro.
Todo ello se ilustra en la siguiente actividad, que da el mismo resultado que el ejemplo anterior (figura 2.9.).
El layout que se ha utilizado en este ejemplo es el mismo que el del anterior, eliminando la etiqueta android:layoutAnimation y definiendo la ID del layout.
2.11. LayoutAnimationListener
Las animaciones de un layout también admiten la interfaz AnimationListener. Para ello, se invoca el método setLayoutAnimationListener. En el siguiente ejemplo se modifica la actividad anterior para cambiar el TextView final y que experimente una rotación después de la animación del layout. El fichero main.xml no se modifica. En la figura 2.11. se muestra el resultado.
Figura 2.11. Animación de un layout mediante LayoutAnimationListener.
3. ANIMACIÓN DE FOTOGRAMAS
3.1. Animación usando recursos
Una animación de fotogramas consiste en una secuencia de imágenes, cada una de las cuales se muestra en pantalla durante un tiempo determinado. La información de los fotogramas y su duración se puede incluir en un fichero xml, que colocaremos en uno de los directorios res/drawable. En el siguiente ejemplo animaremos una secuencia de seis imágenes jpg, que irán cambiando en intervalos de tres segundos. Las imágenes las copiaremos en el directorio res/drawable-mdpi. En el mismo directorio crearemos el siguiente recurso, un fichero xml con la lista de fotogramas de la animación.
Usaremos el siguiente layout, que consiste en dos botones para activar y detener la animación y un ImageView para contener las imágenes.
Finalmente, en la actividad AnimacionDeFotogramas.java declaramos la animación como un objeto de tipo AnimationDrawable. La animación comienza y se detiene ejecutando sus métodos start y stop. Para definir la animación, primero asignamos el recurso fotogramas_animados.xml al ImageView mediante setBackgroundResource y luego extraemos la animación ejecutando el método getBackground. En la figura 3.1. se muestra el resultado.
Figura 3.1. Animación de fotogramas.
Hay que advertir que el método start de AnimationDrawable no se puede ejecutar directamente en onStart para comenzar la animación automáticamente, porque la aplicación no funcionaría. También hay que controlar el tamaño de las imágenes que se usan como fotogramas. Si son muy grandes o abundantes, se puede producir un error de memoria. Por lo tanto, se debe reducir el tamaño y el número de los fotogramas tanto como sea posible.
3.2. Animación de fotogramas en Java: AnimationDrawable
En el ejemplo anterior hemos animado una serie de fotogramas. La animación se definió como un recurso, en el fichero fotogramas_animados.xml, que se asignó al background de un ImageView mediante setBackgroundResource.
La misma animación la realizaremos ahora en Java sin utilizar el recurso anterior. En Java, una animación de fotogramas es un objeto de la clase AnimationDrawable. Los fotogramas se añaden a esta animación con el método addFrame, que admite dos argumentos: la imagen a dibujar (como un objeto Drawable) y la duración del fotograma en milisegundos. El método setOneShot(boolean) controla si la animación se va a mostrar solo una vez (true) o se va a repetir (false). Para asociar la animación al background, usamos el método setBackgroundDrawable de ImageView. Este sería el programa Java de la animación, usando el layout del ejemplo anterior. El resultado es el mismo que se muestra en la figura 3.1.
3.3. Ajuste de la relación de aspecto
En los ejemplos anteriores hemos asignado una animación de fotogramas al background de un ImageView. El problema que surge en estas animaciones es que los parámetros del ImageView en el layout eran fill_parent. Esto provoca que el tamaño de las imágenes se reduzca para llenar completamente el espacio disponible, sin que se mantenga la relación altura/anchura. Si intentamos modificar el tamaño cambiando los parámetros a wrap_content en el layout, el resultado es que no se muestra nada; es decir, no se ven las imágenes. Esto se debe a que la animación es un objeto de tipo Drawable cuyas dimensiones intrínsecas son cero. No obstante, esta limitación de la clase AnimationDrawable puede solventarse parcialmente, simulando aproximadamente el efecto de wrap_content.
En primer lugar, creamos una actividad AnimacionFotoAspecto.java con el siguiente layout:
La actividad comienza creando la misma animación de fotogramas del ejemplo anterior, pero en lugar de asignarla al background, la asignaremos directamente al ImageView mediante
Esto todavía no resuelve el problema. Si intentamos iniciar la animación, veremos que la imagen no mantiene su relación de aspecto, como se observa en la figura 3.3.1. La razón es que los parámetros del ImageView son fill_parent (recordemos que no podemos usar wrap_content porque no se muestra nada). Un modo de forzar a modificar el tamaño de la imagen sería añadir márgenes mediante setPadding. Para ello tenemos que calcular el tamaño vertical que ocupa la imagen y, a partir de ahí, calcular la anchura que debería tener manteniendo su relación de aspecto.
Figura 3.3.1. Animación de fotogramas que no mantiene la relación de aspecto.
En la siguiente actividad se calcula la relación de aspecto real del primer fotograma. Seguidamente, calculamos la altura que tiene el ImageView en la pantalla mediante getMeasuredHeight. En este punto nos encontramos otro problema, ya que si escribimos estos valores en la pantalla, veremos que el ImageView tiene dimensiones nulas, como se muestra en la figura 3.3.2. La razón es que estas dimensiones se están calculando dentro del método onCreate de la actividad cuando el layout de la actividad no se ha desplegado completamente. Los contenidos solo están definidos cuando la actividad está lista para interaccionar con el usuario; por ejemplo, al pulsar uno de los botones. Por lo tanto, la solución es realizar nuestras manipulaciones dentro del método onClick. El programa Java es el siguiente:
Al calcular dentro de onClick las dimensiones del ImageView, vemos que ya no son cero, por lo que podemos calcular el margen necesario en la imagen para mantener la relación de aspecto (padding). El resultado se muestra en la figura 3.3.2. (izquierda). Hemos escrito las dimensiones del ImageView antes y después de onClick, cuando ya se ha desplegado el layout, así como la nueva anchura de la imagen y el valor necesario del padding. Es ilustrativo pulsar los botones Detener y Comenzar de nuevo, porque entonces se vuelve a calcular las dimensiones del ImageView, que se han modificado respecto al paso anterior al escribir dos líneas de texto. Esto hace recalcular la anchura de la imagen y reajustar el padding, como se observa en la figura 3.3.2. (derecha). Dicha operación puede repetirse varias veces y se verá que la relación de aspecto se mantiene en todos los pasos, hasta que la imagen desaparezca de la pantalla por abajo.
Figura 3.3.2. Animación de fotogramas que mantiene la relación de aspecto usando padding.
4. PROCESOS EN BACKGROUND
En Android podemos ejecutar simultáneamente partes de un programa como procesos paralelos. Cada uno de estos procesos se denomina thread, o hilo, y se ejecuta en background con una prioridad determinada bajo el control de un objeto de la clase Thread de Java. Una forma de crear un hilo es definir una clase que extienda a la clase Thread. La nueva clase debe sobrescribir el método run(), que se ejecutará cuando se inicie el hilo. Otra forma de crear un hilo es implementando la interfaz Runnable. En este capítulo veremos algunos ejemplos del uso de hilos (en el libro Android se pueden ver ejemplos alternativos).
Inicialmente, solo hay un hilo asociado a una actividad, el hilo principal, que consiste en una serie de instrucciones o sentencias que se ejecutan secuencialmente. Además, este hilo es el que controla los elementos de la interfaz de usuario (UI) que se muestran en pantalla. En este capítulo veremos también cómo se puede modificar la UI desde otro hilo.
4.1. Uso de Timer y RunOnUiThread
La clase Timer del paquete java.util permite programar tareas que se ejecutan transcurrido un tiempo, de forma única o secuencialmente. Un temporizador es un objeto de tipo Timer. Para programarlo se utilizan los métodos schedule, para realizar una única tarea, o scheduleAtFixedRate, para una serie repetitiva de tareas. Para detener el temporizador, se usa el método cancel. Por ejemplo, para programar una tarea repetitiva, escribiríamos lo siguiente:
Aquí, retraso es el tiempo de espera hasta que se ejecute la acción; periodo, el tiempo de repetición y timerTask es un objeto de una subclase de TimerTask, que contiene las instrucciones a ejecutar a través de su método run.
El método run de TimerTask se ejecuta en background en su propio hilo. Por lo tanto, no podremos modificar la UI (la pantalla), pues los objetos de esta, por ejemplo un TextView, solo se pueden cambiar desde el hilo principal. Para sincronizar con la UI y modificar sus objetos, podemos usar el método runOnUiThread(Runnable), de la clase Activity. Su argumento es un objeto de tipo Runnable cuyo método run se ejecutará en sincronía con el hilo principal y permitirá modificar los objetos View que en él residan.
La siguiente actividad ilustra el uso de Timer combinado con RunOnUiThread para mostrar un cronómetro que avanza cada 100 milisegundos. Usaremos el siguiente layout:
La actividad Temporizador.java se detalla a continuación. Contiene dos clases internas: Tarea, subclase de TimerTask, y CambiaTexto, que implementa a la interfaz Runnable, para modificar el TextView. No es necesario usar invalidate para redibujar la pantalla. En la figura 4.1. se muestra el resultado.
Figura 4.1. Cronómetro con Timer y RunOnUiThread.
4.2. Modificación de la UI con post
Alternativamente al método runOnUiThread de la clase Activity, podemos usar el método post(Runnable) de la clase View. El argumento de post debe definir un método run que se ejecutará en el mismo hilo que la UI. Veamos un ejemplo en el que modificamos ligeramente el programa anterior, sustituyendo runOnUiThread por tv.post. También se ha modificado el texto a escribir. No es necesario usar invalidate para actualizar el TextView. En la figura 4.2. se muestra el resultado.
Figura 4.2. Modificación de la UI con View.post.
4.3. Modificación de la UI con Handler.post
Una tercera forma de modificar la interfaz de usuario consiste en usar un controlador, u objeto de la clase Handler. A este objeto le pasaremos un objeto Runnable mediante el método Handler.post(), que lo añade al hilo principal. En el siguiente ejemplo, que es una variación de los dos anteriores, se explica el modo de hacerlo. El resultado se muestra en la figura 4.3. (ver Android para otros ejemplos del uso de la clase Handler pasándole un mensaje).
Figura 4.3. Modificación de la UI con Handler.
4.4. Métodos combinados para modificar la UI
Los tres métodos anteriores, usando RunOnUiThread(), View.post() y Handler.post(), pueden combinarse como en el siguiente ejemplo, donde abrimos tres hilos, cada uno de los cuales varía el color de un texto de forma aleatoria. Para ello modificamos la clase CambiaTexto de los ejemplos anteriores añadiéndole dos parámetros: el texto a modificar y un número que se utiliza para cambiar las componentes RGB del color mediante un algoritmo. En la figura 4.4. se muestra el resultado.
El layout, definido en el siguiente fichero main.xml, contiene tres TextView.
A continuación se detalla el fichero Java de la actividad CambiaColor.
Figura 4.4. Tres hilos simultáneos cambiando el color de un texto.
4.5. Modificación de la UI con AsyncTask
La clase AsyncTask permite realizar tareas en background y publicar los resultados en la interfaz de usuario sin necesidad de crear hilos y sincronizarlos con la UI. Para utilizarla hay que definir una subclase, que toma tres parámetros genéricos (ver Apéndice B, secciones B.3 y B.4 sobre el uso de genéricos en Java).
Aquí, X, Y, Z son tres nombres de clases utilizados para los datos del input, progreso y resultado, respectivamente. La clase anterior debe sobrescribir necesariamente el método doInBackground, que admite un número variable de parámetros de tipo X y devuelve un dato de tipo Z (ver Apéndice B, en el que se discuten los métodos de Java con un número variable de parámetros).
El método doInBackground se ejecuta en un nuevo hilo. Desde este hilo se pueden publicar actualizaciones en el hilo principal llamando al método publishProgress(Y … progreso). Entonces, el hilo principal se encarga de invocar el método onProgressUpdate(Y … progreso). Al finalizar la ejecución del proceso en background, el hilo principal invoca el método onPostExecute(Z resultado).
En el siguiente ejemplo se ilustra el uso de AsyncTask, donde un contador se incrementa desde 0 hasta 99 y en cada paso mostramos su valor en un objeto TextView en la pantalla, aumentando proporcionalmente su tamaño. Al final de la ejecución se añade el texto «Fin», como se muestra en la figura 4.5.
Figura 4.5. Uso de AsyncTask para ejecutar un proceso en background y publicar su progreso en el hilo principal.
Ücretsiz ön izlemeyi tamamladınız.