creando una aplicación de androi1

18
Creando una aplicación de Android: mejoras Bienvenidos a la quinta parte de la serie de entradas en el ciclo Creando una aplicación de Android. Si aún no lo has hecho, comienza desde la primera entrada mostrada en el menú inmediatamente superior. En esta entrada vamos a continuar con el resultado de la entrada anterior, en la que habíamos acabado la lógica de movimientos y las colisiones. Añadiremos funcionalidades durante esta entrada y la siguiente para acabar la aplicación. Para esta entrada añadiremos vibración del dispositivo, reproducción de sonidos y uso del acelerómetro para los movimientos de las raquetas. ¡Comenzamos! Importante: esta entrada va a basarse en los códigos creados en las anteriores entradas del ciclo al que pertenece. Tienes disponible pinchando aquí (MD5: 31b264db2de4125fa01c52d4d169bc99) el proyecto de Eclipse con todo lo hecho hasta ahora, el cual puedes importar a tu Eclipse y comenzar a trabajar. El Android SDK incluye una gran cantidad de clases que hace muy sencillo el uso de los sensores del dispositivo, la vibración, la reproducción de audio… Para mostrar cómo de sencillo es, comenzamos añadiéndole vibración a nuestra aplicación. Comenzamos abriendo nuestro Eclipse. Vibración Para que una aplicación pueda vibrar, ya que necesita acceder a un elemento del sistema, necesita que el usuario acepte el uso de este elemento. Para ello tenemos que añadir al AndroidManifest.xml que queremos usar vibración. Tenemos dos formas, la primera es añadiendo <uses-sdk android:minSdkVersion=”7″ /> justo detrás de la etiqueta <manifest … >. La segunda forma consiste en ir a la pestaña Permissions del manifest, pulsar en Add, elegir Uses Permission y aceptar, y luego escribir android.permission.VIBRATE en el cuadro de la derecha. Lo siguiente es programar el uso del vibrador. El uso de la vibración está definido por un Context, por lo que vamos a necesitar usar el contexto de nuestra aplicación para poder obtener el vibrador y posteriormente hacerlo vibrar. La vibración la vamos a realizar solamente cuando la Bola rebote en una de las dos Raquetas. Si recordamos las entradas anteriores, nuestra Bola y nuestra Raqueta tenían una función llamada puedoMover(), heredada de su superclase ElementoPong, que decía si se podía mover este elemento sin salirse de la pantalla. Sabemos que cuando una bola rebota es porque se ha chocado con algo. Si se ha chocado con algo pero aún se puede mover por

Upload: mane-rex

Post on 11-Nov-2015

227 views

Category:

Documents


2 download

DESCRIPTION

desarrollo de app

