Tutoriales Android

Introducción y objetivos

Android nos proporciona a través de su API gráfica una potente y variada colección de funciones que pueden cubrir prácticamente cualquier necesidad gráfica de una aplicación. Podemos destacar la manipulación de imágenes, gráficos vectoriales, animaciones, trabajo con texto o gráficos en 3D.

En este capítulo se introducen alguna de las características más significativas de la API gráfica de Android. Nos centraremos en el estudio de las clases utilizadas para el desarrollo de gráficos en 2D. En el capítulo 2 hemos descrito cómo se utilizan las vistas como elemento constructivo para el diseño de la interfaz de usuario. Disponíamos de una amplia paleta de vistas. No obstante, en muchas ocasiones va a ser interesante diseñar nuestras propias vistas. En este capítulo veremos cómo hacerlo.

Trataremos de aplicar lo aprendido en un ejemplo concreto, la representación de gráficos en Asteroides. Se utilizarán dos técnicas alternativas: los gráficos en mapa de bits y en formato vectorial. Al final del capítulo se describen las herramientas disponibles en Android para realizar animaciones. En concreto se describirán las animaciones Tween y las animaciones de propiedades. Por supuesto, resultaría imposible abarcar todas sus funciones para gráficos, por lo que se recomienda al lector que consulte la documentación de Android para obtener una descripción pormenorizada.

 

Objetivos:

  • Enumerar las distintas API gráficas par 2D y 3D disponibles en Android.
  • Describir cómo se utilizan las principales clases para gráficos en 2D (Canvas, Paint y Path).
  • Introducir la clase Drawable y utilizar muchos de sus descendientes (BitmapDrawable, GradienDrawable, …).
  • Estudiar cómo creas nuevas vistas y utilizarlas en distintas aplicaciones.
  • Aplicar gran parte de lo aprendido en un ejemplo concreto: Asteroides.
  • Describir las herramientas de Android para crear animaciones.

 

Clases para gráficos en Android

Antes de empezar a trabajar con gráficos conviene familiarizarse con las clases que nos van a permitir crear y manipular gráficos en Android. A continuación se introducen algunas de estas clases:

Puedes ver algunos aspectos relacionados en formato poli[Media]

APIs  para gráficos en Android

 

 

 

 

Canvas

La clase Canvas representa una superficie donde podemos dibujar. Dispone de una serie de métodos que nos permiten representar líneas, círculos, texto, …Para dibujar en un Canvasnecesitaremos un pincel (Paint) donde definiremos el color, grosor de trazo, transparencia,… También podremos definir una matriz de 3x3 (Matrix) que nos permitirá transformar coordenadas aplicando una translación, escala o rotación. Otra opción consiste en definir un área conocida como Clip, de forma que los métodos de dibujo afecten solo a esta área.

Veamos a continuación algunos métodos de la clase Canvas. No se trata de un listado exhaustivo y muchos de estos métodos pueden trabajar con otros parámetros, por lo tanto se recomienda consultar la documentación del SDK para una información más detallada.

Para dibujar figuras geométricas:

drawCircle(float cx, float cy, float radio, Paint pincel)
drawOval(RectF ovalo, Paint pincel)
drawRect(RectF rect, Paint pincel)
drawPoint(float x, float y, Paint pincel)
drawPoints(float[] pts, Paint pincel)

Para dibujar líneas y arcos:

drawLine(float iniX, float iniY, float finX, float finY,
                                                      Paint pincel)
drawLines(float[] puntos, Paint pincel)
drawArc(RectF ovalo, float iniAnglulo, float anglulo, 
                                                      boolean usarCentro,Paint pincel)
drawPath(Path trazo, Paint pincel)

Para dibujar texto:

drawText(String texto, float x, float y, Paint pincel)
drawTextOnPath(String texto, Path trazo, float desplazamHor, 
                                  float desplazamVert, Paint pincel)
drawPosText(String texto, float[] posicion, Paint pincel)

Para rellenar todo el Canvas (a no ser que se haya definido un Clip)

drawColor(int color)
drawARGB(int alfa, int rojo, int verde, int azul)    
drawPaint(Paint pincel)

Para dibujar imágenes:

 

drawBitmap(Bitmap bitmap, Matrix matriz, Paint pincel)

Si definimos un Clip, solo se dibujará en el área indicada:

boolean clipRect(RectF rectangulo)
boolean clipRegion(Region region)
boolean clipPath(Path trazo)

Definir una matriz de transformación (Matrix) nos permitirá transformar coordenadas aplicando una translación, escala o rotación.

setMatrix(Matrix matriz)
Matrix getMatrix()
concat(Matrix matriz)
translate(float despazX, float despazY)
scale(float escalaX, float escalaY)
rotate(float grados, float centroX, float centroY)
skew(float despazX, float despazY)

Para averiguar el tamaño del Canvas:

int getHeight()
int getWidth()

Puedes ver algunos aspectos relacionados en formato poli[Media]

 La clase canvas  en Android

 

Ejercicio paso a paso: Creación de una vista personalizada

A continuación se muestra un ejemplo donde se crea una vista que es dibujada por código por medio de un Canvas.

1.     Crea un nuevo proyecto con los siguientes datos:

Application name: EjemploGraficos

Package name: org.example.ejemplograficos

Minimun Requiered SDK: API 7 Android 2.1 (Eclair)

Activity Name: EjemploGraficosActivity

Layout Name: main.xml

2.     Reemplaza el código de la actividad por:

public class EjemploGraficosActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new EjemploView(this));
    }
 
    public class EjemploView extends View {
            public EjemploView (Context context) {
                super(context);
            }
        @Override
            protected void onDraw(Canvas canvas) {
            //Dibujar aquí
        }
    }
}

Comienza con la creación de una Activity, pero en este caso, el objeto View que asociamos a la actividad mediante el método setContentView() no está definido mediante XML. Por el contrario, es creado mediante código a partir del constructor de la clase EjemploView.

La clase EjemploView extiende View, modificando solo el método onDraw() responsable de dibujar la vista. A lo largo del capítulo iremos viendo ejemplos de código que pueden ser escritos en este método.

3.     Si ejecutas la aplicación no se observará nada. Aprenderemos a dibujar en esta Canvasutilizando el objeto Paint descrito en la siguiente sección.

Nota sobre Java: Siempre que en un ejemplo aparezca un error indicándote que no se puede resolver un tipo, pulsa Ctrl-Shift-O para añadir los import de forma automática.

 

Paint

Como acabamos de ver, la mayoría de los métodos de la clase Canvas utilizan un parámetro de tipo Paint. Esta clase nos permite definir el color, estilo o grosor del trazado de un gráfico vectorial.

Puedes ver algunos aspectos relacionados en formato poli[Media]

 La clase Paint  en Android

Ejercicio paso a paso: Uso de la clase Paint

1.     Escribe dentro de OnDraw del ejercicio anterior el código siguiente:

Paint pincel = new Paint();
pincel.setColor(Color.BLUE);
pincel.setStrokeWidth(8);
pincel.setStyle(Style.STROKE);
canvas.drawCircle(150, 150, 100, pincel);

Nota sobre Java: Si pulsas Ctrl-Shift-O para añadir los import el sistema te advertirá que la clase Style está definida en dos paquetes diferentes:

Te preguntará cual de los dos quieres importar. No suele resultar complicado resolver este problema si analizas el contexto en el que estás trabajando. En nuestro caso estamos utilizando la clase Paintpor lo que la primera opción es la adecuada. Si alguna vez te equivocas en esta selección, lo normal es que aparecerán errores en el código. En este caso, deshaces y seleccionas la otra opción.

 

 

2.     Ejecuta la aplicación para ver el resultado.

3.     Aprovechando la opción de autocompletar de Eclipse, prueba otros valores para Color. YStyle.

4.     Prueba otros métodos de dibujo, como drawLine() o drawPoint().

Definición de colores

Android representa los colores utilizando enteros de 32 bits. Estos bits son divididos en 4 campos de 8 bits: alfa, rojo, verde y azul (ARGB, usando las iniciales en inglés). Al estar formado cada componente por 8 bits, podrá tomar 256 valores diferentes.

Los componentes rojo, verde y azul son utilizados para definir el color, mientras que el componente alfa define el grado de transparencia con respecto al fondo. Un valor de 255 significa un color opaco y a medida que vayamos reduciendo el valor el dibujo se irá haciendo transparente.

Para definir un color tenemos las siguientes opciones:

int color;
color = Color.BLUE;                  //Azul opaco
color = Color.argb(127, 0, 255, 0);  //Verde transparente
color = 0x7F00FF00;                  //Verde transparente
color = getResources().getColor(R.color.color_Circulo);

 Para conseguir una adecuada separación entre programación y diseño, se recomienda utilizar la última opción. Es decir, no definir los colores directamente en código, sino utilizar el fichero de recursos res/values/colors.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="color_circulo">#7fffff00</color>
</resources>

Como puedes observar los colores han de definirse en el fichero colors.xml mediante sus componentes ARGB en hexadecimal.

 

 

Ejercicio paso a paso: Definición de colores

 

1.     Modifica el ejercicio anterior, añadiendo al final de OnDraw el código siguiente:

pincel.setColor(Color.argb(127,255,0,0));
canvas.drawCircle(150, 250, 100, pincel);

2.     Observa como el color rojo seleccionado es mezclado con el color de fondo. Prueba otros valores de alfa.

3.     Reemplaza la primera línea que acabas de introducir por:

