Animación en Android. Clase Elefante

Documentos relacionados
Android Touch. En esta misma clase modificamos también el método avanza() de tal manera que ahora el elefante pueda desplazarse también en el eje y.

Hola Android. Introducción al desarrollo de aplicaciones para Android

Programación Android. Alejandro Alcalde. elbauldelprogramador.com

Unidad Didáctica 2. Elementos básicos del lenguaje Java Tipos, declaraciones, expresiones y asignaciones

Variables. Una variable no es más que un nombre simbólico que identifica una dirección de memoria: vs.

ALMACENAMIENTOS DE DATOS EN ANDROID CON SQLITE

CREAR UN DASHBOARD CON PENTAHO BI-SERVER. Dashboard Pentaho con CDE. Jortilles.com

CODIGO PROYECTO: AppPixelproServicioWeb Proyecto Android - Servicio Web

Introducción a Java LSUB. 30 de enero de 2013 GSYC

UNIDAD 1. Introducción a Photoshop.

Programación Android. Rafael Morón Abad

Carlos Montenegro. Programación Orientada a Objetos Proyecto Curricular de Ingeniería de Sistemas

PROGRAMA JAVA SE (Standard Edition) MODALIDAD ONLINE

Ejercicio 18. Configuración de Widgets en Android. Android Con Java. Ejercicio 18. Configuración de Widgets en Android. Curso de Android con Java

Lección 2: Creando una Aplicación en Java. 1. Estructura del archivo de una clase. 3. Definiendo clases fundamentos

Arquitecturas cliente/servidor

Formato para prácticas de laboratorio

Programación Gráfica II. 8. Manejador de Modelos y Picking Up.

Elementos esenciales de Word

Opciones de animación y configuración

Taller de TeamViewer. Manual De TeamViewer

µmanual de Kdenlive Introducción a la Edición de Vídeo Versión del manual 1.01 Escrito por: José Víctor Torrellas

Manual uso en reuniones DOCUMENTACIÓN SKYPE EMPRESARIAL

Acceso a Datos con Visual Basic

Grabación de audio/vídeo y gráficos avanzados en Android

FACULTAD DE INGENIERÍA

Construcciones del Lenguaje Java

Android Con Java. Ejercicio 2. Ciclo de Vida de las Actividades en Android. Ejercicio 2. Ciclo de Vida de las Actividades en Android

Laboratorio 01: Generación del Hola Mundo Android

PERIODO 3 NOCIONES AVANZADAS DE POWERPOINT

DEMOSTRACION DE UNA APLICACIÓN N-CAPASCON JAVA- POSTGRESQL

APUNTADORES. Un apuntador es un objeto que apunta a otro objeto. Es decir, una variable cuyo valor es la dirección de memoria de otra variable.

Métodos que devuelven valor Dado el siguiente triángulo rectángulo:

INSERCIÓN DE UN REGISTRO CON PHP Y MYSQL

Clases y Objetos en Java. ELO329: Diseño y Programación Orientados a Objetos

Centro Asociado Palma de Mallorca. Antonio Rivero Cuesta

Programación orientada a objetos. Resumen de Temas Unidad 5: Herencia

ALMACENAMIENTO Y RECUPERACIÓN DE UN LIBRO

Índice. Herramientas de desarrollo. Historia Qué es Android? Arquitectura del sistema. Componentes Android Modelos de Negocio

FACULTAD DE ECONOMIA Y ADMINISTRACION DEPARTAMENTO DE CIENCIAS DE LA COMPUTACION CÁTEDRA PROGRAMACION GENERAL. Trabajo Práctico Nº 4

MANUAL DEL USUARIO. Página Web.

2. Instalación / Activación del servicio de Escritorio Remoto.

Manual de OpenOffice Impress

Programación Android. Rafael Morón Abad

Tema 7.- Fundamentos de la Programación Orientada a Objetos

Lo que necesitaremos para programar en Java, será un editor de texto o IDE y la JDK.

Instalar y Configurar Network Load Balancing (NLB) en Windows Server 2008 R2

preferencias, última opción de la barra de herramientas editar, y veremos las herramientas del menú ver.

1. Instalar el componente en el sitio por Extensiones gestor de extensiones.

Práctica 2 Animaciones

INGRESAR DATOS CON UN CONTROL VISUAL EN JAVA. CLASE JTEXTFIELD Y MÉTODO GETTEXT. EJEMPLOS (CU00928C)

Tutorial 6: Qué es una función? y como utilizarla

Android Manejo de Eventos. Rogelio Ferreira Escutia

Test : Conteste exclusivamente en una HOJA DE LECTURA ÓPTICA, no olvidando marcar que su tipo de examen es A.

Administración de Infraestructuras

Por el contrario System.in es un byte Stream sin caracteristicas de character Stream.

GUIA Nº 3 MASCARAS GRAFICOS

Cómo hacer un video en. Movie Maker. Realizado por Silvia Nicosia

La funcionalidad básica es la del proyecto 1 (Pacman III). Sobre ella reemplazamos la interfaz de usuario para adaptarla al nuevo entorno

Manual Power Point Manejo de Hipervínculos