TRANSCRIPT

  • Creando una aplicacin de Android: mejoras

    Bienvenidos a la quinta parte de la serie de entradas en el ciclo Creando una aplicacin de

    Android. Si an no lo has hecho, comienza desde la primera entrada mostrada en el men

    inmediatamente superior.

    En esta entrada vamos a continuar con el resultado de la entrada anterior, en la que

    habamos acabado la lgica de movimientos y las colisiones. Aadiremos funcionalidades

    durante esta entrada y la siguiente para acabar la aplicacin.

    Para esta entrada aadiremos vibracin del dispositivo, reproduccin de sonidos y uso del

    acelermetro para los movimientos de las raquetas.

    Comenzamos!

    Importante: esta entrada va a basarse en los cdigos creados en las

    anteriores entradas del ciclo al que pertenece. Tienes disponible pinchando aqu (MD5:

    31b264db2de4125fa01c52d4d169bc99) el proyecto de Eclipse con todo lo hecho hasta

    ahora, el cual puedes importar a tu Eclipse y comenzar a trabajar.

    El Android SDK incluye una gran cantidad de clases que hace muy sencillo el uso de los

    sensores del dispositivo, la vibracin, la reproduccin de audio Para mostrar cmo de sencillo es, comenzamos aadindole vibracin a nuestra aplicacin. Comenzamos

    abriendo nuestro Eclipse.

    Vibracin

    Para que una aplicacin pueda vibrar, ya que necesita acceder a un elemento del sistema,

    necesita que el usuario acepte el uso de este elemento. Para ello tenemos que aadir al

    AndroidManifest.xml que queremos usar vibracin. Tenemos dos formas, la primera es

    aadiendo justo detrs de la etiqueta . La segunda forma consiste en ir a la pestaa Permissions del manifest, pulsar en Add, elegir

    Uses Permission y aceptar, y luego escribir android.permission.VIBRATE en el cuadro de

    la derecha. Lo siguiente es programar el uso del vibrador.

    El uso de la vibracin est definido por un Context, por lo que vamos a necesitar usar el

    contexto de nuestra aplicacin para poder obtener el vibrador y posteriormente hacerlo

    vibrar. La vibracin la vamos a realizar solamente cuando la Bola rebote en una de las dos

    Raquetas. Si recordamos las entradas anteriores, nuestra Bola y nuestra Raqueta tenan una

    funcin llamada puedoMover(), heredada de su superclase ElementoPong, que deca si se

    poda mover este elemento sin salirse de la pantalla. Sabemos que cuando una bola rebota

    es porque se ha chocado con algo. Si se ha chocado con algo pero an se puede mover por

  • la pantalla significa que, por eliminacin, se ha chocado con una raqueta. Ya tenemos

    planteado lo que vamos a hacer, as que hagmoslo.

    Primero vamos a aadir un nuevo atributo a la clase BolaMoveThread de la siguiente

    forma:

    1 private Vibrator v = null;

    Este ser nuestro vibrador. Ahora necesitamos inicializarle, puesto que est a null y si lo

    usramos ahora la aplicacin se morira debido a un NullPointerException (de ah que se

    inicialice a null, para debuggearlo ms fcil). Para inicializarle necesitamos el Context de la

    aplicacin, de modo que al constructor de BolaMoveThread le vamos a pasar el Context,

    quedando as:

    1 public BolaMoveThread(Bola bola, Raqueta izda, Raqueta dcha,

    2 Rect screen, Context context) {

    3 this.bola = bola;

    4 this.raquetaIzda = izda;

    5 this.raquetaDcha = dcha;

    6 this.screen = screen;

    7 this.run = false;

    8 this.speed = 1;

    9 this.v =

    (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);

    10 }

    Faltara cambiar la forma en que se crea este Thread de la siguiente forma (en el mtodo

    surfaceCreated() del PongGameView:

    1 bolaThread = new BolaMoveThread((Bola)bola, (Raqueta)raquetaIzda,

    2 (Raqueta)raquetaDcha, new Rect(0,0,getWidth(),getHeight()),

    3 this.getContext());

    Por ltimo slo queda hacer que vibre, para ello vamos a modificar el cdigo del run() del

    BolaMoveThread de la siguiente forma:

    1 @Override

    2 public void run() {

    3 while(run) {

    4 try {

    5 Thread.sleep(10);

    6 } catch (InterruptedException e) {

    7 e.printStackTrace();

    8 }

  • 9 if(!bola.puedoMover(speed, speed, screen,

    raquetaIzda.getRectElemento(), raquetaDcha.getRectElemento())) {

    10 bola.rebota(speed, speed, screen,

    raquetaIzda.getRectElemento(), raquetaDcha.getRectElemento());

    11 if(bola.puedoMover(speed, speed, screen))

    12 v.vibrate(50);

    13 }

    14 bola.move(speed, speed);

    15 }

    16 }

    Con esto estamos haciendo una vibracin breve (50 milisegundos) cada vez que rebotamos

    contra una raqueta. Fcil, verdad?

    Como informacin extra tenemos que saber que un objeto de la clase Vibrator tambin

    puede vibrar dado un patrn, utilizando la funcin vibrate(pattern, repeat), siendo pattern

    un long[] y repeat un int que dice cuntas veces se repite (-1 si no queremos repetir).

    Sonido

    El siguiente paso consistir en aadir sonido a los rebotes, el tpico sonido metlico.

    Existen diversas formas de usar sonidos en Android, pero vamos a usar la ms simple de

    todas: el MediaPlayer. Lo primero que tenemos que hacer es buscar un sonido que nos

    valga. Por lo general se recomienda usar ficheros codificados en formato .ogg (Ogg Vorbis)

    por ser el ms compatible y tener una gran compresin. Yo he elegido el siguiente sonido.

    Si quieres usar el mismo, haz click derecho sobre el enlace y selecciona Guardar enlace

    como (y gurdalo con un nombre distinto, en minsculas, sin espacios y sin tildes).

    Ahora crearemos una nueva carpeta dentro de la carpeta res de nuestro proyecto, y la

    llamaremos raw. Dentro de ella meteremos nuestro fichero de audio (en mi caso pong.ogg).

    A partir de ahora podremos acceder a ello referenciando a R.raw.pong. Aadimos un nuevo

    atributo a BolaMoveThread:

    1 private MediaPlayer mp = null;

    Y el constructor queda as:

    1 public BolaMoveThread(Bola bola, Raqueta izda,

    2 Raqueta dcha, Rect screen, Context context) {

    3 this.bola = bola;

    4 this.raquetaIzda = izda;

    5 this.raquetaDcha = dcha;

    6 this.screen = screen;

    7 this.run = false;

    8 this.speed = 1;

  • 9 this.v =

    (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);

    10 this.mp = MediaPlayer.create(context, R.raw.pong);

    11 }

    La llamada a MediaPlayer.create() crea y prepara el audio local para ser reproducido.

    Existe una funcin de los objetos MediaPlayer llamada prepare() que hace lo mismo (pero

    slo hay que llamarla en ciertos momentos, ahora explicamos ms). Falta hacer que

    reproduzca el sonido, de modo que vamos a la funcin run(), que queda:

    1 @Override

    2 public void run() {

    3 while(run) {

    4 try {

    5 Thread.sleep(10);

    6 } catch (InterruptedException e) {

    7 e.printStackTrace();

    8 }

    9 if(!bola.puedoMover(speed, speed, screen,

    raquetaIzda.getRectElemento(), raquetaDcha.getRectElemento())) {

    10 mp.start();

    11 bola.rebota(speed, speed, screen,

    raquetaIzda.getRectElemento(), raquetaDcha.getRectElemento());

    12 if(bola.puedoMover(speed, speed, screen))

    13 v.vibrate(50);

    14 }

    15 bola.move(speed, speed);

    16 }

    17 }

    Podramos hacer un stop() y prepare() despus y antes (respectivamente) de cada start().

    Sin embargo eso sera un consumo muy grande de los recursos, y start() ya mantiene

    preparado el audio para hacer un replay. Sera interesante, sin embargo, hacer una llamada

    a stop() y prepare() dentro del setRunning() dependiendo de si paramos o iniciamos el

    thread. Agregar sonidos simples ha resultado ser tambin muy fcil, no?

    Activar y desactivar

    Ahora mismo tenemos que la aplicacin hace que el dispositivo vibre al rebotar con una

    pala, y hace que reproduzca un sonido de choque metlico cada vez que rebote con algo

    (sea pared o raqueta). Recordemos que tenemos un men en el cual tenamos una parte de

    Opciones, pero que no tena nada dentro. Es ahora el momento de crear las opciones del

    juego, permitiendo activar y desactivar estas dos funcionalidades.

    Comenzamos creando un nuevo layout que tendr el siguiente cdigo:

  • 1

    2

    5

    7

    11

    13

    14

    16

    20

    22

    23

    Teniendo en cuenta que tenemos que aadir los Strings a strings.xml:

    1

    2

    3 Pong

    4 Jugar

    5 Opciones

    6 Salir

    7 Sonido

    8 Vibracin

  • 9

    Tenemos que aadir la Activity al AndroidManifest.xml igual que hicimos en otra de las

    entradas. Para ello abrimos el AndroidManifest.xml y aadimos lo siguiente antes de

    :

    1

    Lo siguiente que necesitamos hacer es aadir el cdigo necesario para crear una actividad

    nueva que muestre este nuevo layout. Creamos una nueva clase llamada

    PongOpcionesActivity, cuyo cdigo es el siguiente:

    1 package com.vidasconcurrentes.pongvc;

    2

    3 import android.app.Activity;

    4 import android.os.Bundle;

    5 import android.view.Window;

    6 import android.view.WindowManager;

    7

    8 public class PongOpcionesActivity extends Activity {

    9

    10 @Override

    11 protected void onCreate(Bundle savedInstanceState) {

    12 super.onCreate(savedInstanceState);

    1

    3 requestWindowFeature(Window.FEATURE_NO_TITLE);

    1

    4 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREE

    N,

    1

    5 WindowManager.LayoutParams.FLAG_FULLSCREEN

    );

    1

    6 setContentView(R.layout.options);

    17 }

    18 }

    Ahora slo falta hacer el cdigo en la actividad principal para que se ejecute esta. Para ello

    vamos a la clase PongvCActivity (la principal), y aadimos la funcin:

    1 private void muestraOpciones() {

    2 Intent opciones = new Intent(this, PongOpcionesActivity.class);

    3 this.startActivity(opciones);

    4 }

  • Ahora en el onCreate() de esta clase, vamos al lugar donde registramos el listener para el

    botn de Opciones y cambiamos el contenido por lo siguiente:

    1 TextView options = (TextView)findViewById(R.id.options_button);

    2 options.setOnClickListener(new OnClickListener() {

    3 @Override

    4 public void onClick(View v) {

    5 muestraOpciones();

    6 }

    7 });

    Ahora s, si hacemos una ejecucin, obtendremos esto tras pulsar en el botn Opciones del

    men principal:

    Ahora tenemos que programar qu pasa cuando activamos y desactivamos estos

    checkboxes. He elegido hacer lo siguiente con un patrn Singleton. A grandes rasgos, un

    patrn Singleton ofrece la garanta de que existe como mximo una instancia de una clase y

    que es accesible globalmente. Nosotros queremos poder acceder al estado de estas opciones

    desde el juego, pero modificarlas desde esta actividad. De modo que comenzamos creando

    nuestra clase PongOpciones dentro de un nuevo paquete llamado

    com.vidasconcurrentes.pongvc.opciones:

    1 package com.vidasconcurrentes.pongvc.opciones;

    2

    3 public class PongOpciones {

    4

    5 private static PongOpciones opciones = null;

    6 private boolean sonido;

  • 7 private boolean vibracion;

    8

    9 private PongOpciones() {

    10 sonido = true;

    11 vibracion = true;

    12 }

    13

    14 public static synchronized PongOpciones getInstance() {

    15 if(opciones == null)

    16 opciones = new PongOpciones();

    17 return opciones;

    18 }

    19

    20 public void toggleSound() {

    21 sonido = !sonido;

    22 }

    23

    24 public void toggleVibration() {

    25 vibracion = !vibracion;

    26 }

    27

    28 public boolean soundEnabled() {

    29 return sonido;

    30 }

    31

    32 public boolean vibrationEnabled() {

    33 return vibracion;

    34 }

    35 }

    Las ponemos inicializadas a true porque, por defecto, nuestra aplicacin va a tener ambas

    activadas. Ahora aadimos el comportamiento al pulsar sobre los checkboxes. Para ello

    vamos al onCreate() de la clase PongOpcionesActivity y aadimos lo siguiente al final:

    1 CheckBox sonido = (CheckBox) findViewById(R.id.checkBoxSonido);

    2 sonido.setOnClickListener(new OnClickListener() {

    3 @Override

    4 public void onClick(View v) {

    5 PongOpciones.getInstance().toggleSound();

    6 }

    7 });

    8

  • 9 CheckBox vibracion = (CheckBox) findViewById(R.id.checkBoxVibracion);

    10 vibracion.setOnClickListener(new OnClickListener() {

    11 @Override

    12 public void onClick(View v) {

    13 PongOpciones.getInstance().toggleVibration();

    14 }

    15 });

    16

    17 sonido.setChecked(PongOpciones.getInstance().soundEnabled());

    18 vibracion.setChecked(PongOpciones.getInstance().vibrationEnabled());

    Como vemos, lo ltimo que hacemos es poner el estado de cada CheckBox acorde con el

    estado de la instancia del patrn Singleton. Cuando pulsamos en un CheckBox, cambiamos

    el valor de la variable a la que se refiere de true a false y viceversa.

    Efectivamente, si ejecutsemos ahora, comprobaramos que podemos tener ambas activas,

    desactivadas o una activa y otra no.

    Acelermetro

    El acelermetro del dispositivo va a permitirnos registrar movimientos de ste para saber si

    se ha cambiado la inclinacin con respecto de la posicin inicial con la que se comenz el

    juego. Existen varias formas de usar el acelermetro, y por supuesto existen muchsimas e

    infinitas formas de programar dnde debe ir cada cosa en nuestro proyecto.

    Mis decisiones han sido las siguientes:

    Queremos que se ejecute un Thread que controle la raqueta izquierda con el

    acelermetro.

    Queremos quitar el registro del acelermetro cuando no lo necesitemos.

    Queremos reanudar el uso del acelermetro al volver al juego.

    Al igual que hicimos al crear los hilos anteriores (pintado y movimiento de la bola), vamos

    a crear un hilo nuevo para las raquetas:

    1 package com.vidasconcurrentes.pongvc.juego;

    2

    3 import android.graphics.Rect;

    4

    5 public class RaquetaMoveThread extends Thread {

    6

    7 private Raqueta raqueta;

    8 private Rect screen;

    9

    10 private boolean run;

  • 11

    12 public RaquetaMoveThread(Raqueta r, Rect s) {

    13 raqueta = r;

    14 screen = s;

    15 }

    16

    17 public void setRunning(boolean run) {

    18 this.run = run;

    19 }

    20

    21 public void run() {

    22 while(run) {

    23 try {

    24 Thread.sleep(10);

    25 } catch (InterruptedException e) {

    26 e.printStackTrace();

    27 }

    28 // nuestro codigo va aqui

    29 }

    30 }

    31 }

    En PongGameView creamos el hilo en surfaceCreated() y lo matamos en

    surfaceDestroyed():

    1 // esto dentro de surfaceCreated()

    2 raquetaThread = new RaquetaMoveThread((Raqueta)raquetaIzda,

    3 new Rect(0,0,getWidth(),getHeight()));

    4 raquetaThread.setRunning(true);

    5 raquetaThread.start();

    6

    7 // esto seria el surfaceDestroyed():

    8 @Override

    9 public void surfaceDestroyed(SurfaceHolder arg0) {

    10 boolean retry = true;

    11 paintThread.setRunning(false);

    12 bolaThread.setRunning(false);

    13 raquetaThread.setRunning(false);

    14 while (retry) {

    15 try {

    16 paintThread.join();

    17 bolaThread.join();

  • 18 raquetaThread.join();

    19 retry = false;

    20 } catch (InterruptedException e) { }

    21 }

    22 }

    Ahora es el momento de crear nuestro acelermetro. Para ello vamos a crear una clase

    envolvente para la funcionalidad que queremos que nos ofrezca. Un dispositivo emulado no

    puede usar acelermetro, as que ser necesario el uso de un dispositivo fsico.

    Nosotros vamos a querer que se mueva la raqueta. Esta raqueta pertenece a una actividad

    que est en modo landscape (apaisado) continuamente. Un dispositivo fsico tiene su eje X

    a lo alto, es decir, es una lnea imaginaria que va desde los botones del dispositivo hasta el

    auricular. Si lo prefers: de abajo a arriba. Su eje Y es el perpendicular a ste que cruza de

    lado a lado. Como hasta ahora hemos visto a la hora de pintar, con el dispositivo en modo

    apaisado, el eje X ser el ancho y el eje Y ser el alto. De modo que si nosotros rotamos el

    dispositivo hacia delante en modo apaisado, queremos que nuestra pala suba. Si rotamos el

    dispositivo hacia nosotros en modo apaisado, queremos que nuestra pala baje.

    Dicho en otras palabras: si la rotacin del eje X es negativa, queremos que vaya arriba; si

    es positiva, queremos que vaya abajo. Para ms informacin sobre los ejes de coordenadas

    tridimensionales pulsa aqu.

    Por tanto slo nos interesa la rotacin del eje X para nuestra pala, pero podemos acceder a

    los valores de Y y Z consultando event.values[1] y event.values[2] respectivamente.

    Creamos una nueva clase AcelerometroPong en el paquete

    com.vidasconcurrentes.pongvc.juego:

    1 package com.vidasconcurrentes.pongvc.juego;

    2

    3 import android.content.Context;

    4 import android.hardware.Sensor;

    5 import android.hardware.SensorEvent;

    6 import android.hardware.SensorEventListener;

    7 import android.hardware.SensorManager;

    8

    9 public class AcelerometroPong implements SensorEventListener {

    10

    11 private SensorManager sm = null;

    12 private int x;

    13

    14 public AcelerometroPong(Context context) {

    15 sm = (SensorManager)

    context.getSystemService(Context.SENSOR_SERVICE);

    16 }

    17

  • 18 public void register() {

    19 sm.registerListener(this,

    sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),

    20 SensorManager.SENSOR_DELAY_GAME);

    21 }

    22

    23 public void unregister() {

    24 sm.unregisterListener(this);

    25 }

    26

    27 @Override

    28 public void onAccuracyChanged(Sensor sensor, int accuracy) { }

    29

    30 @Override

    31 public void onSensorChanged(SensorEvent event) {

    32 if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

    33 x = Math.round(event.values[0] * 100);

    34 }

    35 }

    36

    37 public int getXInclination() {

    38 return x;

    39 }

    40 }

    Desde la Activity PongJuego vamos a usar esta clase como atributo, y aadiremos dos

    nuevos mtodos de modo que quedar as:

    1 package com.vidasconcurrentes.pongvc;

    2

    3 import android.app.Activity;

    4 import android.os.Bundle;

    5 import android.view.Window;

    6 import android.view.WindowManager;

    7

    8 import com.vidasconcurrentes.pongvc.juego.AcelerometroPong;

    9 import com.vidasconcurrentes.pongvc.pintado.PongGameView;

    10

    11 public class PongJuego extends Activity {

    12

    13 private AcelerometroPong acelerometro;

    14

  • 15 @Override

    16 protected void onCreate(Bundle savedInstanceState) {

    17 super.onCreate(savedInstanceState);

    18 requestWindowFeature(Window.FEATURE_NO_TITLE);

    1

    9 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREE

    N,

    2

    0 WindowManager.LayoutParams.FLAG_FULLSCREEN

    );

    21 acelerometro = new AcelerometroPong(this.getApplicationContext());

    22 setContentView(new PongGameView(this, acelerometro));

    23 }

    24

    25 @Override

    26 protected void onResume() {

    27 super.onResume();

    28 acelerometro.register();

    29 }

    30

    31 @Override

    32 protected void onStop() {

    33 super.onStop();

    34 acelerometro.unregister();

    35 }

    36 }

    Es importante no usar el sensor si no es necesario, de ah onResume() y onStop().

    Nuestro PongGameView ahora recibe tambin el acelermetro, as que tendremos que

    modificar el constructor para que as lo reciba y aadir un atributo nuevo.

    1 private Integer xInit = null;

    2 private AcelerometroPong acelerometro;

    3

    4 public PongGameView(Context context, AcelerometroPong acelerometro) {

    5 super(context);

    6 getHolder().addCallback(this);

    7

    8 this.acelerometro = acelerometro;

    9 }

    Ahora, queremos que sea el Thread que controla el movimiento de la raqueta el encargado

    de consultar el acelermetro, as que igualmente cambiamos el constructor de ste y lo

    aadimos como atributo a RaquetaMoveThread:

  • 1 private AcelerometroPong acelerometro;

    2

    3 public RaquetaMoveThread(Raqueta r, Rect s, AcelerometroPong a) {

    4 raqueta = r;

    5 screen = s;

    6 this.acelerometro = a;

    7 }

    Adems, aadimos esta nueva variable xInit de tipo Integer (para poder inicializarla a null).

    Y por qu queremos inicializarla a null? Es tan slo una pequea treta que se me ha

    ocurrido para poder realizar una calibracin en cada ejecucin. Si es null es que an no

    hemos calibrado la posicin en la que est el dispositivo al arrancar el Thread, si no es null

    es que ya est calibrado.

    Ahora modificaremos el run() de este Thread, de la siguiente forma:

    1 public void run() {

    2 if(xInit == null)

    3 xInit = acelerometro.getXInclination();

    4

    5 while(run) {

    6 try {

    7 if(Math.abs(xInit - acelerometro.getXInclination()) < 200)

    8 Thread.sleep(5);

    9 else

    10 Thread.sleep(2);

    11 } catch (InterruptedException e) {

    12 e.printStackTrace();

    13 }

    14 if(xInit < acelerometro.getXInclination() - UMBRAL ||

    15 xInit < acelerometro.getXInclination() + UMBRAL)

    16 if(raqueta.puedoMover(0, 1, screen))

    17 raqueta.move(0, 1);

    18 if(xInit > acelerometro.getXInclination() - UMBRAL ||

    19 xInit > acelerometro.getXInclination() + UMBRAL)

    20 if(raqueta.puedoMover(0, -1, screen))

    21 raqueta.move(0, -1);

    22 }

    23 }

    Lo primero que hacemos es inicializar la posicin de calibrado, si es null. Lo siguiente que

    hacemos es hacer que el movimiento de la raqueta sea ms o menos rpido dependiendo de

    si hemos inclinado mucho el dispositivo o no. En nuestro caso, si hay una diferencia

  • absoluta (positiva o negativa) de menos de 200 unidades, entonces nos vamos a mover a

    una velocidad normal. Si esto es mayor significa que hemos inclinado el dispositivo mucho

    ms, por lo que deseamos que se mueva ms rpido. La variable UMBRAL no es ms que

    una variable final de la clase que en este caso tiene el valor de 20. Este UMBRAL nos sirve

    para no mover la raqueta si el movimiento es demasiado pequeo (quiz por errores de

    medida o de redondeo). Adems aadimos la comprobacin de si puedoMover() para evitar

    salirse de la pantalla.

    Con esto ya tenemos la raqueta izquierda funcionando para moverse con el acelermetro,

    pero a la vez funciona con el dedo y esto no es lo deseable.

    Activar / desactivar acelermetro

    Igual que hicimos con el sonido y la vibracin, ahora queremos poder cambiar acelermetro

    por tctil y viceversa. Para ello vamos a nuestra clase PongOpciones y aadimos un nuevo

    atributo, de modo que queda:

    1 private static PongOpciones opciones = null;

    2 private boolean sonido;

    3 private boolean vibracion;

    4 private boolean acelerometro;

    5

    6 private PongOpciones() {

    7 sonido = true;

    8 vibracion = true;

    9 acelerometro = false;

    10 }

    Aadimos tambin los siguientes dos mtodos, para cambiar el valor y consultarlo:

    1 public void toggleAcelerometro() {

    2 acelerometro = !acelerometro;

    3 }

    4

    5 public boolean accelerometerEnabled() {

    6 return acelerometro;

    7 }

    Ahora tenemos que llamar a estos mtodos desde la actividad de las opciones, la cual

    necesita ser cambiada para aadir una nueva fila. Por tanto, antes del de

    options.xml, aadimos:

    1

  • 2

    3

    4

    Hay que aadir tambin el String al fichero strings.xml. De esta forma veramos la siguiente

    imagen al ejecutar:

    En el onCreate() de PongOpcionesActivity, aadimos:

    1 CheckBox acelerometro = (CheckBox) findViewById(R.id.checkBoxAccel);

    2 acelerometro.setOnClickListener(new OnClickListener() {

    3 @Override

    4 public void onClick(View v) {

    5 PongOpciones.getInstance().toggleAcelerometro();

    6 }

    7 });

    8

    9 acelerometro.setChecked(PongOpciones.getInstance().accelerometerEnabled

    ());

    De esta forma cambiamos la variable de activado a desactivado y viceversa.

  • Es el momento ahora de hacer que la aplicacin use una u otra. Todo el cdigo se va a

    poner por tanto en PongGameView. El Thread de la raqueta slo se ejecuta cuando est

    activado el acelermetro, por tanto modificamos el cdigo de surfaceCreated() y

    cambiamos:

    1 if(PongOpciones.getInstance().accelerometerEnabled()) {

    2 raquetaThread = new RaquetaMoveThread((Raqueta)raquetaIzda,

    3 new Rect(0,0,getWidth(),getHeight()), acelerometro);

    4 raquetaThread.setRunning(true);

    5 raquetaThread.start();

    6 }

    Adems, si este Thread no se ha iniciado, no se puede matar. As que en

    surfaceDestroyed():

    1 @Override

    2 public void surfaceDestroyed(SurfaceHolder arg0) {

    3 boolean retry = true;

    4 paintThread.setRunning(false);

    5 bolaThread.setRunning(false);

    6 if(PongOpciones.getInstance().accelerometerEnabled())

    7 raquetaThread.setRunning(false);

    8 while (retry) {

    9 try {

    10 paintThread.join();

    11 bolaThread.join();

    12 if(PongOpciones.getInstance().accelerometerEnabled())

    13 raquetaThread.join();

    14 retry = false;

    15 } catch (InterruptedException e) { }

    16 }

    17 }

    Adems, si activamos el acelermetro no queremos poder mover la raqueta con el dedo. De

    modo que en el onTouchEvent() vamos a envolverlo todo con un:

    1 if(!PongOpciones.getInstance().accelerometerEnabled() {

    2 // aqui todo el codigo anterior, incluyendo el switch

    3 }

    4 return true;

    Hecho esto podemos ejecutar la aplicacin y trastear con ella activando y desactivando

    cosas, reiniciando el juego La idea es iniciar el juego, pulsar la tecla de actividad

  • anterior que nos lleva al men, elegir Opciones y cambiarlas, dar a actividad anterior y luego a Jugar.

    Aqu llega el final de la entrada de hoy. En ella hemos visto cmo usar distintos sensores

    del sistema como la vibracin o el acelermetro, adems de las herramientas que nos ofrece

    para reproducir sonidos locales. En la siguiente entrada acabaremos la aplicacin aadiendo

    las ltimas mejoras como un pequeo umbral para el juego tctil, el marcador de juego y la

    posibilidad de que se cuele la bola y por ltimo una pequea Inteligencia Artificial para la

    raqueta derecha.