pincel.setColor(0x7FFF0000);

4.     Observa como el resultado es idéntico.

5.     Define en el fichero res/values/colors.xml un nuevo color utilizando el siguiente código:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="color_circulo">#7fffff00</color>
</resources>

6.     Modifica el ejemplo anterior para que se utilice este color definido en los recursos:

pincel.setColor(getResources().getColor(R.color.color_circulo));

{jcomments on}

 

Path

La clase Path permite definir un trazado a partir de segmentos de línea y curvas. Una vez definido puede ser dibujado con canvas.drawPath(Path, Paint). Un Path también puede ser utilizado para dibujar un texto sobre el trazado marcado.

Ejercicio paso a paso: Uso de la clase Path

1.     Reemplaza dentro de OnDraw del ejercicio anterior el código siguiente:

Path trazo = new Path();
trazo.addCircle(150, 150, 100, Direction.CCW);
canvas.drawColor(Color.WHITE);
Paint pincel = new Paint();
pincel.setColor(Color.BLUE);
pincel.setStrokeWidth(8);
pincel.setStyle(Style.STROKE);
canvas.drawPath(trazo, pincel);
pincel.setStrokeWidth(1);
pincel.setStyle(Style.FILL);
pincel.setTextSize(20);
pincel.setTypeface(Typeface.SANS_SERIF);
canvas.drawTextOnPath("Desarrollo de aplicaciones para
                     móviles con Android", trazo, 10, 40, pincel);

2.     El resultado obtenido ha de ser:

3.     Modifica en la segunda línea el parámetro Direction.CCW (contrario a las agujas del reloj) porDirection.CW (a favor de las agujas del reloj). Observa el resultado.

4.   Modifica los parámetros de canvas.drawTextOnPath() hasta que comprendas su significado.

5.     ¿Podrías dibujar el texto a favor de las agujas del reloj, pero por fuera del círculo?

 

Ejercicio paso a paso: Un ejemplo de Path más complejo

1.     Reemplaza las dos primeras líneas del ejemplo anterior por:

Path  trazo = new Path();
trazo.moveTo(50, 100);
trazo.cubicTo(60,70, 150,90, 200,110);
trazo.lineTo(300,200); 

2.     El resultado obtenido ha de ser similar a:

El trazo comienza desplazándose a las coordenadas (50,100). Luego introduce una curva cúbica o Bézier hasta la coordenada (200,110). Una curva Bézier introduce dos puntos de control, el primero (60,70) permite controlar como arranca la dirección del comienzo de la curva y el segundo (150,90) la dirección del final de la curva. Funciona de la siguiente manera, si trazas una recta desde el comienzo de la curva (50,100) hasta el primer punto de control (60,70) la curva se trazará tangencialmente a esta recta. Finalmente, se añade una línea desde las coordenadas (200,110) hasta (300,200).

3.     ¿Por qué aparece una separación entre la “p” y la “l”? ¿Qué dos parámetros del siguiente gráfico tendríamos que modificar para que esta separación no estuviera? (Solución: (150,90) => (150,65))

 

Preguntas de repaso y reflexión: Las clases para gráficos en Android 

{jcomments on}

 

Drawable

La clase Drawable es una abstracción que representa “algo que puede ser dibujado”. Esta clase se extiende para definir gran variedad de objetos gráficos más específicos. Muchos de ellos pueden ser definidos como recursos usando ficheros XML. Entre ellos tenemos los siguientes:

BitmapDrawable: Imagen basada en un fichero gráfico (PNG o JPG). Etiqueta XML <bitmap>.

ShapeDrawable: Permite realizar un gráfico a partir de primitivas vectoriales, como formas básicas (círculos, cuadrados,…) o trazados (Path). No puede ser definido mediante un fichero XML.

LayerDrawable: Contiene un array de Drawable que son visualizados según el orden del array. El índice mayor del array es el que se representa encima. Cada Drawable puede situarse en una posición determinada. Etiqueta XML<layer-list>

StateListDrawable: Similar al anterior pero ahora podemos usar una máscara de bits podemos seleccionar los objetos visibles. Etiqueta XML <selector>.

GradientDrawable: Degradado de color que puede ser usado en botones o fondos.

TransitionDrawable: Una extensión de LayerDrawables que permite un efecto de fundido entre la primera y la segunda capa. Para iniciar la transición hay que llamar a startTransition(inttiempo). Para visualizar la primera capa hay que llamar a resetTransition(). Etiqueta XML<transition>.

AnimationDrawable: Permite crear animaciones frame a frame a partir de una serie de objetosDrawable. Etiqueta XML <animation-list>

También puede ser interesante que uses la clase Drawable o uno de sus descendientes como base para crear tus propias clases gráficas.

Además de ser dibujada, la clase Drawable proporciona una serie de mecanismos genéricos que permiten indicar como ha de ser dibujada. No todo Drawable ha de implementar todos los mecanismos. Veamos los más importantes:

El método setBounds(x1,y1,x2,y2) permite indicar el rectángulo donde ha de ser dibujado. Todo Drawable debe respetar el tamaño solicitado por el cliente, es decir, ha de permitir el escalado. Podemos consultar el tamaño preferido de un Drawable mediante los métodosgetIntrinsicHeight() y getIntrinsicWidth().

El método getPadding(Rect) nos proporciona información sobre los márgenes recomendados para representar contenidos. Por ejemplo, un Drawable que intente ser un marco para un botón, debe devolver los márgenes correctos para localizar las etiquetas, u otros contenidos, en el interior del botón.

El método setState(int[]) permite indicar al Drawable en qué estado ha de ser dibujado, por ejemplo "con foco", "seleccionado", etc. Algunos Drawable cambiarán su representación según este estado.

El método setLevel(int) permite  implementar un controlador sencillo para indicar como se representará el Drawable. Por ejemplo, un nivel puede ser interpretado como una batería de niveles o un nivel de progreso. Algunos Drawable modificarán la imagen basándose en el nivel.

Un Drawable puede realizar animaciones al ser llamado desde el cliente a su métodoDrawable.Callback. Todo cliente debe implementar esta interfaz (viasetCallback(Drawable.Callback)). El sistema nos facilita realizar esta tarea de forma sencilla a través de setBackgroundDrawable( Drawable) e ImageView.

 

Puedes ver algunos aspectos relacionados en formato poli[Media]

 La clase drawable  en Android

Existen varias alternativas para crear una instancia de Drawable. Puedes crearla a partir de un fichero de imagen almacenado en los recursos del proyecto, también puedes crearla a partir del diseño basado en XML o puedes crearla a partir de código.

Veamos con más detalle algunas de las subclases de Drawable:

{jcomments on}

BitmapDrawable

La forma más sencilla de añadir gráficos a tu aplicación es incluirlos en la carpetares/drawable del proyecto. El SDK de Android soporta los formatos PNG, JPG y GIF. El formato aconsejado es PNG, aunque si el tipo de gráfico así lo recomienda también puedes utilizar JPG. El formato GIF está desaconsejado.

Cada gráfico de esta carpeta es asociado a un ID de recurso. Por ejemplo, para el ficheromi_imagen.png se creará el ID mi_imagen. Este ID te permitirá hacer referencia al gráfico desde el código o desde XML.

Ejercicio paso a paso: Dibujar un BitmapDrawable de los recursos

Busca en Internet un fichero gráfico en codificación png o jpg (los formatos gráficos usados por defecto en Android).

1.     Renombra el fichero para que se llame mi_imagen.png o mi_imagen.jpg y arrastra ares/drawable.

2.     Declara la variable miImagen en la clase EjemploView del ejercicio anterior:

private Drawable miImagen;

3.     Escribe las siguientes tres líneas dentro del constructor de esta clase:

Resources res = context.getResources();
      miImagen = res.getDrawable(R.drawable.mi_imagen);
      miImagen.setBounds(30,30,200,200);

4.     Escribe la siguiente línea en el método onDraw:

miImagen.draw(canvas);

{jcomments on}

 

GrandienDrawable

También podemos definir en XML otro tipo de Drawables como GradienDrawable. Por ejemplo, el siguiente fichero define un degradado desde el color blanco (FFFFFF) a azul (0000FF):

<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#FFFFFF"
android:endColor="#0000FF"
android:angle="270" />
  </shape>

Este tipo de objetos gráficos es utilizado con frecuencia como fondo de botones o de pantalla. El parámetro angle indica la dirección del degradado. Solo se permiten los ángulos 0, 90, 180 y 270.

 

Ejercicio paso a paso: Definir un GradienDrawable.

1.     Abre el proyecto Asteroides.

2.     Crea el siguiente fichero res/drawable/degradado.xml:

<shape 
  xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
       android:startColor="#FFFFFF"
       android:endColor="#0000FF"
       android:angle="270" />
</shape>

Podrías introducir la siguiente línea en el constructor de una vista, para conseguir que estedrawable sea utilizado como fondo

setBackgroundResource(R.drawable.degradado);

3.     Resulta más conveniente definir el fondo de una vista en su Layout en XML en lugar de hacerlo por código. Comenta la línea introducida en el punto anterior e introduce el siguiente atributo en el Layout main.xml.

android:background="@drawable/degradado"

 

 

TransitionDrawableD

Un TransitionDrawable es una extensión de LayerDrawables que permite un efecto de fundido entre la primera y la segunda capa. Para iniciar la transición hay que llamar a startTransition(inttiempo). Para volver a visualizar la primera capa hay que llamar a resetTransition()..

Ejercicio paso a paso: Definir un TransitionDrawable

1.     Crea un nuevo proyecto con nombre Transition.

2.     Crea el siguiente recursos en res/drawable/transicion.xml con el código:

<?xml version="1.0" encoding="utf-8"?>
<transition  xmlns:android= 
          "http://schemas.android.com/apk/res/android">
  <item android:drawable="@drawable/asteroide1"/>
  <item android:drawable="@drawable/asteroide3"/>
</transition>

3.     Reemplaza en la actividad el método onCreate por el siguiente código:

@Override public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
   ImageView image = new ImageView(this);
   setContentView(image);
   TransitionDrawable transition = (TransitionDrawable)
                       getResources().getDrawable(R.drawable.transicion);
   image.setImageDrawable(transition);
   transition.startTransition(2000);
}