Servicios Avanzados. Índice. 1 Servicios en segundo plano Notificaciones AppWidgets Publicación de software...8

Introducción a Android. [Tema 2]

TUTORIAL CONEXIÓN SQLSERVER CON JAVA DESDE ECLIPSE

Gestión de datos maestros

Threads. La plataforma JAVA soporta programas multhreading a través del lenguaje, de librerías y del sistema de ejecución. Dos.

Conversión entre Tipos

INSTALACIÓN Y VERIFICACIÓN DE LA TARJETA CRIPTOGRÁFICA

MDM Cloud. Enrolamiento de dispositivos IOS. Alestra Información de uso publico

2.4 Luego, en la pestaña superior al lado de la barra del menú, elegir Desarrollador de aplicaciones

Repaso de las características más importantes de la programación Java y su adaptación a Android

INSTALACIÓN Y VERIFICACIÓN DE LA TARJETA CRIPTOGRÁFICA

Manual de Instrucciones para el uso con un ordenador

Ejecuta el modo XP sin virtualización de hardware

GUÍA 10 Tema: Creación de Gif animado.

Elabora en un documento PDF la instalación y configuración de servicios de clusters en Windows 2008 Server.

Ejercicio 4. Manejo de Layouts en Android. Android Con Java. Ejercicio 4. Manejo de Layouts en Android. Curso de Android con Java

Aplicación Android de Asistencia al Caminante

CONSTRUCCION DE TIMER PARA VUELO CIRCULAR CON MOTOR ELECTRICO

3.3 Conceptos Básicos del Lenguaje Java

Almacenamiento en Android

Manual de configuración de Impresoras para POS VIRTUAL Navegador FIREFOX

TUTORIAL KINOVEA. Bogotá, Colombia

COMUNICACIÓN ENTRE EL CLIENTE Y SERVIDOR SIN PHP Y CON PHP. INTÉRPRETE PHP Y GESTOR DE BASES DE DATOS (CU00804B)

OPENOFFICE IMPRESS. Creación básica de presentaciones digitales

Manual de Usuarios SOFTWARE RAZUNA - DAM. Grupo de Innovación y Apropiación de Tecnologías de la Información Archivística CKAN

Manual Evernote. Tutorial creado por Joaquin (MacNauta 2009)

La última versión disponible cuando se redactó este manual era la 5 Beta (versión ), y sobre ella versa este manual.

Sensores. JOSE LUIS BERENGUEL GÓMEZ Marzo 2012

Una actividad de prueba con Edilim

Curso PUDE. Desarrollo de Aplicaciones Móviles en Android

Tema 8: Publicación de Aplicación en Google Play

Compartir discos y particiones

1. COMPARTIR Y MANTENER LIBROS

TP Nº4 Android - SQLite Fecha Miércoles 2013/08/14 Profesor: Pablo Ulman (Polshu).

PRACTICAS DE ANDROID 12 - Lanzar un segundo "Activity" y pasar parámetros Problema:

Universidad Autónoma de Tlaxcala. M.C. José Juan Hernández Mora. Primera Sesión

Manual Power Point Animaciones y Transiciones

Ministerio de Educación. Diseño de Presentaciones en la Enseñanza. Módulo 8: Sonidos

Impresión en formato PDF con

Transcripción:

Animación en Android En este tutorial cubriremos algunos aspectos básicos sobre el manejo de la pantalla, gráficos y animación en Android, para esto construiremos una Actividad que muestre en pantalla la animación de un elefante. Retomaremos las clases Elefante y Animacion que previamente desarrollamos en la sección de Applets ya que nos serán de gran utilidad para este tutorial, sin embargo realizaremos algunas modificaciones para que se adapten a nuestras necesidades en Android. Empezamos por crear un nuevo proyecto de Android para la plataforma 2.2, la Actividad principal la llamaremos TutorialElefante pero esta la retomaremos más adelante. Dentro del mismo paquete donde se encuentra la Actividad principal agregamos dos nuevas clases de Java, una para la clase Elefante y otra para la clase Animacion. Clase Elefante La clase Elefante define el modelo abstracto de un elefante que podemos animar y desplazar en la pantalla. Esta incluye cuatro miembros en la clase para el manejo de la posición en los ejes x y y, la dirección del movimiento y la animación del elefante y dos constantes que nos ayudarán a definir la dirección de movimiento del objeto Elefante. Iniciamos por definir la clase y los miembros y las constantes que la componen. public class Elefante { //Dirección del Elefante, Izquierda public static final int LEFT = - 1; //Dirección del Elefante, Derecha public static final int RIGHT = 1; //Posición del Elefante public int x, y; //Dirección actual del Elefante public int dir; //Animación del Elefante public Animacion animacion; Las constantes LEFT y RIGHT ayudan a determinar la dirección de movimiento del objeto Elefante. Los miembros x y y guardan la posición del objeto Elefante mientras que dir guarda la dirección en la cuál se mueve el objeto Elefante, finalmente animacion guarda un objeto Animacion con la animación de nuestro elefante. Cabe destacar que los miembros de la clase son públicos para dar acceso directo a estos valores en lugar de implementar los métodos get y set para cada miembro ya que de esta manera se tiene un menor costo de memoria y una mayor velocidad de acceso. Sin embargo, aunque es recomendable, no siempre es conveniente deshacerse de todos los métodos get y set, es importante tomar en cuenta el diseño de la aplicación antes de tomar esta decisión.

Ahora, en el método constructor de la clase Elefante se reciben como parámetro los valores para la posición en x y y y la animación del objeto. La dirección del movimiento del objeto se define por defecto hacia la derecha con el valor de RIGHT. * Método constructor de la clase Elefante * @param x es la posición en X del Elefante * @param y es la posición en Y del Elefante * @param animacion es la animación del Elefante public Elefante (int x, int y, Animacion animacion) { this.x = x; this.y = y; //Dirección por defecto del Elefante dir = RIGHT; this.animacion = animacion; Finalmente, definimos un último método llamado avanza() que se encarga de actualizar la posición del objeto en base a su dirección. * Método avanza actualiza la posición del Elefante en * base a su dirección public void avanza(){ //Avanza a la derecha if (dir == RIGHT) x += 5; //Avanza a la izquierda if (dir == LEFT) x - = 5; Clase Animacion La clase Animacion provee las herramientas necesarias para crear una animación cuadro por cuadro que podemos desplegar en pantalla. Esta incluye cuatro miembros que definen los cuadros de la animación, la duración de la animación, el índice del cuadro actual y el tiempo que ha transcurrido de animación. Iniciamos por importar las clases requeridas en la clase, definimos también la clase y los miembros de esta. import android.graphics.bitmap; import java.util.arraylist; import java.util.list;

public class Animacion { //Lista de objetos Cuadro, contiene los cuadros de la animación public List<Cuadro> cuadros; //Índice del cuadro actual public int indice; //Tiempo transcurrido public float tiempo; //Duración de la animación public float duracion; La lista de objetos Cuadro, cuadros, contiene todos los cuadros (imágenes) de la animación; la clase Cuadro es una clase interna de la clase Animacion, esta la definiremos más adelante. En indice guardamos el índice del cuadro que se esta mostrando cuando reproducir la animación, tiempo lleva la cuenta del tiempo que ha transcurrido desde que la animación inicio mientras que duracion guarda la duración total de la animación. En seguida creamos un método constructor vacío para nuestra clase con el cual crearemos animaciones vacías. Para esto creamos la lista de objetos Cuadro sin agregar ningún elemento a la misma, inicializamos duración en 0 y llamamos al método iniciar(), que más adelante definiremos, que simplemente se encarga de iniciar el tiempo transcurrido en 0 al igual que el índice para que la animación corra desde el primer cuadro cuando inicie. * Método constructor de la clase Animación * Crea una animación vacía public Animacion() { //Crea la lista de cuadros cuadros = new ArrayList<Cuadro>(); //Inicializa la duración de la animación en 0 duracion = 0; //Inicializa la animación iniciar(); Ahora definimos el método sumacuadro() que se encarga de añadir cuadros a la animación * Método sumacuadro * Añade un cuadro (imagen) a la animación con * la duración indicada (tiempo que se muestra el cuadro) * * @param imagen es la imagen del cuadro * @param duracion es el tiempo que se muestra el cuadro

public synchronized void sumacuadro(bitmap imagen, float duracion) { //Agrega la duración del cuadro a la duración de la animación this.duracion += duracion; //Crea un nuevo cuadro y lo agrega a la animación cuadros.add(new Cuadro(imagen, this.duracion)); El método recibe como parámetros la imagen del cuadro y un valor flotante que indica la duración del cuadro. Se añade la duración del cuadro a la duración de la animación. Creamos un nuevo objeto Cuadro utilizando la imagen que recibimos como parámetro y la duración de la animación y lo agregamos a la lista de objetos Cuadro. Continuamos por definir los métodos iniciar() y anima(). Como se mencionó anteriormente, el método iniciar() se encarga de inicializar el índice y el tiempo de la animación en 0 con el fin de que una vez que la animación se reproduzca esta inicie por desde el primer cuadro. Por su parte, el método anima() actualiza la animación en base al tiempo transcurrido desde la última vez que se actualizó la animación. * Método iniciar * Inicializa la animación en el primer cuadro con * tiempo 0 public synchronized void iniciar(){ tiempo = 0; indice = 0; * Método anima * Actualiza la animación en base al tiempo transcurrido desde * la última actualización * * @param tiempo es el tiempo transcurrido public synchronized void anima(float tiempo) { //Si la animación no esta vacía if (cuadros.size() > 1) { //Guarda el tiempo transcurrido this.tiempo += tiempo; //Si el tiempo transcurrido es mayor a la duración de la //animación if (this.tiempo >= duracion) { //Reinicia la animación this.tiempo = this.tiempo % duracion;

indice = 0; //Cambia el índice del cuadro de acuerdo al tiempo //transcurrido while (this.tiempo > cuadros.get(indice).tiempo) { indice ++; Como último método de esta clase definimos el método getcuadro() el cual simplemente se encarga de regresar la imagen del cuadro que se este desplegando en pantalla para poder desplegarlo. * Método getcuadro * Retorna la imagen del cuadro actual * * @return Regresa un Bitmap con la imagen del cuadro actual public synchronized Bitmap getcuadro(){ //Si la animación esta vacía if (cuadros.size() == 0) //Regresa nulo return null; else //Regresa la imagen del cuadro actual return cuadros.get(indice).imagen; Para finalizar con la clase Animacion definimos la clase interna Cuadro que nos permite crear objetos Cuadro para manejar la imagen y la duración de un cuadro de animación. La clase Cuadro este compuesta por dos miembros, imagen y tiempo que guardan respectivamente la imagen del cuadro y el tiempo de duración del mismo. En esta clase definimos dos métodos constructores, uno vacío y otro con parámetros con los cuales podremos crear ya sea un cuadro de animación vacío o uno con las características que nosotros necesitamos. * Clase Cuadro * Maneja la imagen y la duración de un cuadro de animación public class Cuadro { Bitmap imagen; //Imagen del cuadro float tiempo; //Duración del cuadro * Método constructor de la clase Cuadro

* Crea un cuadro vacío public Cuadro() { this.imagen = null; this.tiempo = 0; * Método constructor de la clase Cuadro * Crea un cuadro de animación con las imagen y el tiempo * recibidos como parámetros * * @param imagen es la imagen del cuadro * @param tiempo es la duración del cuadro public Cuadro(Bitmap imagen, float tiempo) { //Imagen del cuadro this.imagen = imagen; //Duración del cuadro this.tiempo = tiempo; Actividad TutorialElefante Ya que tenemos listas las clases Elefante y Animacion continuaremos por trabajar con nuestra Actividad que mostrará la animación del elefante. Sin embargo, antes de empezar a trabajar de lleno con nuestra actividad debemos de realizar algunas configuraciones a nuestro proyecto. Primero necesitamos asegurarnos de que nuestro proyecto contenga la carpeta Assets ya que en esta guardaremos los recursos que la Actividad utilizará. Si el proyecto no contiene esta carpeta será necesario crearla al mismo nivel de la carpeta Resources. Proyecto: -Assets -Resources - La razón por la cual utilizamos este directorio para guardar nuestros recursos en lugar de utilizar la carpeta Resources es que nos brinda una mayor libertad para organizar los recursos de acuerdo a nuestras necesidades a diferencia de la carpeta Resources que nos obliga a utilizar la estructura de organización que ya tiene predefinida. Por ejemplo, si nuestra Actividad manejará imágenes y sonidos y quisiéramos guardarlas en una carpeta de imágenes para las imágenes y otra de sonidos para los sonidos, dentro de la carpeta Assets podríamos

hacerlo sin problema alguno, por el contrario, en la carpeta de Resources no se podría implementar. Ahora definimos el archivo manifiesto de la aplicación <?xml version="1.0" encoding="utf- 8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jcj.testelefante" android:versioncode="1" android:versionname="1.0" android:installlocation="preferexternal"> <application android:label="@string/app_name" android:icon="@drawable/icon" android:debuggable="true"> <activity android:name="tutorialelefante" android:label="@string/app_name" android:configchanges="keyboard keyboardhidden orientation" android:screenorientation="portrait"> <intent- filter> <action android:name= "android.intent.action.main" /> <category android:name= "android.intent.category.launcher" /> </intent- filter> </activity> </application> <uses- permission android:name="android.permission.wake_lock"/> <uses- permission android:name= "android.permission.write_external_storage"/> <uses- sdk android:minsdkversion="3" android:targetsdkversion="8"/> </manifest> Analizemos que es lo que realiza cada elemento definido en el manifiesto y sus atributos. El elemento manifest inicia por definir un namespace de XML que en este caso es android el cual se implementa en el resto del archivo. El atributo package define el nombre del paquete raíz de la aplicación( en el caso de este ejemplo es com.jcj.testelefante pero en tu caso será el que tu hayas definido para el proyecto). Los atributos versioncode y versionname indican el número de versión de la aplicación, sin embargo son usados con diferentes propósitos, el primero es utilizado por el Android Market para llevar un registro de las versiones de la aplicación y el segundo es desplegado por el Android Market a los usuarios cuando revisan la aplicación. El atributo installlocation existe apartir de la versión 2.2 de Android y específica donde debe ser instalada la aplicación. En este caso el valor preferexternal indica la aplicación sea instalada en la tarjeta SD en caso de que haya una instalada.

El elemento application define las propiedades de la aplicación, inicia con el atributo label el cual define el nombre de la aplicación que será mostrado en el dispositivo una vez que sea instalada. El atributo icon define el icono de la aplicación mientras que el atributo debuggable indica si la aplicación puede debuggearse o no, para aplicaciones en desarrollo lo más común es definir este atributo en true. El elemento activity define las propiedades de una Actividad, si nuestra aplicación contiene más de una Actividad entonces será necesario definir este elemento para cada Actividad. En el atributo name se indica el nombre de la clase de la Actividad relativo al paquete definido en el elemento manifest. El atributo label despliega el texto definido en la barra de título de la Actividad si es que esta contiene una. Con configchanges le indicamos a Android que nosotros nos haremos cargo de alguno o algunos cambios de configuración que se pudieran presentar en el dispositivo como por ejemplo la orientación, en este caso indicamos que nos encargaremos de los cambios en la configuración del teclado del dispositivo así como también del cambio de orientación del teclado. Por último, el atributo screenorientation indica que deseamos fijar la vista de la aplicación en una sola orientación que en este caso será en orientación portrait. El elemento intent-filter que esta dentro del elemento activity ayuda a determinar la forma en que Android se comunicará con la Actividad. En el caso de este ejemplo, en el atributo action indicamos que la Actividad es la actividad principal de la aplicación y con el atributo category indicamos a Android que la Actividad sea añadida al application launcher de Android de tal manera que al presionar el botón de la aplicación en el dispositivo esta Actividad será llamada. El elemento uses-permission nos permite tener acceso a recursos del sistema como, la cámara, Internet, la tarjeta SD, etc. En esta actividad pediremos permiso al sistema para implementar el Wake Lock y tener acceso a la tarjeta SD para poder instalar nuestra aplicación en ella. El Wake Lock evita que el dispositivo entre en estado de reposo cuando no ha recibido ninguna entrada por parte del usuario. Finalmente, el elemento uses-sdk nos ayuda a definir los requerimientos mínimos de plataforma para que la aplicación pueda correr sin ningún problema. El atributo minsdkversion especifica la plataforma mínima con la cual es compatible la aplicación mientras que el atributo targetsdkversion especifica la versión de plataforma para la cual estamos desarrollando. En este tipo de casos es importante que en el desarrollo se tenga cuidado de no utilizar APIs que no existan en las versiones anteriores de Android para asegurar la compatibilidad de la aplicación. Existen muchos más elementos y atributos que podemos definir de acuerdo a los requerimientos de la aplicación sin embargo, para nuestra aplicación no necesitas definir ningún elemento o atributo extra.

Para nuestra aplicación no definiremos un layout en XML si no que lo haremos dentro del mismo código de la Actividad, por lo tanto ahora podemos ir directamente a trabajar con la Actividad. Dado a que en nuestra Actividad mostraremos una animación necesitaremos de una vista especial que nos ayude a realizar el trabajo de la manera más óptima posible. Para esto haremos nuestra propia implementación de la clase SurfaceView de Android la cual nos ofrece una vista que permite la posibilidad de trabajar en un thread independiente al de la Actividad. Además para asegurarnos de que nuestra animación se vea igual y mantenga su proporción independientemente del dispositivo en el que corra la aplicación utilizaremos un buffer virtual para dibujar la escena y finalmente proyectarla en la pantalla. Iniciamos nuestra Actividad importando las clases que estaremos implementando así como también por la definición de la misma y de sus miembros. import android.app.activity; import android.content.context; import android.content.res.assetfiledescriptor; import android.content.res.assetmanager; import android.content.res.configuration; import android.graphics.bitmap; import android.graphics.bitmap.config; import android.graphics.bitmapfactory; import android.graphics.canvas; import android.graphics.rect; import android.media.audiomanager; import android.media.soundpool; import android.os.bundle; import android.os.powermanager; import android.os.powermanager.wakelock; import android.view.surfaceholder; import android.view.surfaceview; import android.view.window; import android.view.windowmanager; import java.io.ioexception; import java.io.inputstream; public class TutorialElefante extends Activity { //Vista de la Actividad ElefanteFastRenderView renderview; //Candado para evitar que el dispositivo duerma WakeLock wakelock; //Objeto Elefante Elefante elefante; //Objeto Animacion Animacion elefanteanim;

//Colección de sonidos SoundPool pool; //Identificador de sonido int soundid = - 1; //Posiciones 'x' y 'y' int x, y; //Objetos Bitmap para el manejo de imágenes Bitmap cuadro, framebuffer; El miembro renderview de la clase ElefanteFastRenderView es la implementación de la clase SurfaceView que anteriormente mencionamos, esta es la vista con la cual estaremos trabajando en la Actividad. La clase ElefanteFastRenderView, la cual definiremos más adelante, esta definida como una clase interna de nuestra Actividad. El miembro wakelock de la clase WakeLock nos ayudará a configurar el dispositivo para evitar que este entre en estado de reposo después de cierto tiempo. Los miembros elefante y elefanteanim son objetos de las clases Elefante y Animacion respectivamente, estos dos miembros se integran para crear el elefante animado que se mostrará en la pantalla. El miembro pool de la clase SoundPool consiste en una colección de sonidos que podemos configurar y administrar para reproducir sonidos en la aplicación. Cuando trabajamos con audio debemos de tomar muy en cuenta el tamaño de el o los archivos de sonido con los cuales trabajaremos ya que debemos buscar la manera de optimizar el uso de la memoria para evitar que la aplicación se vuelva muy lenta. Cuando trabajamos con archivos de sonido cortos y que ocupan poca memoria, como por ejemplo cualquier efecto de sonido, implementamos la clase SoundPool la cual nos permitirá cargar los archivos a memoria para posteriormente trabajar con ellos. En el caso de archivos de sonido más largos y pesados no es conveniente cargarlos a memoria ya que consumirán una gran cantidad de recursos, para estos utilizaremos un método de streaming de audio el cual discutiremos más adelante en otro tutorial. El miembro soundid es un valor entero que implementaremos en conjunto con el miembro pool para poder reproducir nuestro efecto de sonido. Los miembros x y y nos ayudan a controlar la posición del elefante. Finalmente tenemos dos miembros de la clase Bitmap, cuadro y framebuffer; cuadro nos servirá de apoyo para cargar las imágenes de los cuadros de la animación del elefante mientras que framebuffer es el buffer virtual del cual hablamos antes que nos ayudará a dibujar de manera correcta nuestra vista en cualquier dispositivo en el cual probemos la aplicación. Continuamos ahora por definir el método oncreate() de nuestra Actividad. En este método configuraremos la Actividad para que se despliegue en full screen, implemente el wake lock para que el dispositivo no entre en reposo y tenga el control del volumen del contenido multimedia. Dentro de la misma creamos la colección de sonidos, cargamos los recursos (sonido e imágenes), creamos la animación del elefante y el objeto Elefante y finalmente asignamos nuestra implementación de la clase SurfaceView, renderview, como la vista de la Actividad.

* Método oncreate sobrescrito de la clase Activity * Llamado cuando la Actividad se crea por primera vez, en * este se realiza la configuración de la actividad * @param savedinstancestate es el estado de la última ejecución de la * Actividad @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); //Configuración para pantalla en fullscreen requestwindowfeature(window.feature_no_title); getwindow().setflags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //Determina la orientación del dispositivo y //crea un buffer en base a esta boolean islandscape = getresources().getconfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; //Ancho del buffer int framebufferwidth = islandscape? 480 : 320; //Alto del buffer int framebufferheight = islandscape? 320 : 480; //Crea el buffer framebuffer = Bitmap.createBitmap(frameBufferWidth, framebufferheight, Config.RGB_565); //Evita que el dispositivo se duerma PowerManager powermanager =(PowerManager) getsystemservice(context.power_service); wakelock = powermanager.newwakelock( PowerManager.FULL_WAKE_LOCK, "Test"); //Establece el control del volumen del sonido setvolumecontrolstream(audiomanager.stream_music); //Crea una colección de sonidos para reproducir pool = new SoundPool (1, AudioManager.STREAM_MUSIC, 0); //Crea un nuevo objeto Animacion elefanteanim = new Animacion(); try{ //Crea un administrador de contenidos para //cargar los contenidos

AssetManager assetmanager = getassets(); //Carga un efecto de sonido AssetFileDescriptor descriptor = assetmanager.openfd( "elephant.ogg"); //Agrega el efecto de sonido a la colección de sonidos soundid = pool.load(descriptor, 1); InputStream is; //Carga las imágenes de una animación y //se añaden a la animación for (int i = 0; i < 6; i++) { //Carga la imagen is = assetmanager.open("elefante" + i + ".png"); cuadro = BitmapFactory.decodeStream(is); is.close(); //Agrega la imagen a la animación elefanteanim.sumacuadro(cuadro, 0.1f); catch (IOException e) { // //Crea un nuevo objeto Elefante elefante = new Elefante(frameBufferWidth / 2 - elefanteanim.getcuadro().getwidth() / 2, framebufferheight / 2 - elefanteanim.getcuadro().getheight() / 2, elefanteanim); //Crea y establece la vista de la Actividad renderview = new ElefanteFastRenderView(this); setcontentview(renderview); El método inicia por configurar la pantalla en modo fullscreen. Posteriormente revisamos la orientación del dispositivo para determinar el ancho y el alto del buffer virtual que utilizaremos para dibujar. Estableceremos como base un buffer de dimensiones 480 x 320 (Landscape) ó 320 x 480 (Portrait) que posteriormente se ajustará al tamaño de la pantalla del dispositivo manteniendo esta proporción. Creamos un PowerManager que nos permitirá administrar las configuraciones de energía del dispositivo, en este caso lo utilizamos para crear el wake lock que evitará que el dispositivo entre en estado de reposo. Para establecer el control del volumen del sonido en la Actividad implementamos el método setvolumecontrolstream() al cual le indicamos que el stream que utilizaremos será el de la música (otro stream podría ser el del volumen de las llamadas). Al crear nuestra colección de sonidos le indicamos al constructor de la clase SoundPool le indicamos que solo podrá reproducir un máximo de un sonido a la vez y que esto lo realizará a través del stream de música del sistema ( el 0 que se envía como parámetro es un valor por defecto ya que actualmente este no tiene ninguna funcionalidad pero es requerido por el constructor). Después de esto creamos el objeto Animacion que contendrá la animación del

elefante y cargamos los recursos de nuestra aplicación. Esto lo realizamos dentro un try-catch para atrapar cualquier excepción que se produzca en caso de que haya algún error al cargar los recursos. Para cargar los recursos es necesario que, además de asegurarnos de que todos estén dentro de la carpeta Assets de nuestro proyecto, creemos un objeto AssetManager que nos brinda las herramientas para cargar y administrar los recursos. En el caso del sonido necesitamos generar un descriptor que nos ayude a identificar el archivo de audio, este descriptor lo guardamos en un objeto de la clase AssetFileDescriptor. Una vez que tenemos el descriptor lo cargamos en la colección de sonidos la cual nos retornará un valor entero que nos ayudará a identificar el sonido dentro de la colección. En el caso de las imágenes nos apoyamos en un InputStream para tener acceso a los archivos. Dado a que en el caso de este ejemplo las imágenes están en serie utilizamos un ciclo for para cargarlas. Con la ayuda del AssetManager abrimos las imágenes y las guardamos en el InputStream, después se lo pasamos al método BitmapFactory.decodeStream() el cuál creará un objeto Bitmap que finalmente utilizaremos para crear un nuevo cuadro de animación. Una vez que termina de cargar los recursos se crea el objeto Elefante para el cual definimos las posiciones en x y y y la animación. Para finalizar el método creamos una nueva vista utilizando nuestra implementación de la clase SurfaceView y la asignamos como la vista de la Actividad. Ahora pasamos a definir los métodos onpause() y onresume(). Debido a que nuestra vista la trabajaremos en un thread independiente al de la Actividad será muy importante mantener sincronizados los threads para que trabajen a la par y serán estos métodos los que nos ayudarán a lograrlo. * Método onpause sobrescrito de la clase Activity * Llamado cuando la Actividad pasa a segundo plano pero aun es * visible, * libera recursos usados por la Actividad @Override protected void onpause() { super.onpause(); //Pausa la vista de la Actividad renderview.pause(); //Libera el candado que protege la pantalla wakelock.release(); //Se liberan los efectos de sonido que actualmente se encuentran //en la colección pool.release(); * Método onresume sobrescrito de la clase Activity * Llamado cuando la Actividad vuelve a primer plano

@Override protected void onresume() { super.onresume(); //Reanuda la vista de la Actividad renderview.resume(); //Retoma el candado que protege a la pantalla wakelock.acquire(); En el método onpause() pausamos el thread de la vista llamando a su método pause(). Liberamos el wake lock para que el sistema o la actividad que se encuentre actualmente en primer plano pueda encargarse de manejarlo. También liberamos los recursos de la colección de sonidos para liberar memoria a través del método release() de la clase SoundPool. En el método onresume() simplemente reanudamos el thread de la vista llamando a su método resume() y retomamos el wake lock para evitar que el dispositivo entre en estado de reposo. Finalmente, definimos nuestra implementación de la clase SurfaceView, la clase ElefanteFastRenderView. La clase ElefanteFastRenderView extiende a la clase SurfaceView e implementa a la interfase Runnable lo cual permite que esta clase corra en su propio thread. Empezamos esta clase por definir sus miembros. * Clase ElefanteFastRenderView * Clase interna de la clase TutorialElefante * Maneja una vista y su ciclo de vida en un thread independiente public class ElefanteFastRenderView extends SurfaceView implements Runnable { //Thread de la vista Thread renderthread = null; //Contenedor de la vista SurfaceHolder holder; //Canvas para dibujar Canvas canvas; //Bandera para conocer el estado de la Activdad volatile boolean running = false; //Controladores de tiempo float tiempotick = 0, tick = 0.1f; El renderthread es el thread mismo de la vista el cual tendremos que sincronizar con el thread de la Actividad. El miembro holder de la clase SurfaceHolder es una superficie que contiene a la vista con la cual estamos trabjando. Con la ayuda del miembro canvas podremos dibujar sobre nuestro buffer virtual para posteriormente proyectarlo en la pantalla. El miembro

running nos indicará si el thread esta actualmente activo lo cual nos ayuda para la sincronización entre el thread de la vista y el thread de la Actividad. Los miembros tiempotick y tick nos indican el tiempo transcurrido y el tiempo que debe durar cada ciclo de actualización respectivamente. En el método constructor de la clase mandamos a llamar al constructor de la clase padre de nuestra implementación para que la vista sea creada de manera adecuada, a este constructor le enviamos el contexto en el cual estamos trabajando que en este caso es nuestra Actividad. Obtenemos también el contenedor de la vista ya que más adelante lo utilizaremos para proyectar en el nuestro buffer virtual. Creamos también un canvas con nuestro buffer virtual para poder dibujar en el. * Método contructor de la clase ElefanteFastRenderView * Llama al método constructor de la clase base al cual * le pasa el contexto como parámetro y crea los objetos * requeridos por la clase * @param context es el contexto desde el cual fue llamado el * método public ElefanteFastRenderView(Context context) { super(context); //Obtiene el contenedor holder = getholder(); //Crea un nuevo objeto canvas con el buffer creado en la //Actividad canvas = new Canvas(frameBuffer); Los métodos resume() y pause() de esta clase se encargan respectivamente de reanudar y pausar el thread de la vista así como también de informar a esta del estado del thread. * Método resume * Llamado cuando la Actividad vuelve a primer plano, * inicia el thread de la vista public void resume() { //La bandera indica que la Actividad esta en ejecución running = true; //Crea un nuevo thread para la vista renderthread = new Thread(this); //Iniciliza el thread de la vista renderthread.start();

* Método pause * Llamado cuando la Actividad pasa a segundo plano, * detiene el thread de la vista public void pause() { //La bandera indica que la Actividad no esta en ejecución running = false; //Espera a que el thread de la vista se detenga while(true) { try { renderthread.join(); break; catch (InterruptedException e) { // Ahora en el método run() de la clase es donde ocurre lo interesante. Es en este método donde mandamos a actualizar al objeto Elefante, dibujamos en nuestro buffer virtual y finalmente proyectamos el buffer virtual en la pantalla del dispositivo. * Método run sobrescrito de la clase Thread * Llamado cuando el thread de la vista esta en ejecución, * se encarga de actualizar la vista de la Actividad public void run() { //Objeto Rect crea un rectángulo Rect dstrect = new Rect(); //Obtiene el tiempo actual long tiempoi = System.nanoTime(); //El ciclo se ejecuta cuando la Actividad esta en ejecución while (running) { //Verifica que exista una vista válida if(!holder.getsurface().isvalid()) continue; //Calcula el tiempo transcurrido float tiempo = (System.nanoTime() - tiempoi) / 1000000000.0f; //Obtiene el tiempo actual tiempoi = System.nanoTime(); //Pinta un fondo amarillo canvas.drawrgb(255, 255, 0); //Actualiza el objeto Elefante en base al tiempo //transcurrido

actualizaelefante(tiempo); //Actualiza la animación en base al tiempo //transcurrido elefante.animacion.anima(tiempo); //Dibuja el objeto Elefante en el buffer canvas.drawbitmap(elefante.animacion.getcuadro(), elefante.x, elefante.y, null); //Crea un nuevo canvas con la vista actual Canvas pantalla = holder.lockcanvas(); //Determina la resolución de la pantalla pantalla.getclipbounds(dstrect); //Dibuja el buffer en la pantalla con el tamaño de la //pantalla pantalla.drawbitmap(framebuffer, null, dstrect, null); holder.unlockcanvasandpost(pantalla); El método run() trabaja de la siguiente manera. Primero crea un objeto Rect el cual se utilizará para obtener las dimensiones de la pantalla y proyectar el buffer virtual a este tamaño. Se obtiene el tiempo actual del sistema lo cuál nos ayudará a actualizar al objeto Elefante más adelante. Dentro de un ciclo que correrá mientras el estado del thread sea activo, verificamos que la vista con la cual estamos trabajando sea válida con el método holder.getsurface.isvalid(). Posteriormente calculamos el tiempo que ha transcurrido desde la última actualización y capturamos una vez más el tiempo actual para tenerlo como referencia para la próxima actualización. Con la ayuda del canvas que creamos con el buffer virtual pintamos un fondo amarilla en el buffer. Actualizamos al objeto Elefante con la ayuda del método AactualizaElefante() que más adelante definiremos, actualizamos también la animación del elefante y finalmente dibujamos el elefante en el buffer virtual. Por último capturamos el contenedor de la vista y con el creamos un canvas el cual usaremos para proyectar nuestro buffer virtual en la pantalla. Con el método getclipbounds() obtenemos las dimensiones del contenedor y las guardamos en el objeto Rect que previamente habíamos creado. Proyectamos el buffer virtual en la pantalla con la ayuda del método drawbitmap() y utilizando las medidas del contenedor que guardamos en el objeto Rect. Finalmente liberamos el contenedor para que muestre en pantalla la imagen del elefante. Para finalizar, definimos el método actualizaelefante() el cual se encarga de actualizar la posición del objeto Elefante en base al tiempo transcurrido. El método checa que el elefante no salga por las orillas de la pantalla y lo envía en la dirección opuesta cada vez que alcance alguna de ellas. Cuando el elefante choca con las orillas se reproduce el efecto de sonido que tenemos cargado en la colección de sonidos. para esto primero nos aseguramos de que el sonido no este en reproducción con la ayuda del método pool.stop() al cual le damos como

parámetro el identificador de nuestro sonido. Finalmente reproduce el sonido con la ayuda del método pool.play() el cual recibe como parámetros el identificador del sonido que queremos reproducir, el volumen del canal izquierdo, el volumen del canal derecho, la prioridad de reproducción, si el sonido tendrá loop o no, y la velocidad de reproducción. * Método actualizaelefante * Llamado para actualizar el objeto Elefante * @param tiempo es el tiempo transcurrido desde la última * vez que se actualizó private void actualizaelefante(float tiempo) { //Guarda el tiempo transcurrido tiempotick += tiempo; //Actualiza mientras el tiempo transcurrido sea mayor al //tiempo de actualización while(tiempotick > tick) { tiempotick - = tick; //Actualiza la posición del objeto Elefante elefante.avanza(); //Checa colisión con las orillas de la pantalla if(elefante.x + elefante.animacion.getcuadro().getwidth() > framebuffer.getwidth() elefante.x < 0){ //Cambia la dirección elefante.dir *= - 1; //Actualiza la posición del objeto Elefante elefante.avanza(); //Si el sonido ya estaba en reproducción lo //detiene pool.stop(soundid); //Reproduce el efecto de sonido pool.play(soundid, 1, 1, 0, 0, 1);