4. Si todo funciona correctamente, verás cómo la llamada a transition.startTransition(2000)provoca que la primera imagen se transforme a lo largo de dos segundos en la segunda imagen.

{jcomments on}

 

ShapeDrawable

Cuando quieras crear un gráfico dinámicamente mediante primitivas vectoriales, una buena opción puede ser utilizar ShapeDrawable. Esta clase permite dibujar gráficos a partir de formas básicas. Un ShapeDrawable es una extensión de Drawable, por lo tanto puedes utilizar todo lo que permite Drawable.

Ejercicio paso a paso: Definir un ShapeDrawable.

Veamos un ejemplo de cómo utilizar un objeto ShapeDrawable para crear una vista a medida.

 

1.     Abre el proyecto EjemploGraficos.

2.     En la clase EjemploView declara la siguiente variable:

private ShapeDrawable miImagen;

3.     Añade las siguientes tres líneas dentro del constructor de esta clase:

miImagen  = new ShapeDrawable(new OvalShape());
miImagen.getPaint().setColor(0xff0000ff);
miImagen.setBounds(10, 10, 310, 60);

En el constructor, un objeto ShapeDrawable es definido como un óvalo. Se le asigna un color y se definen sus fronteras.

4.     Escribe la siguiente línea en el método onDraw:

miImagen.draw(canvas);

5.     Ejecuta la aplicación y observa el resultado.

 

AnimatiAnimationDrawable

Android nos proporciona varios mecanismos para crear animaciones. Una ventaja a destacar es que estas animaciones pueden ser creadas en ficheros XML. En este apartado veremos una de las animaciones más sencillas, las creadas a partir de una serie de fotogramas. Para ello utilizaremos la clase AnimationDrawable.

Ejercicio paso a paso: Uso de AnimationDrawable.

1.     Crea un nuevo proyecto con nombre Animacion. Ha de crearse una actividad inicial con nombreAnimationActivity.

2.     Crea el siguiente recurso res/drawable/animacion.xml.

<animation-list
xmlns:android= "http://schemas.android.com/apk/res/android"
   android:oneshot= "false">
          <item android:drawable="@drawable/misil1" 
                       android:duration="200" />
          <item android:drawable="@drawable/misil2" 
                       android:duration="200" />
          <item android:drawable="@drawable/misil3" 
                       android:duration="200" />
</animation-list>

3.     Copia los ficheros misil1.png, misil2.png y misil3.png a la carpeta res/drawable.Los encontrarás enwww.androidcurso.com

4.     Reemplaza el código de la actividad por:

public class AnimacionActivity extends Activity {
       AnimationDrawable animacion;
       @Override public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             animacion = (AnimationDrawable)getResources().getDrawable( 
                                                                                             R.drawable.animacion);
             ImageView vista = new ImageView(this);
             vista.setBackgroundColor(Color.WHITE);
             vista.setImageDrawable(animacion);
             vista.setOnClickListener(new OnClickListener() {
                    public void onClick(View view) {
                           animacion.start();
                    }
             });
             setContentView(vista);
       }
}

En este ejemplo se comienza declarando un objeto animacion de la clase AnimationDrawable. Se inicializa utilizando el fichero XML incluido en los recursos. Se crea una nueva vista de la claseImageView para ser representada por la actividad y se pone como imagen de de esta vistaanimacion.  Finalmente se crea un escuchador de evento onClick para que cuando se pulse sobre la vista se ponga en marcha la animación.

 

Preguntas de repaso y reflexión:

Pincha aquí para hacer un test.

Como hemos vistos en los ejemplos anteriores, para poder dibujar en Android hemos tenido que crear una nueva clase EjemploView, descendiente de View. Esta clase era creada dentro de EjemploGraficosActivity por lo que solo podía ser utilizada desde esta clase. Va a resultar mucho más interesante crear esta clase en un fichero independiente. De esta forma podremos utilizarla desde cualquier parte de nuestro proyecto, o desde otros proyectos. Incluso estará disponible en la paleta de vistas del editor visual. El siguiente ejercicio, te permite verificar este concepto.

 

Ejercicio paso a paso: Creación de una nueva vista independiente

En este ejercicio vamos a poner la clase EjemploView en un fichero independiente para que pueda ser utilizada desde cualquier parte.

1. Abre el proyecto EjemploGraficos.

2. En la clase EjemploGraficosActivity selecciona todo el texto correspondiente a la definición de la clase EjemploView y córtalo al portapapeles.

3. Pulsa con el botón derecho sobre src/org.example/ejemplografico y selecciona la opción New/Class...

4. Introduce como nombre de la clase EjemploView.

5. Pega el texto que has puesto en el portapapeles.

6. Verifica que al ejecutar la aplicación el resultado es idéntico al obtenido en la sección anterior.

En este apartado aprovechamos para introducir otros aspectos que tendrás que tener en cuenta para crear nuevas vistas. Cuando quieres crear una nueva vista, tendrás que extender la clase View, escribir un constructor y como mínimo sobreescribir los métodos onSizeChanged()onDraw(). Es decir, tendrás que seguir el siguiente esquema:


public class MiVista extends View {
 
   public MiVista(Context context, AttributeSet attrs) {
          super(context, attrs);
          //Inicializa la vista
          //OjO: Aún no se conocen sus dimensiones
   }
 
   @Override protected void onSizeChanged(int ancho, int alto,
                 int ancho_anterior, int alto_anterior){
          //Te informan del ancho y el alto
   }
 
   @Override protected void onDraw(Canvas canvas) {
          //Dibuja aquí la vista
   }
}

Observa como el constructor utilizado tiene dos parámetros: El primero de tipo Context te permitirá acceder al contexto de aplicación, por ejemplo para utilizar recursos de esta aplicación. El segundo, de tipo AttributeSet, te permitirá acceder a los atributos de esta vista, cuando sea creada desde XML. El constructor es el lugar adecuado para crear todos los componentes de tu vista, pero cuidado, en este punto todavía no se conoce las dimensiones que tendrá.

Android realiza un proceso de varias pasadas para determinar el ancho y alto de cada vista dentro de un Layout. Cuando finalmente ha establecido las dimensiones de una vista llamará a su método onSizeChanged(). Nos indica como parámetros el ancho y alto asignado. En caso de tratarse de un reajuste de tamaños, por ejemplo una de las vistas del Layout desaparece y el resto tienen que ocupar su espacio, se nos pasará el ancho y alto anterior. Si es la primera vez que se llama al método estos parámetros valdrán 0.

El último método que siempre tendrás que reescribir es onDraw(). Es aquí donde tendrás que dibujar la vista.

 

Ejercicio paso a paso: Creación de una vista que pueda ser diseñada desde XML

Vamos a modificar la vista anterior para que pueda ser utilizada usando un diseño en XML:

1.     Modifica el código de la clase EjemploView. para que coincida con el siguiente (en negrita se resaltan las diferencias):

public class EjemploView extends View {
   private ShapeDrawable miImagen;
 
   public EjemploView(Context context, AttributeSet attrs) {
          super(context, attrs);
          miImagen = new ShapeDrawable(new OvalShape());
          miImagen.getPaint().setColor(0xff0000ff);
   }

   @Override protected void onSizeChanged(int ancho, int alto,
                int ancho_anterior, int alto_anterior){
          miImagen.setBounds(0, 0, ancho, alto);
   }
 
   @Override protected void onDraw(Canvas canvas) {
          miImagen.draw(canvas);
   }
}

 

La primera diferencia es la utilización de un constructor con el parámetro AttributeSet. Este constructor es imprescindible si quieres poder definir la vista en XML. También se ha añadido el métodoonSizeChanged, para que el óvalo se dibuje siempre ajustado al tamaño de la vista.

2.     Abre el fichero main.xml en modo Graphical Layout. En la ventana Outline selecciona con el botón derecho la raíz del Layout, que seguramente será un RelativeLayout. Serecciona la opción Change Layout… y como nuevo tipo indica LinearLayout (Vertical).

3.     En la paleta de vistas dentro de la última sección, observa como se ha añadido la nueva vista:

 

4.     Arrastra dos elementos de este tipo al Layout.

 

5.     Modifica la propiedades que define el ancho y alto de estas vistas, tal y como se muestra a continuación:

 

<LinearLayout>
<org.example.ejemplograficos.EjemploView
             android:id="@+id/ejemploView1"
             android:layout_width="200px"
             android:layout_height="100px" />
       <org.example.ejemplograficos.EjemploView
             android:id="@+id/ejemploView2"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent" />
       </LinearLayout>

NOTA: No utilices el valor "wrap_content". Nuestra vista no ha sido programada para soportar este tipo de valor

 

En la clase EjemploGraficosActivity  reemplaza setContentView(new EjemploView(this)) por setContentView(R.layout.main).

7.     Ejecuta la aplicación. El resultado ha de ser similar a:

       

{jcomments on}

Una vez que conocemos los rudimentos que nos permiten utilizar gráficos en Android vamos a aplicarlos a nuestro ejemplo.

Ejercicio paso a paso: Creando la actividad principal en Asteroides

Lo primero que necesitamos es crear una actividad que controle la pantalla del juego propiamente dicho. A esta actividad le llamaremos Juego.

1.     Abre el proyecto Asteroides.

2.     Lo primero que necesitamos es crear una actividad que controle la pantalla del juego propiamente dicho. Crea la clase Juego con el siguiente código.

public class Juego extends Activity {
   @Override public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.juego);
   }
}

3.     Necesitaremos un Layout para la pantalla del juego. Crea el fichero res/layout/juego.xml con el siguiente contenido:

<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    <org.example.asteroides.VistaJuego
          android:id="@+id/VistaJuego"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:focusable="true"
          android:background="@drawable/fondo" />
</LinearLayout>

Como puedes observar, cuando diseñamos un Layout en XML, no estamos limitados a escoger los elementos que tenemos en la paleta de vistas; vamos a utilizar vistas creadas por nosotros. En este ejemplo utilizaremos la vista org.example.asteroides.VistaJuego que será descrita más adelante. Va a ser esta vista la que lleve el peso de la aplicación

4.   Observa que se ha indicado el parámetro android:background asociado al recurso @drawable/fondo. Por lo tanto, tendremos que poner el recurso fondo.jpg en la carpeta correspondiente. Copia también los gráficos correspondientes a los asteroides, la nave y el misil a la carpeta res/drawable. Estos gráficos serán utilizados en los siguientes apartados. Los puedes encontrar en www.androidcurso.com.

5.  De momento no podremos ejecutar la aplicación hasta haber definido la clase VistaJuego.

La clase Gráfico

El juego que estamos desarrollando va a desplazar muchos tipos de gráficos por pantalla: asteroides, nave, misiles,… Dado que el comportamiento de todos estos elementos es muy similar, con el fin de reutilizar el código y mejorar su comprensión, vamos a crear una clase que represente un gráfico a desplazar por pantalla. A esta nueva clase le llamaremos Grafico y presentará las siguientes características. El elemento a dibujar será representado en un objeto Drawable. Como hemos visto, esta clase presenta una gran versatilidad, lo que nos permitirá trabajar con gráficos en bitmap (BitmapDrawable), vectoriales (ShapeDrawable), gráficos con diferentes representaciones (StateListDrawable), gráficos animados (AnimationDrawable),… Además un Grafico dispondrá de posición, velocidad de desplazamiento, ángulo de rotación y velocidad de rotación. Para finalizar, un gráfico que salga por uno de los extremos de la pantalla reaparecerá por el extremo contrario, tal y como ocurría en el juego original de Asteroides. 

Ejercicio paso a paso: La clase Gráfico

1. Crea una nueva clase Graficoen el proyecto Asteroides y copia el siguiente código

class Grafico {

      private Drawable drawable;   //Imagen que dibujaremos

      private double posX, posY;   //Posición

      private double incX, incY;   //Velocidad desplazamiento

      private int angulo, rotacion;//Ángulo y velocidad rotación

      private int ancho, alto;     //Dimensiones de la imagen

      private int radioColision;   //Para determinar colisión

       //Donde dibujamos el gráfico (usada en view.ivalidate)

      private View view;

       // Para determinar el espacio a borrar (view.ivalidate)

      public static final int MAX_VELOCIDAD = 20;

     

      public Grafico(View view, Drawable drawable){

            this.view = view;

            this.drawable = drawable;

            ancho = drawable.getIntrinsicWidth();  

            alto = drawable.getIntrinsicHeight();

            radioColision = (alto+ancho)/4;

      }

           public void dibujaGrafico(Canvas canvas){

            canvas.save();

            int x=(int) (posX+ancho/2);

            int y=(int) (posY+alto/2);

            canvas.rotate((float) angulo,(float) x,(float) y);

            drawable.setBounds((int)posX, (int)posY,
                                                                 (int)posX+ancho, (int)posY+alto);

            drawable.draw(canvas);

            canvas.restore();

            int rInval = (int) Math.hypot(ancho,alto)/2 + MAX_VELOCIDAD;

            view.invalidate(x-rInval, y-rInval, x+rInval, y+rInval);

      }

      public void incrementaPos(double factor){

            posX+=incX * factor;

            // Si salimos de la pantalla, corregimos posición

            if(posX<-ancho/2) {posX=view.getWidth()-ancho/2;}

            if(posX>view.getWidth()-ancho/2) {posX=-ancho/2;}

            posY+=incY * factor;

            if(posY<-alto/2) {posY=view.getHeight()-alto/2;}

            if(posY>view.getHeight()-alto/2) {posY=-alto/2;}

            angulo += rotacion * factor; //Actualizamos ángulo

      }



      public double distancia(Grafico g) {

            return Math.hypot(posX-g.posX, posY-g.posY);

      }



      public boolean verificaColision(Grafico g) {

            return(distancia(g) < (radioColision+g.radioColision));

      }

}

2.    Al principio de la clase hemos definido varios campos con el modificador private. Vamos a necesitar acceder a estos campos desde fuera de la clase, por lo que resulta necesario declarar los métodos get y set correspondientes. Vamos a realizar esta tarea de forma automática utilizando una herramienta de Eclipse. Sitúa el cursor al final de la clase (justo antes de la última llave) y pulsa con el botón derecho. Selecciona en el menú desplegable Source/Generate Getters and Setters…En la ventana de diálogo marca todos los campos (botón Sellect All) ypula OK. Eclipse hará el trabajo por nosotros.

 Nota sobre Java:En el tutorial Java Esencial / Encapsulamiento y visibilidad puedes aprender más sobre los métodos get y set. Lo encontrarás en la Web www.androidcurso.com.

 

 

La clase VistaJuego

Pasemos a describir la creación de VistaJuego, que como hemos indicado es la responsable de la ejecución el juego. En una primera versión solo se representarán los asteroides de forma estática:

Ejercicio paso a paso: La clase VistaJuego

1. Crea una nueva clase VistaJuego en el proyecto Asteroides y copia el siguiente código:

public class VistaJuego extends View {

      // //// ASTEROIDES //////

      private Vector Asteroides; // Vector con los Asteroides

      private int numAsteroides= 5; // Número inicial de asteroides

      private int numFragmentos= 3; // Fragmentos en que se divide



      public VistaJuego(Context context, AttributeSet attrs) {

            super(context, attrs);

            Drawable drawableNave, drawableAsteroide, drawableMisil;

            drawableAsteroide = context.getResources().getDrawable(
                                                                                  R.drawable.asteroide1);

            Asteroides = new Vector();

            for (int i = 0; i < numAsteroides; i++) {

                  Grafico asteroide = new Grafico(this, drawableAsteroide);

                  asteroide.setIncY(Math.random() * 4 - 2);

                  asteroide.setIncX(Math.random() * 4 - 2);

                  asteroide.setAngulo((int) (Math.random() * 360));

                  asteroide.setRotacion((int) (Math.random() * 8 - 4));

                  Asteroides.add(asteroide);

            }

      }



      @Override protected void onSizeChanged(int ancho, int alto,
                                                           int ancho_anter, int alto_anter) {

            super.onSizeChanged(ancho, alto, ancho_anter, alto_anter);

            // Una vez que conocemos nuestro ancho y alto.

            for (Grafico asteroide: Asteroides) {

                  asteroide.setPosX(Math.random()*
                                                                       (ancho-asteroide.getAncho()));

                  asteroide.setPosY(Math.random()*
                                                                       (alto-asteroide.getAlto()));

            }

      }



      @Override protected void onDraw(Canvas canvas) {

            super.onDraw(canvas);

            for (Grafico asteroide: Asteroides) {

                asteroide.dibujaGrafico(canvas);

            }

      }

}

Como ves se han declarado tres métodos. En el constructor creamos los asteroides e inicializamos su velocidad, ángulo y rotación. Sin embargo, resulta imposible iniciar su posición, dado que no conocemos el alto y ancho de la pantalla. Esta información será conocida cuando se llame a onSizeChanged(). Observa cómo en esta función los asteroides están situados de forma aleatoria. El último método, onDraw(), es el más importante de la clase View, dado que es el responsable de dibujar la vista.

2. Hemos creado una vista personalizada. No tendría demasiado sentido, pero podrá ser utilizada en cualquier otra aplicación que desarrolles. Visualiza el Layout juego.xml y observa como la nueva vista se integra perfectamente en el entorno de desarrollo.

 

3. Registra la actividad Juego en AndroidManifest.xml.

4. Ejecuta la aplicación. Has de ver cinco asteroides repartidos al azar por la pantalla

 

Introduciendo la nave en VistaJuego

El siguiente paso consiste en dibujar la nave que controlará el usuario para destruir los asteroides.

 

Práctica: Introduciendo la nave en VistaJuego

1. Declara las siguientes variables al comienzo de la clase VistaJuego:

// //// NAVE //////

   private Grafico nave;// Gráfico de la nave

   private int giroNave; // Incremento de dirección

   private float aceleracionNave; // aumento de velocidad

   // Incremento estándar de giro y aceleración

   private static final int PASO_GIRO_NAVE = 5;

   private static final float PASO_ACELERACION_NAVE = 0.5f;

Algunas de estas variables serán utilizadas en el siguiente capítulo.

2. En el constructor de la clase instancia la variable drawableNave de forma similar como se ha hecho en drawableAsteroide.

3. Inicializa también en el constructor la variable navede la siguiente forma:

            nave = new Grafico(this, drawableNave);

4. En el método onSiceChange() posiciona la nave justo en el centro de la vista.

5. En el método onDraw() dibuja la nave en el Canvas.

6. Ejecuta la aplicación. La nave ha de aparecer centrada:

 

7. Si cuando situamos los asteroides, alguno coincide con la posición de la nave, el jugador no tendrá ninguna opción de sobrevivir. Sería más interesante asegurarnos de que al posicionar los asteroides estos se encuentran a una distancia adecuada a la nave, y en caso contrario tratar de obtener otra posición. Para conseguirlo puedes utilizar el siguiente código en sustitución de las dos líneas asteroide.setPosX(…) y asteroide.setPosY(…).

do{

      asteroide.setPosX(Math.random()*(ancho-asteroide.getAncho()));

      asteroide.setPosY(Math.random()*(alto-asteroide.getAlto()));

} while(asteroide.distancia(nave) < (ancho+alto)/5);

Ejercicio paso a paso: Evitando que VistaJuego cambie su representación con el dispositivo en horizontal y en vertical

 

 

1. Ejecuta la aplicación

2. Cambia de orientación la pantalla del dispositivo. En el emulador se consigue pulsando la tecla Ctrl-F11.

3. Observa cómo cada vez, se reinicializa la vista, regenerando los asteroides. Esto nos impediría jugar de forma adecuada. Para solucionarlo edita AndroidManifet.xml. En la lengüeta Application selecciona la actividad Juego. En los parámetros de la derecha selecciona en Screen orientation: landscape.

4. Ejecuta de nuevo la aplicación. Observa como la actividad Juego será siempre representada en modo horizontal, de forma independiente a la posición del teléfono.

5. Abre de nuevo las propiedades de la actividad Juego. En Theme selecciona el valor @android:style/Theme.NoTitleBar.Fullscreen. Este tema visualizará la vista ocupando toda la pantalla, sin la barra de notificaciones ni el nombre de la aplicación.

6. Si en Theme pulsas el botón Browse… y seleccionas el botón circular System Resources puedes ver una lista de estilos definidos en el sistema.

NOTA. En algunas instalaciones esta lista puede que no te funcione.

7. Ejecuta la aplicación en un terminal real y verifica el resultado.

NOTA: en un emulador si cambias la orientación (Crtl-F11) esta cambiará igualmente. Se trata de un error de simulación, al no soportar esta configuración.

{jcomments on}

La clase Gráfico

El juego que estamos desarrollando va a desplazar muchos tipos de gráficos por pantalla: asteroides, nave, misiles,… Dado que el comportamiento de todos estos elementos es muy similar, con el fin de reutilizar el código y mejorar su comprensión, vamos a crear una clase que represente un gráfico a desplazar por pantalla. A esta nueva clase le llamaremos Grafico y presentará las siguientes características. El elemento a dibujar será representado en un objeto Drawable. Como hemos visto, esta clase presenta una gran versatilidad, lo que nos permitirá trabajar con gráficos en bitmap (BitmapDrawable), vectoriales (ShapeDrawable), gráficos con diferentes representaciones (StateListDrawable), gráficos animados (AnimationDrawable),… Además un Grafico dispondrá de posición, velocidad de desplazamiento, ángulo de rotación y velocidad de rotación. Para finalizar, un gráfico que salga por uno de los extremos de la pantalla reaparecerá por el extremo contrario, tal y como ocurría en el juego original de Asteroides. 

Ejercicio paso a paso: La clase Gráfico

1.     Crea una nueva clase Grafico en el proyecto Asteroides y copia el siguiente código

class Grafico {
       private Drawable drawable;   //Imagen que dibujaremos
       private int cenX, cenY;   //Posición el centro el gráfico
       private int ancho, alto;     //Dimensiones de la imagen
       private double incX, incY;   //Velocidad desplazamiento
       private double angulo, rotacion;//Ángulo y velocidad rotación
       private int radioColision;   //Para determinar colisión
       private int XAnterior, YAnterior;   //Posición anterior
       private int radioInval;   //Radio usado en invalidate()
       private View view;   //Usada en view.invalidate()
              
       public Grafico(View view, Drawable drawable){
             this.view = view;
             this.drawable = drawable;
             ancho = drawable.getIntrinsicWidth();  
             alto = drawable.getIntrinsicHeight();
             radioColision = (alto+ancho)/4;
             radioInval = (int) Math.hypot(ancho/2,alto/2);
       }
      
       public void dibujaGrafico(Canvas canvas){
             int x= cenX - ancho/2);
             int y= cenY - alto/2);
             drawable.setBounds(x, y, x+ancho, y+alto);   
             canvas.save();
             canvas.rotate((float)angulo,cenX,cenY);                                        drawable.draw(canvas);
             canvas.restore();
             view.invalidate(cenX-radioInval, cenY-radioInval, 
                            cenX+radioInval, cenY+radioInval);
             view.invalidate(xAnterior -radioInval, YAnterior-radioInval, 
                            xAnterior+radioInval, YAnterior+radioInval);
             xAnterior= cenX;
             yAnterior= cenY;
       }
 
       public void incrementaPos(double factor){
             cenX += incX * factor;
             cenY += incY* factor;
             angulo += rotacion * factor;
             // Si salimos de la pantalla, corregimos posición
             if(cenX<0)                   cenX=view.getWidth();
             if(cenX=view.getWidth())     cenX=0;
             if(cenY<0)                   cenY=view.getHeiht();
             if(cenY=view.getHeiht())     cenY=0;
      }
            
       public double distancia(Grafico g) {
             return Math.hypot(cenX-g.cenX, cenY-g.cenY-g.cenY);
       }
 
       public boolean verificaColision(Grafico g) {
             return (distancia(g) < (radioColision + g.radioColision));
       }
}

Cada objeto de la clase Grafico se caracteriza por situarse en unas coordenadas centrales (cenX, cenY). Por lo tanto su esquina superior derecha estará en las coordenadas (x, y) = (cenX - ancho/2, cenY - alto/2). Aunque un graficos pueden ser alargados (si alto diferente a ancho), a efecto de las colisiones, vamos a considerar que son círculos.El radio de este círculo se podría calcular como ancho/2  ó  alto/2, según tomáramos el radio mayor o el radio menor. Lo que hacemos es tomar una media de estos dos posibles radios: (ancho/2+alto/2)/2= (ancho+alto)/4. El método verificaColision()comprueba si hemos colisionado con otro gráfico. Para ello se comprueba si la distancia al otro Graficoes menor a la suma de los dos radios de colisión.

El método dibujaGrafico()se encarga de dibujar el Drawable del Grafico en un Canvas. Comienza indicando los límites donde se situará el Drawable, utilizando setBounds(). Luego, guarda la matriz de transformación del Canvas. A continuación, aplica una transformación de rotación según lo indicado en la variable anguloutilizando como eje de rotación (cenX, cenY). Se dibuja el Drawable en el Canvas y se recupera la matriz de transformación, para que la rotación introducida no se aplique en futuras operaciones con este Canvas.

Para finalizar hacemos dos llamadas al método invalidate() de la vista donde estamos dibujando el Grafico. Con este método informamos a la vista que tiene que ser redibujada. Para mejorar la eficiencia indicaremos solo el rectángulo que hemos modificado. De esta forma, la vista no tendrá que redibujarse en su totalidad. En un primer momento podríamos pensar que el rectángulo de invalidación coincide con el utilizado en setBounds(). Pero, hay que recordar que hemos realizado una rotación sobre el Drawable y, como puede verse en la ilustración de la izquierda, posiblemente el Drawable se salga de este rectángulo. Para resolver este problema vamos a aumentar el área de invalidación a un cuadrado con la mitad de su lado igual a la mitad de la diagonal de gráfico. Este valor es precalculado en la variable radioInval.

 

Hay que tener en cuenta que hemos desplazado el Drawable desde una posición anterior. Por lo tanto, también es necesario indicar a la vista que redibuje el área donde estaba antes el Grafico. Con este fin vamos a utilizar las varibles xAnterior y yAnterior.

Otro método interesante es incrementaPos() que es utilizado  para modificar la posición y ángulo del Grafico según la velocidad de translación (incX, incY) y la velocidad de rotación (rotacion). Este método tiene el párametro, factor, que permite ajustar esta velocidad. Con un valor igual a 1, tendremos una velocidad normal; si vale 2, el grafico se mueve al doble de velocidad. En el juego original de Asteroides, si un grafico salía por un lado de la pantalla, aparecía de nuevo por el lado opuesto. Este comportamiento es implementado en las últimas líneas del método

2.     Al principio de la clase hemos definido varios campos con el modificador private. Vamos a necesitar acceder a estos campos desde fuera de la clase, por lo que resulta necesario declarar los métodos get yset correspondientes. Vamos a realizar esta tarea de forma automática utilizando una herramienta de Eclipse. Sitúa el cursor al final de la clase (justo antes de la última llave) y pulsa con el botón derecho.Selecciona en el menú desplegable Source/Generate Getters and Setters… En la ventana de diálogo marca todos los campos (botón Sellect All) y pula OK. Eclipse hará el trabajo por nosotros.

Nota sobre Java: En el tutorial Java Esencial / Encapsulamiento y visibilidad puedes aprender más sobre los métodos get y set. Lo encontrarás en la Web www.androidcurso.com.

La clase VistaJuego

Pasemos a describir la creación de VistaJuego, que como hemos indicado es la responsable de la ejecución el juego. En una primera versión solo se representarán los asteroides de forma estática:

Ejercicio paso a paso: La clase VistaJuego

1. Crea una nueva clase VistaJuego en el proyecto Asteroides y copia el siguiente código:

public class VistaJuego extends View {

      // //// ASTEROIDES //////

      private Vector Asteroides; // Vector con los Asteroides

      private int numAsteroides= 5; // Número inicial de asteroides

      private int numFragmentos= 3; // Fragmentos en que se divide



      public VistaJuego(Context context, AttributeSet attrs) {

            super(context, attrs);

            Drawable drawableNave, drawableAsteroide, drawableMisil;

            drawableAsteroide = context.getResources().getDrawable(
                                                                                  R.drawable.asteroide1);

            Asteroides = new Vector();

            for (int i = 0; i < numAsteroides; i++) {

                  Grafico asteroide = new Grafico(this, drawableAsteroide);

                  asteroide.setIncY(Math.random() * 4 - 2);

                  asteroide.setIncX(Math.random() * 4 - 2);

                  asteroide.setAngulo((int) (Math.random() * 360));

                  asteroide.setRotacion((int) (Math.random() * 8 - 4));

                  Asteroides.add(asteroide);

            }

      }



      @Override protected void onSizeChanged(int ancho, int alto,
                                                           int ancho_anter, int alto_anter) {

            super.onSizeChanged(ancho, alto, ancho_anter, alto_anter);

            // Una vez que conocemos nuestro ancho y alto.

            for (Grafico asteroide: Asteroides) {

                  asteroide.setCenX((int) Math.random()ancho);

                  asteroide.setCenY((int) Math.random()alto);

            }

      }



      @Override protected void onDraw(Canvas canvas) {

            super.onDraw(canvas);

            for (Grafico asteroide: Asteroides) {

                asteroide.dibujaGrafico(canvas);

            }

      }

}

Como ves se han declarado tres métodos. En el constructor creamos los asteroides e inicializamos su velocidad, ángulo y rotación. Sin embargo, resulta imposible iniciar su posición, dado que no conocemos el alto y ancho de la pantalla. Esta información será conocida cuando se llame a onSizeChanged(). Observa cómo en esta función los asteroides están situados de forma aleatoria. El último método, onDraw(), es el más importante de la clase View, dado que es el responsable de dibujar la vista.

2. Hemos creado una vista personalizada. No tendría demasiado sentido, pero podrá ser utilizada en cualquier otra aplicación que desarrolles. Visualiza el Layout juego.xml y observa como la nueva vista se integra perfectamente en el entorno de desarrollo.

 

 

3. Registra la actividad Juego en AndroidManifest.xml.

4. Ejecuta la aplicación. Has de ver cinco asteroides repartidos al azar por la pantalla

 

Introduciendo la nave en VistaJuego

El siguiente paso consiste en dibujar la nave que controlará el usuario para destruir los asteroides.

Práctica: Introduciendo la nave en VistaJuego

1. Declara las siguientes variables al comienzo de la clase VistaJuego:

// //// NAVE //////

   private Grafico nave;// Gráfico de la nave

   private int giroNave; // Incremento de dirección

   private float aceleracionNave; // aumento de velocidad

   // Incremento estándar de giro y aceleración

   private static final int PASO_GIRO_NAVE = 5;

   private static final float PASO_ACELERACION_NAVE = 0.5f;

Algunas de estas variables serán utilizadas en el siguiente capítulo.

 

2. En el constructor de la clase instancia la variable drawableNave de forma similar como se ha hecho en drawableAsteroide.

3. Inicializa también en el constructor la variable navede la siguiente forma:

            nave = new Grafico(this, drawableNave);

4. En el método onSiceChange() posiciona la nave justo en el centro de la vista.

5. En el método onDraw() dibuja la nave en el Canvas.

6. Ejecuta la aplicación. La nave ha de aparecer centrada:

 

7. Si cuando situamos los asteroides, alguno coincide con la posición de la nave, el jugador no tendrá ninguna opción de sobrevivir. Sería más interesante asegurarnos de que al posicionar los asteroides estos se encuentran a una distancia adecuada a la nave, y en caso contrario tratar de obtener otra posición. Para conseguirlo puedes utilizar el siguiente código en sustitución de las dos líneas asteroide.setPosX(…) y asteroide.setPosY(…).

do{

      asteroide.setPosX(Math.random()*(ancho-asteroide.getAncho()));

      asteroide.setPosY(Math.random()*(alto-asteroide.getAlto()));

} while(asteroide.distancia(nave) < (ancho+alto)/5);

Ejercicio paso a paso: Evitando que VistaJuego cambie su representación con el dispositivo en horizontal y en vertical

 

 

1. Ejecuta la aplicación

2. Cambia de orientación la pantalla del dispositivo. En el emulador se consigue pulsando la tecla Ctrl-F11.

3. Observa cómo cada vez, se reinicializa la vista, regenerando los asteroides. Esto nos impediría jugar de forma adecuada. Para solucionarlo edita AndroidManifet.xml. En la lengüeta Application selecciona la actividad Juego. En los parámetros de la derecha selecciona en Screen orientation: landscape.

4. Ejecuta de nuevo la aplicación. Observa como la actividad Juego será siempre representada en modo horizontal, de forma independiente a la posición del teléfono.

5. Abre de nuevo las propiedades de la actividad Juego. En Theme selecciona el valor @android:style/Theme.NoTitleBar.Fullscreen. Este tema visualizará la vista ocupando toda la pantalla, sin la barra de notificaciones ni el nombre de la aplicación.

6. Si en Theme pulsas el botón Browse… y seleccionas el botón circular System Resources puedes ver una lista de estilos definidos en el sistema.

NOTA. En algunas instalaciones esta lista puede que no te funcione.

7. Ejecuta la aplicación en un terminal real y verifica el resultado.

NOTA: en un emulador si cambias la orientación (Crtl-F11) esta cambiará igualmente. Se trata de un error de simulación, al no soportar esta configuración.

{jcomments on}

La clase VistaJuego

Pasemos a describir la creación de VistaJuego, que como hemos indicado es la responsable de la ejecución el juego. En una primera versión solo se representarán los asteroides de forma estática:

Ejercicio paso a paso: La clase VistaJuego

1.     Crea una nueva clase VistaJuego en el proyecto Asteroides y copia el siguiente código:

public class VistaJuego extends View {
       // //// ASTEROIDES //////
       private Vector Asteroides; // Vector con los Asteroides
       private int numAsteroides = 5; // Número inicial de asteroides
       private int numFragmentos = 3; // Fragmentos en que se divide
 
       public VistaJuego(Context context, AttributeSet attrs) {
             super(context, attrs);
             Drawable drawableNave, drawableAsteroide, drawableMisil;
             drawableAsteroide = context.getResources().getDrawable( 
                                                                                             R.drawable.asteroide1);
             Asteroides = new Vector();
             for (int i = 0; i < numAsteroides; i++) {
                    Grafico asteroide = new Grafico(this, drawableAsteroide);
                    asteroide.setIncY(Math.random() * 4 - 2);
                    asteroide.setIncX(Math.random() * 4 - 2);
                    asteroide.setAngulo((int) (Math.random() * 360));
                    asteroide.setRotacion((int) (Math.random() * 8 - 4));
                    Asteroides.add(asteroide);
             }
       }
 
       @Override protected void onSizeChanged(int ancho, int alto, 
                                                                   int ancho_anter, intalto_anter) {
             super.onSizeChanged(ancho, alto, ancho_anter, alto_anter);
             // Una vez que conocemos nuestro ancho y alto.
             for (Grafico asteroide: Asteroides) {
                    asteroide.setCenX((int) Math.random()*ancho);
                    asteroide.setCenY((int) Math.random()*alto);
             }
       }
 
       @Override protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
             for (Grafico asteroide: Asteroides) {
                 asteroide.dibujaGrafico(canvas);
             }
       }
}

Como ves se han declarado tres métodos. En el constructor creamos los asteroides e inicializamos su velocidad, ángulo y rotación. Sin embargo, resulta imposible iniciar su posición, dado que no conocemos el alto y ancho de la pantalla. Esta información será conocida cuando se llame a onSizeChanged(). Observa cómo en esta función los asteroides están situados de forma aleatoria. El último método,onDraw(), es el más importante de la clase View, dado que es el responsable de dibujar la vista.

2.     Hemos creado una vista personalizada. No tendría demasiado sentido, pero podrá ser utilizada en cualquier otra aplicación que desarrolles. Visualiza el Layout juego.xml y observa como la nueva vista se integra perfectamente en el entorno de desarrollo.

 

3.     Registra la actividad Juego en AndroidManifest.xml.

4. Asocia al atributo onClick del botón Jugar del Layout main el método lanzarJuego. Crea el métodolanzarJuego dentro de la clase Asteroides, de forma similar al método lanzarAcercaDe, pero esta vez arrancando la actividad Juego.

5.    Ejecuta la aplicación. Has de ver cinco asteroides repartidos al azar por la pantalla.

 

Introduciendo la nave en VistaJuego

El siguiente paso consiste en dibujar la nave que controlará el usuario para destruir los asteroides.

 

Práctica: Introduciendo la nave en VistaJuego

 

1. Declara las siguientes variables al comienzo de la clase VistaJuego:

// //// NAVE //////

   private Grafico nave;// Gráfico de la nave

   private int giroNave; // Incremento de dirección

   private float aceleracionNave; // aumento de velocidad

   // Incremento estándar de giro y aceleración

   private static final int PASO_GIRO_NAVE = 5;

   private static final float PASO_ACELERACION_NAVE = 0.5f;

Algunas de estas variables serán utilizadas en el siguiente capítulo.

2. En el constructor de la clase instancia la variable drawableNave de forma similar como se ha hecho en drawableAsteroide.

3. Inicializa también en el constructor la variable navede la siguiente forma:

            nave = new Grafico(this, drawableNave);

4. En el método onSiceChange() posiciona la nave justo en el centro de la vista.

5. En el método onDraw() dibuja la nave en el Canvas.

6. Ejecuta la aplicación. La nave ha de aparecer centrada:

 

7. Si cuando situamos los asteroides, alguno coincide con la posición de la nave, el jugador no tendrá ninguna opción de sobrevivir. Sería más interesante asegurarnos de que al posicionar los asteroides estos se encuentran a una distancia adecuada a la nave, y en caso contrario tratar de obtener otra posición. Para conseguirlo puedes utilizar el siguiente código en sustitución de las dos líneas asteroide.setPosX(…) y asteroide.setPosY(…).

do{

      asteroide.setPosX(Math.random()*(ancho-asteroide.getAncho()));

      asteroide.setPosY(Math.random()*(alto-asteroide.getAlto()));

} while(asteroide.distancia(nave) < (ancho+alto)/5);

Ejercicio paso a paso: Evitando que VistaJuego cambie su representación con el dispositivo en horizontal y en vertical

 

1. Ejecuta la aplicación

2. Cambia de orientación la pantalla del dispositivo. En el emulador se consigue pulsando la tecla Ctrl-F11.

3. Observa cómo cada vez, se reinicializa la vista, regenerando los asteroides. Esto nos impediría jugar de forma adecuada. Para solucionarlo edita AndroidManifet.xml. En la lengüeta Application selecciona la actividad Juego. En los parámetros de la derecha selecciona en Screen orientation: landscape.

4. Ejecuta de nuevo la aplicación. Observa como la actividad Juego será siempre representada en modo horizontal, de forma independiente a la posición del teléfono.

5. Abre de nuevo las propiedades de la actividad Juego. En Theme selecciona el valor @android:style/Theme.NoTitleBar.Fullscreen. Este tema visualizará la vista ocupando toda la pantalla, sin la barra de notificaciones ni el nombre de la aplicación.

6. Si en Theme pulsas el botón Browse… y seleccionas el botón circular System Resources puedes ver una lista de estilos definidos en el sistema.

NOTA. En algunas instalaciones esta lista puede que no te funcione.

7. Ejecuta la aplicación en un terminal real y verifica el resultado.

NOTA: en un emulador si cambias la orientación (Crtl-F11) esta cambiará igualmente. Se trata de un error de simulación, al no soportar esta configuración.

{jcomments on}

Introduciendo la nave en VistaJuego

El siguiente paso consiste en dibujar la nave que controlará el usuario para destruir los asteroides.

 

Práctica: Introduciendo la nave en VistaJuego

1.     Declara las siguientes variables al comienzo de la clase VistaJuego:

 

// //// NAVE //////
   private Grafico nave; // Gráfico de la nave
   private int giroNave; // Incremento de dirección
   private float aceleracionNave; // aumento de velocidad
   // Incremento estándar de giro y aceleración
   private static final int PASO_GIRO_NAVE = 5;
   private static final float PASO_ACELERACION_NAVE = 0.5f;

Algunas de estas variables serán utilizadas en el siguiente capítulo.

 

2.     En el constructor de la clase instancia la variable drawableNave de forma similar como se ha hecho endrawableAsteroide.

3.     Inicializa también en el constructor la variable nave de la siguiente forma:

nave = new Grafico(this, drawableNave);

4.     En el método onSiceChange() posiciona la nave justo en el centro de la vista.

5.     En el método onDraw() dibuja la nave en el Canvas.

6.     Ejecuta la aplicación. La nave ha de aparecer centrada:

7.     Si cuando situamos los asteroides, alguno coincide con la posición de la nave, el jugador no tendrá ninguna opción de sobrevivir. Sería más interesante asegurarnos de que al posicionar los asteroides estos se encuentran a una distancia adecuada a la nave, y en caso contrario tratar de obtener otra posición. Para conseguirlo puedes utilizar el siguiente código en sustitución de las dos líneas asteroide.setCenX(…) y asteroide.setCenY(…).

do {
       asteroide.setCenX(int)Math.random ()*ancho);
       asteroide.setCenY(int)Math.random ()*alto);
} while(asteroide.distancia(nave) < (ancho+alto)/5);

 

Ejercicio paso a paso: Evitando que VistaJuego cambie su representación con el dispositivo en horizontal y en vertical

1.     Ejecuta la aplicación

2.     Cambia de orientación la pantalla del dispositivo. En el emulador se consigue pulsando la tecla Ctrl-F11.

3.     Observa cómo cada vez, se reinicializa la vista, regenerando los asteroides. Esto nos impediría jugar de forma adecuada. Para solucionarlo edita AndroidManifet.xml. En la lengüeta Application selecciona la actividad Juego. En los parámetros de la derecha selecciona en Screen orientation: landscape.

4.     Ejecuta de nuevo la aplicación. Observa como la actividad Juego será siempre representada en modo horizontal, de forma independiente a la posición del teléfono.

5.     Abre de nuevo las propiedades de la actividad Juego. En Theme selecciona el valor@android:style/Theme.NoTitleBar.Fullscreen. Este tema visualizará la vista ocupando toda la pantalla, sin la barra de notificaciones ni el nombre de la aplicación.

6.     Si en Theme pulsas el botón Browse… y seleccionas el botón circular System Resources puedes ver una lista de estilos definidos en el sistema.

 

NOTA. En algunas instalaciones esta lista puede que no te funcione.

7.     Ejecuta la aplicación en un terminal real y verifica el resultado.

NOTA: en un emulador si cambias la orientación (Crtl-F11) esta cambiará igualmente. Se trata de un error de simulación, al no soportar esta configuración.

 

{jcomments on}

La versión original del juego Asteroides se ejecutaba sobre ordenadores con escasa potencia gráfica. Tal y como puedes ver a continuación, nuestra nave se representaba con un simple triángulo.

 

Dado que cuando hemos diseñado la clase Grafico, la representación del mismo la hemos delegado en un objeto Drawable, va a resultar muy fácil cambiar los gráficos de la aplicación para que tengan una apariencia más retro. Simplemente utilizando la subclase de Drawable, ShapeDrawable, en lugar de BitmapDrawable para cambiar la forma de dibujar los gráficos

Ejercicio paso a paso: Representación vectorial de los Asteroides

1.     Abre la clase VistaJuego.

2.     En el constructor reemplaza la línea:

drawableAsteroide = context.getResources().getDrawable(                                                                  
R.drawable.asteroide1);

por el siguiente código:        

SharedPreferences pref = context.getSharedPreferences( 
   "org.example.asteroides_preferences", Context.MODE_PRIVATE);
if (pref.getString("graficos", "1").equals("0")) {
       Path pathAsteroide = new Path();
       pathAsteroide.moveTo((float) 0.3, (float) 0.0);
       pathAsteroide.lineTo((float) 0.6, (float) 0.0);
       pathAsteroide.lineTo((float) 0.6, (float) 0.3);
       pathAsteroide.lineTo((float) 0.8, (float) 0.2);
       pathAsteroide.lineTo((float) 1.0, (float) 0.4);
       pathAsteroide.lineTo((float) 0.8, (float) 0.6);
       pathAsteroide.lineTo((float) 0.9, (float) 0.9);
       pathAsteroide.lineTo((float) 0.8, (float) 1.0);
       pathAsteroide.lineTo((float) 0.4, (float) 1.0);
       pathAsteroide.lineTo((float) 0.0, (float) 0.6);
       pathAsteroide.lineTo((float) 0.0, (float) 0.2);
       pathAsteroide.lineTo((float) 0.3, (float) 0.0);
       ShapeDrawable dAsteroide = new ShapeDrawable(
                                                             new PathShape(pathAsteroide, 1, 1));
       dAsteroide.getPaint().setColor(Color.WHITE);
       dAsteroide.getPaint().setStyle(Style.STROKE);
       dAsteroide.setIntrinsicWidth(50);
       dAsteroide.setIntrinsicHeight(50);
       drawableAsteroide = dAsteroide;
       setBackgroundColor(Color.BLACK);
} else {
       drawableAsteroide = context.getResources().getDrawable( 
                                                                         R.drawable.asteroide1);
}

Lo primero que hace este código es consultar en las preferencias para ver si el usuario ha escogido gráficos vectoriales. En caso negativo se realizará la misma inicialización de drawableAsteroide que teníamos antes. En caso afirmativo comenzamos creando la variable pathAsteroide de la clase Path. En este objeto se introducen todas las órdenes de dibujo necesarias para dibujar un asteroide. Luego se crea la variable dAsteroide de la clase ShapeDrawable para crea un drawable a partir del path. Los últimos dos parámetros (...,1,1) significan el valor de escala aplicado al eje x y al eje y. Luego se indica el color y el estilo del pincel e indicamos el alto y ancho por defecto del drawable. Finalmente asignamos el objeto creado a drawableAsteroide.

3.     Ejecuta la aplicación y selecciona el tipo de gráficos adecuado en las preferencias.

Práctica: Representación vectorial de la nave

Como habrás comprobado en el ejercicio anterior la nave se representa siempre utilizando un fichero png. En esta práctica has de intentar que también pueda representarse vectorialmente.

1. Crea un nuevo objeto de la clase Path para representar la nave dentro de la sección if introducida en el ejercicio anterior. Como puedes ver en la ilustración siguiente ha de ser un simple triángulo. Como el ángulo de rotación inicial es cero, la nave ha de mirar a la derecha.

2. Crea un nuevo ShapeDrawable a partir de Path anterior. Unas dimensiones adecuadas para que la nave pueden ser 20 de ancho y 15 de alto.

3. Inicializa la variable drawableNave de forma adecuada.

{jcomments on}

El entorno de programación Android incorpora tres mecanismos para crear animaciones en nuestras aplicaciones:

  • La clase  AnimationDrawable : Permite crear drawables que reproducen una animación fotograma a fotograma. Se ha descrito su uso en la sección de Drawables.
  • Animaciones Tween: Permiten crear efectos de translación, rotación, zoom y alfa a cualquiera vista de nuestra aplicación.
  • Animaciones de propiedades: Nuevo mecanismo incorporado en Android 3.0. Permite animar cualquier propiedad de cualquier objeto, sea una vista o no. Además modifica el objeto en sí, no solamente cambia su representación en pantalla como ocurre en un animación Tween.

Las siguientes secciones describen con más detalle estos tipos de animación.

{jcomments on}

Animaciones Tween

Una “animación tween” puede realizar series de transformaciones simples (posición, tamaño, rotación y transparencia) en el contenido de un objeto View. Por ejemplo, si tienes un objeto TextView puedes moverlo, rotarlo, aumentarlo, disminuirlo o cambiarle la transparencia al texto.

La secuencia de órdenes que define la “animación tween” puede estar escrita mediante xml o código, pero es recomendable el fichero xml, al ser mas legible, reutilizable e intercambiable.

Las instrucciones de la animación definen las transformaciones que quieres que ocurran, cuando ocurrirán y cuando tiempo tardaran en completarse. Las transformaciones pueden ser secuenciales o simultáneas. Cada tipo de transformación tiene unos parámetros específicos, y también existen unos parámetros comunes a todas las transformaciones, como el tiempo que durarán y el tiempo de inicio.

El fichero XML que define a la animación debe pertenecer al directorio res/anim/en tu proyecto Android. El archivo debe tener solo un único elemento raíz: este debe ser uno de los siguientes:,<translate>, <rotate>, <scale>, <alpha> o elemento <set> que puede contener grupos de estos elementos (además de otro <set>). Por defecto, todas las instrucciones de animación ocurren a partir del instante inicial. Si quieres que una animación comience más tarde debes especificar el atributo startOffset.

Ejercicio paso a paso: Creación de una animación Tween para animar una vista

1.     Crea un nuevo proyecto con nombre AnimacionTween.

2.     Crea el fichero res/anim/animacion.xml y pega el siguiente código:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="2000"
        android:fromXScale="2.0"
        android:fromYScale="2.0"
        android:toXScale="1.0"
        android:toYScale="1.0" />
    <rotate
        android:startOffset="2000"
        android:duration="2000"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"/>
    <translate
        android:startOffset="4000"
        android:duration="2000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="50"
        android:toYDelta="100" />
    <alpha
        android:startOffset="4000"
        android:duration="2000"
        android:fromAlpha="1"
        android:toAlpha="0" />  
</set>

3.     Abre el fichero res/Layout/main.xml y añade el siguiente atributo a la vista de tipo TextView:

android:id="@+id/textView"

4.     Abre la actividad del proyecto y añade las líneas marcadas en negrita al método OnCreate().

@Override public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);   
       TextView texto = (TextView) findViewById(R.id.textView);
       Animation animacion = AnimationUtils.loadAnimation(this, 
                                                                                             R.anim.animacion);
       texto.startAnimation(animacion);
}

5.     Ejecuta la aplicación.

Como podrás ver, el TextView comienza haciéndose más pequeño (etiqueta <scale>), después rota sobre sí mismo (etiqueta <rotate>) y finalmente, se desplaza (etiqueta <translate>) a la vez que se hace transparente (etiqueta <alpha>). Al finalizar la animación, vuelve a su posición y estado inicial, sin importar donde ni como haya acabado.

Recursos adicionales: Lista de etiquetas de las animaiones tween y sus atributos

Los siguientes atributos son aplicables a todas las transformaciones:

startOffset – Instante inicial de la transformación en milisegundos.

duration – duración de la transformación en milisegundos.

repeatCount – número de repeticiones adicionales de la animació

interpolator– en lugar de realizar una transformación lineal se aplica algún tipo de interpolación. Alguno de los valores posibles son:

accelerate_decelerate_interpolator, accelerate_interpolator, anticipate_interpolator, anticipate_overshoot_interpolator, bounce_interpolator, cycle_interpolator, decelerate_interpolator, linear_interpolator, overshoot_interpolator

Lista de las transformaciones con sus atributos específicos:

<translate> – Desplaza la vista

fromXDelta, toXDelta – Valor inicial y final del desplazamiento en eje X

fromYDelta, toYDelta – Valor inicial y final del desplazamiento en eje Y.

<rotate> – Rota la vista.

fromDegrees, toDegrees – Valor inicial y final en grados de la rotación en grados. Si quieres un giro completo en sentido antihorario pon de 0 a 360, y si lo quieres en sentido horario, de 360 a 0 o de 0 a -360. Si quieres dos giros pon de 0 a 720.

pivotX, pivotY– Punto sobre el que se realizará el giro. Este quedará fijo en la pantalla.

<scale> – Cambia el tamaño de la vista

fromXScale,toXScale – Valor inicial y final para la escala del eje X (0.5=50%, 1=100%)

fromYScale, toYScale – Valor inicial y final para la escala del eje Y

pivotX, pivotY– Punto sobre el que se realizará el zoom. Este quedará fijo en la pantalla.

<alpha> – Cambia la opacidad de la vista

fromAlpha, toAlpha – Valor inicial y final de la opacidad

 

Práctica: Introduciendo animaciones en Asteroides

En esta práctica has de conseguir que los diferentes elementos del Layout inicial de Asteroides vayan apareciendo uno tras otro con diferentes efectos.

 

1. Abre el proyecto Asteroides.

2. Crea una nueva animación con nombre giro_con_zoom.xml. Ha de durar dos segundos y de forma simultánea ha de hacer un zoom de escala 3 a 1 y un giro de dos vueltas (720º). El punto de anclaje de la rotación y el zoom ha de ser el centro de la vista.

3. Selecciona el Layout main.xml y pon un id al TextView correspondiente al título.

4. En la actividad inicial de Asteroides, crea un objeto correspondiente a este TextView y aplícale la animación anterior.

5. Crea una nueva animación con nombre aparecer.xml. Ha de comenzar a los dos segundos, durar un segundo y modificar el valor de alpha de 0 hasta 1.

6. Aplica esta animación al primer botón.

7. Crea una nueva animación con nombre desplazamiento_derecha.xml. Ha de comenzar a los tres segundos, durar un segundo y modificar el valor de desplazamiento x de 400 hasta 0. Prueba también algún tipo de interpolación

8. Aplica esta animación al segundo botón.

9. Si dispones de tiempo crea dos nuevas animaciones a tu gusto y aplícalas al tercer y cuarto botón.

10. Aplica la animación giro_con_zoom.xmlal botón “Acerca de” cuando sea pulsado. Observa como al lanzar la nueva actividad AcercaDe, la actividad principal continua ejecutándose.

 

{jcomments on}

Animaciones de propiedades

A partir de la versión 3.0 de Android (nivel de API 11) se han incorporado un nuevo tipo de animaciones. A diferencia de las animaciones Tween que solo es aplicable a vistas, una animación de propiedades puede animar cualquier tipo de objetos. Además, no está restringido a las cuatro transformaciones antes vistas, podemos animar cualquier propiedad del objeto. Por ejemplo, podemos hacer una animación que cambie progresivamente el color de fondo de una vista.

Otra diferencia con respecto a las animaciones Tween, es que estas solo modifican la forma en que la vista es representada, pero no sus propiedades. Por ejemplo, si aplicas una animación Tween para que un texto se desplace por la pantalla, se visualizará correctamente, pero al acabar la animación el texto estará en el lugar inicial. Lo que te obligará a implementar tu propia lógica para manejar este cambio de posición. En una animación de propiedades estará cambiando el objeto en sí, no solamente cómo se representa.

     Desventajas de las animaciones Tween:

  • Solo podemos animar objetos de la clase View.
  • Está limitado a estas cuatro transformaciones y no puede aplicarse a otros aspectos como cambiar el color de fondo.
  • Solo modifica la forma en que la vista es representada, pero no sus propiedades en si.

 

Desventajas de las animaciones de propiedades:

  • Solo disponible a partir de la versión 3.0. En la actualidad existen muy pocos dispositivos que la soporten.
  • Requiere más tiempo en inicializarse y hay que escribir más código

 

Para aprende más sobre este tipo de animación es puedes leer la siguiente sección de Android Developers: Property Animation

{jcomments on}