Bienvenidos a la sexta 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 acabaremos el desarrollo de la aplicación, mejoraremos el control táctil para hacerlo más tolerante e incluiremos todo lo necesario para el uso del marcador (y que se guarde si volvemos al menú). En la próxima y última entrada de la serie, publicaremos la aplicación.
¡Comencemos!
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: 1e3eeda28b041716b8526aa59f1aa312) el proyecto de Eclipse con todo lo hecho hasta ahora, el cual puedes importar a tu Eclipse y comenzar a trabajar.
Mejora del control táctil
El primer paso para esta entrada va a ser el añadir un pequeño umbral al control táctil de las raquetas. Ahora mismo el control exige que toquemos exactamente dentro de la raqueta, de modo que si tocas un píxel por fuera no te va a detectar nada. El feedback de la gente que ha probado la aplicación en mi dispositivo es que necesita ser más tolerante, así que allá vamos.
Con nuestro Eclipse abierto, localizamos la clase PongGameView, que es la clase encargada de procesar el control táctil. Vamos a añadir el siguiente atributo a la clase, que a fin de cuentas es una constante:
public static final int UMBRAL_TACTIL = 70;
El número que pongamos a esta variable será el número de píxeles de más que queremos que acepte por la izquierda y la derecha.
Ahora vamos a añadir este umbral al procesado del control táctil. Para ello vamos a la función onTouchEvent() que, si recordamos, consta principalmente de un switch con tres casos. El primer caso se activa al pulsar, el segundo se activa al arrastrar después de haber pulsado y el tercero se activa al soltar. Nosotros estamos interesados en el primer caso, así que lo modificaremos de la siguiente forma:
case MotionEvent.ACTION_DOWN: // hemos pulsado Rect aux; aux = new Rect(raquetaIzda.getRectElemento()); aux.set(aux.left - UMBRAL_TACTIL, aux.top, aux.right + UMBRAL_TACTIL, aux.bottom); if(aux.contains(x, y)) { elementoActivo = raquetaIzda; origenY = y; break; } aux = new Rect(raquetaDcha.getRectElemento()); aux.set(aux.left - UMBRAL_TACTIL, aux.top, aux.right + UMBRAL_TACTIL, aux.bottom); if(aux.contains(x, y)) { elementoActivo = raquetaDcha; origenY = y; break; } break;
Así, usamos un rectángulo a partir de los rectángulos definidos por las raquetas, con la salvedad de que los expandimos por los lados tantas unidades como UMBRAL_TACTIL diga. Hasta aquí la mejora del control táctil.
Marcador
Lo siguiente que vamos a hacer es crear el marcador de juego y todo lo necesario para que funcione. Es decir, cuando completemos esta sección tendremos algo jugable de principio a fin.
Comenzamos creando una clase Marcador:
package com.vidasconcurrentes.pongvc.juego; public class Marcador { public static final int MAX_PUNT = 9; private int puntosIzda; private int puntosDcha; public void initMarcador() { puntosIzda = 0; puntosDcha = 0; } public Marcador() { initMarcador(); } public int getPuntosIzda() { return puntosIzda; } public int getPuntosDcha() { return puntosDcha; } public void addPuntoIzda() { puntosIzda++; } public void addPuntoDcha() { puntosDcha++; } public boolean acabado() { return puntosDcha == MAX_PUNT || puntosIzda == MAX_PUNT; } }
De esta forma estamos definiendo que el juego acabará cuando uno de los dos jugadores haga 9 puntos. Además estamos definiendo los comportamientos necesarios para inicializar el marcador, consultar el valor y añadir un punto a los jugadores.
Una cosa está clara cuando programamos, y es que queremos ver que las cosas que hacemos funcionan, y queremos verlo lo antes posible. Por esta razón lo siguiente que vamos a hacer es lo necesario para mostrar en pantalla el marcador para que veamos que lo que vayamos haciendo realmente funciona.
Vamos a la clase PongGameView y añadimos un nuevo atributo de clase Marcador:
private Marcador marcador;
Además, añadimos el constructor de este objeto al constructor de la clase, de modo que el constructor quedará:
public PongGameView(Context context, AcelerometroPong acelerometro) { super(context); getHolder().addCallback(this); this.acelerometro = acelerometro; this.marcador = new Marcador(); }
Ahora ya contamos con un marcador. Lo siguiente será crear en PongGameView las funciones necesarias para pintar los números en pantalla. Queremos crear una linea divisoria a la mitad de la pantalla, al estilo Pong original. Comenzamos creando este método:
private void drawCenterLine(Canvas canvas, Paint paint) { int w = 6; int h = 20; int gap = 10; int ini = gap / 2; // por estetica, si no seria 0 for(int i = 0; i < this.getHeight() / (h+gap); i++) { canvas.drawRect(this.getWidth()/2 - w/2, ini, this.getWidth()/2 + w/2, ini + h, paint); ini += h + gap; } }
En este código tenemos unas variables iniciales, que muy bien podían ser parte de los atributos de la clase puesto que no vamos a modificarlos en ningún momento, pero quería mantener la explicación en códigos breves. La variable w será el ancho de cada una de las lineas divisorias. Se recomienda que sea par. La variable h es la altura de cada linea divisoria y gap es el espacio entre una y otra linea. La variable ini define dónde comenzaremos a pintar. He elegido gap / 2 para su inicialización para que esté igual de separada por arriba que por abajo de los límites, pero es una elección empírica. En el bucle vamos pintando cada uno de los tramos de la linea discontínua hasta llegar abajo. Sólo faltaría añadir la llamada a este método dentro del onDraw():
@Override public void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.WHITE); canvas.drawColor(Color.BLACK); drawCenterLine(canvas, paint); canvas.drawRect(raquetaIzda.getRectElemento(), paint); canvas.drawRect(raquetaDcha.getRectElemento(), paint); canvas.drawRect(bola.getRectElemento(), paint); }
Aquí tenemos una captura de pantalla de la ejecución actual de la aplicación:
Lo siguiente que vamos a añadir es el pintado de los números de las puntuaciones de cada jugador. Además, vamos a usar una font propia, que no viene en el SDK de Android. Lo primero será buscar una, yo elegí la font Kelly Slab de Google Web Fonts. Ahora la incluimos en nuestra aplicación, para lo cual vamos a añadir un directorio llamado fonts en nuestro directorio assets, y ahí incluimos nuestra recién descargada font:
Atención: ¡continuar leyendo antes de crear ningún código!
Lo siguiente será crear el método donde cargamos el tipo de letra, de la siguiente forma:
private void drawMarcador(Canvas canvas, Paint paint) { paint.setTextAlign(Align.CENTER); paint.setTypeface(Typeface.createFromAsset(this.getContext().getAssets(), "fonts/KellySlab-Regular.ttf")); paint.setTextSize(80); paint.setAntiAlias(true); canvas.drawText(Integer.toString(marcador.getPuntosIzda()), getWidth() / 2 - 80, 80, paint); canvas.drawText(Integer.toString(marcador.getPuntosDcha()), getWidth() / 2 + 80, 80, paint); }
Con esto añadimos opciones al objeto Paint y pintamos los números del marcador. Si ahora modificamos el método onDraw():
@Override public void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.WHITE); canvas.drawColor(Color.BLACK); drawCenterLine(canvas, paint); drawMarcador(canvas, paint); canvas.drawRect(raquetaIzda.getRectElemento(), paint); canvas.drawRect(raquetaDcha.getRectElemento(), paint); canvas.drawRect(bola.getRectElemento(), paint); }
Podemos ejecutar la aplicación y obtendremos una captura como la siguiente:
Si continuamos con el código actual, con lo de arriba explicado, hagamos una reflexión. Pongamos que la carga de esa font gastara 1KB de memoria por cada ejecución. Nuestos dispositivos tienen 512MB de media, ¡así que eso no es un problema! Falso.
Nuestra aplicación no ejecuta un número de frames por segundo fijo, sino que ejecuta todo lo rápido que puede. Por ejemplo, la aplicación podría estar refrescando la pantalla a una velocidad de 60FPS, lo que conlleva una carga de la font en cada refresco con el consecuente tiempo de carga y el consecuente gasto de memoria. Si esto lo prolongamos con el tiempo… entonces nos vamos a quedar sin memoria (been there, done that).
Por lo tanto, dado que esto no es un PC de mesa que suelen tener varios GB de memoria RAM… es de especial importancia no realizar la carga de ficheros sin necesidad (y otras operaciones costosas y que comen RAM). Si podemos hacerlas una sola vez, mejor.
Ahora sí, vamos a crear el código necesario y relativamente optimizado. Lo primero va a ser agregar el siguiente atributo a la clase PongGameView:
private Paint paint;
Ahora el constructor de la clase será de la siguiente forma (puesto que el objeto Paint no se modifica en los demás métodos):
public PongGameView(Context context, AcelerometroPong acelerometro) { super(context); getHolder().addCallback(this); this.acelerometro = acelerometro; this.marcador = new Marcador(); paint = new Paint(); paint.setColor(Color.WHITE); paint.setTextAlign(Align.CENTER); paint.setTypeface(Typeface.createFromAsset(this.getContext().getAssets(), "fonts/KellySlab-Regular.ttf")); paint.setTextSize(80); paint.setAntiAlias(true); }
El método de pintado completo será el conjunto de lo siguiente:
@Override public void onDraw(Canvas canvas) { canvas.drawColor(Color.BLACK); drawCenterLine(canvas); drawMarcador(canvas); canvas.drawRect(raquetaIzda.getRectElemento(), paint); canvas.drawRect(raquetaDcha.getRectElemento(), paint); canvas.drawRect(bola.getRectElemento(), paint); } private void drawCenterLine(Canvas canvas) { int w = 6; int h = 20; int gap = 10; int ini = gap / 2; // por estetica, si no seria 0 for(int i = 0; i < this.getHeight() / (h+gap); i++) { canvas.drawRect(this.getWidth()/2 - w/2, ini, this.getWidth()/2 + w/2, ini + h, paint); ini += h + gap; } } private void drawMarcador(Canvas canvas) { canvas.drawText(Integer.toString(marcador.getPuntosIzda()), getWidth() / 2 - 80, 80, paint); canvas.drawText(Integer.toString(marcador.getPuntosDcha()), getWidth() / 2 + 80, 80, paint); }
Ahora sí, tenemos un pintado inteligente intentando optimizar el uso de Paint.
El siguiente paso será hacer que, al tocar en uno de los laterales de la pantalla (izquierdo o derecho), se sume un punto al jugador en cuestión.
Comenzamos modificando el método rebota() de la Bola, de modo que ahora va a devolver un valor. Devolverá -1 si la bola entró por la izquierda, 1 si entró por la derecha y 0 si sigue en la pantalla. Por tanto, este método ahora es:
public int rebota(int x, int y, Rect screen, Rect raquetaIzda, Rect raquetaDcha) { if(!puedoMover(x,y,screen)) { switch(direccion) { case DCHA_ARRIBA: if(origen.getY() - y <= screen.top) direccion = DCHA_ABAJO; else return 1; break; case IZDA_ARRIBA: if(origen.getY() - y <= screen.top) direccion = IZDA_ABAJO; else return -1; break; case IZDA_ABAJO: if(origen.getY() + alto + y >= screen.bottom) direccion = IZDA_ARRIBA; else return -1; break; case DCHA_ABAJO: if(origen.getY() + alto + y >= screen.bottom) direccion = DCHA_ARRIBA; else return 1; break; } } Rect raqueta = null; if(chocaraCon(x, y, raquetaIzda)) raqueta = raquetaIzda; if(chocaraCon(x, y, raquetaDcha)) raqueta = raquetaDcha; if(raqueta != null) { switch(direccion) { case DCHA_ARRIBA: direccion = (origen.getX()+ancho < raqueta.left) ? IZDA_ARRIBA : DCHA_ABAJO; break; case IZDA_ARRIBA: direccion = (origen.getX() > raqueta.right) ? DCHA_ARRIBA : IZDA_ABAJO; break; case IZDA_ABAJO: direccion = (origen.getX() > raqueta.right) ? IZDA_ARRIBA : DCHA_ABAJO; break; case DCHA_ABAJO: direccion = (origen.getX()+ancho < raqueta.left) ? IZDA_ABAJO : DCHA_ARRIBA; break; } } return 0; }
De esta forma hemos modificado la primera parte del código del método, si ahora mismo ejecutásemos la aplicación podríamos ver que la bola se cuela por los lados, pero no ocurre nada. Ahora es cuando haremos que ocurra ese algo al colarse.
Comenzamos añadiendo el Marcador a la clase BolaMoveThread, pues va a ser éste el que se encargue de modificarlo. El constructor con el nuevo atributo quedaría:
private Marcador marcador; public BolaMoveThread(Bola bola, Raqueta izda, Raqueta dcha, Rect screen, Context context, Marcador marcador) { this.bola = bola; this.raquetaIzda = izda; this.raquetaDcha = dcha; this.screen = screen; this.run = false; this.speed = 1; this.v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); this.mp = MediaPlayer.create(context, R.raw.pong); this.marcador = marcador; }
Ahora vamos a modificar el método run() de esta clase. Queremos que, si el método bola.rebota() devuelve 0, haga lo que hace hasta ahora. Si devuelve -1 queremos que sume un punto a la raqueta derecha y si devuelve 1 que sume a la raqueta izquierda (recordamos que el signo indica por qué lado entró). De esta forma, modificamos así:
@Override public void run() { while(run) { try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } if(punto) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if(!marcador.acabado()) { punto = false; } continue; } if(!bola.puedoMover(speed, speed, screen, raquetaIzda.getRectElemento(), raquetaDcha.getRectElemento())) { switch(bola.rebota(speed, speed, screen, raquetaIzda.getRectElemento(), raquetaDcha.getRectElemento())) { case 0: if(PongOpciones.getInstance().soundEnabled()) mp.start(); if(bola.puedoMover(speed, speed, screen) && PongOpciones.getInstance().vibrationEnabled()) v.vibrate(50); break; case -1: marcador.addPuntoDcha(); reinitBola(); punto = true; break; case 1: marcador.addPuntoIzda(); reinitBola(); punto = true; break; } } bola.move(speed, speed); } }
Aquí hemos hecho varias cosas, expliquémoslas por partes.
Lo primero que hemos hecho es añadir una variable private boolean punto; a los atributos de la clase, la cual se inicializa a false. Lo siguiente es añadir el primer bloque. Si punto == true, entonces estamos diciendo que se ha marcado un punto ahora mismo. En este caso queremos que la bola no se mueva y que se quede dos segundos parada. En caso de haber acabado no volverá a moverse hasta que no volvamos al menú y juguemos de nuevo. En caso de no haber acabado, devolvemos la variable punto a false, lo que significa que quedan puntos. En cualquier caso continuamos con la siguiente iteración sin pasar por el resto del código.
El resto del código modificado es un switch con el valor de salida del método bola.rebota(). Si devuelve 0 ejecutamos el código que ya teníamos. Si devuelve otra cosa, añadimos un punto al jugador correspondiente, reiniciamos la posición de la bola y ponemos punto a true. El código de reinitBola() necesita que hayamos agregado dos nuevos atributos y los hayamos inicializado. Todo lo necesario para reinitBola() se indica a continuación:
private int bolaInitX; private int bolaInitY; private boolean punto; public BolaMoveThread(Bola bola, Raqueta izda, Raqueta dcha, Rect screen, Context context, Marcador marcador) { this.bola = bola; this.bolaInitX = bola.getOrigenX(); this.bolaInitY = bola.getOrigenY(); this.raquetaIzda = izda; this.raquetaDcha = dcha; this.screen = screen; this.run = false; this.speed = 1; this.v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); this.mp = MediaPlayer.create(context, R.raw.pong); this.marcador = marcador; this.punto = false; } private void reinitBola() { bola.setOrigenX(bolaInitX); bola.setOrigenY(bolaInitY); }
Además hemos cambiado el primer Thread.sleep() a 5 para que vaya más rápido la bola con respecto de cómo iba hasta ahora. Una pequeña y rápida partida jugando a propósito nos podría dar la siguiente imagen:
Ya tenemos el marcador completo, funcional y en pantalla. Sin embargo, si ahora mismo jugamos y volvemos al menú en mitad de una partida, no mantendremos nuestra puntuación guardada.
Para ilustrar el paso de datos entre actividades vamos a hacer que se mantenga la puntuación.
Lo primero que tenemos que hacer es modificar el código en la clase PongvCActivity. Localizamos el método empiezaJuego(), que contiene este código:
private void empiezaJuego() { Intent juego = new Intent(this, PongJuego.class); this.startActivity(juego); }
Ahora lo modificamos para que sea:
private void empiezaJuego() { Intent juego = new Intent(this, PongJuego.class); this.startActivityForResult(juego, Activity.RESULT_OK); }
De esta forma, en lugar de iniciar una Activity nueva, decimos que queremos que nos mande algo de vuelta. Ahora tenemos que crear el lugar donde procesar ese algo. Para ello sobrecargamos el método onActivityResult():
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch(requestCode) { case 1: if (resultCode == Activity.RESULT_OK) { // solo si el codigo devuelto es RESULT_OK, procesamos } break; } }
Lo siguiente que vamos a hacer es crear el código que devuelve las puntuaciones del marcador. Vamos a la clase PongJuego, y añadimos el siguiente atributo con la necesaria modificación del constructor:
private AcelerometroPong acelerometro; private PongGameView view; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); acelerometro = new AcelerometroPong(this.getApplicationContext()); view = new PongGameView(this, acelerometro); setContentView(view); }
Además, en PongGameView vamos a poner un nuevo método:
public Marcador getMarcador() { return marcador; }
Así podremos consultar el marcador desde la actividad y poder devolver el valor. Para ello, en PongJuego crearemos el siguiente método:
@Override public void onBackPressed() { Bundle bundle = new Bundle(); bundle.putInt("PuntosIzda", view.getMarcador().getPuntosIzda()); bundle.putInt("PuntosDcha", view.getMarcador().getPuntosDcha()); Intent mIntent = new Intent(); mIntent.putExtras(bundle); setResult(Activity.RESULT_OK, mIntent); super.onBackPressed(); }
Ahora sólo falta procesar estos datos devueltos. Lo primero será añadir dos nuevos atributos en PongvCActivity, con su inicialización:
private int puntosIzdaIniciales = 0; private int puntosDchaIniciales = 0;
Ahora volvemos al método onActivityResult() de la clase PongvCActivity, y añadimos el código interno a este if:
if (resultCode == Activity.RESULT_OK && data.getExtras() != null) { // solo si el codigo devuelto es RESULT_OK, procesamos puntosIzdaIniciales = data.getExtras().getInt("PuntosIzda"); puntosDchaIniciales = data.getExtras().getInt("PuntosDcha"); }
Solo falta hacer que se usen estos valores al arrancar la actividad de PongJuego. Para ello vamos a modificar la forma de llamarla en empiezaJuego(), incluyendo estos dos valores. Usamos la misma idea de añadir extras, obteniendo este código:
private void empiezaJuego() { Intent juego = new Intent(this, PongJuego.class); juego.putExtra("PuntosIzda", puntosIzdaIniciales); juego.putExtra("PuntosDcha", puntosDchaIniciales); this.startActivityForResult(juego, 1); }
Ahora falta usar estos valores en PongJuego, de modo que modificamos el constructor para que quede así:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); acelerometro = new AcelerometroPong(this.getApplicationContext()); if(getIntent().getExtras() != null) { int puntosIzda = getIntent().getExtras().getInt("PuntosIzda"); int puntosDcha = getIntent().getExtras().getInt("PuntosDcha"); if(puntosIzda == Marcador.MAX_PUNT || puntosDcha == Marcador.MAX_PUNT) view = new PongGameView(this, acelerometro, 0, 0); else view = new PongGameView(this, acelerometro, puntosIzda, puntosDcha); } else view = new PongGameView(this, acelerometro, 0, 0); setContentView(view); }
Y modificar el constructor de PongGameView así:
public PongGameView(Context context, AcelerometroPong acelerometro, int puntosIzda, int puntosDcha) { super(context); getHolder().addCallback(this); this.acelerometro = acelerometro; this.marcador = new Marcador(puntosIzda, puntosDcha); paint = new Paint(); paint.setColor(Color.WHITE); paint.setTextAlign(Align.CENTER); paint.setTypeface(Typeface.createFromAsset(this.getContext().getAssets(), "fonts/KellySlab-Regular.ttf")); paint.setTextSize(80); paint.setAntiAlias(true); }
Esto, lógicamente, requiere añadir un nuevo constructor al Marcador:
public Marcador(int i, int d) { puntosIzda = i; puntosDcha = d; }
Ahora sí, podemos ejecutar nuestra aplicación y veremos que si volvemos al menú (por ejemplo para cambiar alguna opción) y luego volvemos a dar a Jugar, mantendremos la puntuación que teníamos acumulada. Llegados a este punto podría crearse un menú emergente que permitiera elegir continuar partida existente (si la hay) o iniciar una nueva. Sin embargo considero que es sobrecargar sin necesidad la entrada, y sería una cosa a tener en cuenta si ésta fuera una aplicación comercial.
Evitar el bloqueo de pantalla
Si utilizamos la aplicación con el acelerómetro nos damos cuenta de que la pantalla acabará por apagarse eventualmente dependiendo de nuestras opciones del timeout para el bloqueo. Sin embargo, nosotros queremos que la pantalla no se bloquee automáticamente para nuestra aplicación si estamos usando el acelerómetro. Esto sin embargo puede ser malo para el consumo de batería si un usuario deja la aplicación abierta.
Lo primero será agregar el siguiente atributo a la clase PongJuego e inicializarlo en el constructor:
private WakeLock wl; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); [...] setContentView(view); PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE); wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "Pong-vC"); }
Ahora añadimos el método onPause() y modificamos el método onResume():
@Override protected void onPause() { super.onPause(); if(PongOpciones.getInstance().accelerometerEnabled()) wl.release(); } @Override protected void onResume() { super.onResume(); acelerometro.register(); if(PongOpciones.getInstance().accelerometerEnabled()) wl.acquire(); }
Ahora sólo falta añadir el permiso de usar éste código en el AndroidManifest.xml, o si no obtendremos una excepción que matará nuestra aplicación. Para añadirlo abrimos el manifest, en la pestaña de Permissions. Pulsamos en Add > Uses Permission. En el cuadro derecho añadimos android.permission.WAKE_LOCK.
Para comprobar que hemos hecho todo correctamente, iniciamos la aplicación vamos a Opciones, cambiamos el control para usar el acelerómetro, volvemos al menú y pulsamos en Jugar. Si todo está bien la aplicación ejecutará el juego y podremos comprobar que no se apaga la pantalla (probar ésto teniendo un timeout bajo).
Última mejora
Las últimas cosas se dejan para el final, y si nos damos cuenta hasta ahora no hemos usado el botón de Salir del menú. Queremos que cuando se pulse se cierre la aplicación.
Vamos a la clase PongvCActivity, en el método onCreate(), en la parte que añadimos el onTouchListener() al botón de salir. Modificamos su código así:
TextView exit = (TextView)findViewById(R.id.exit_button); exit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { finish(); } });
Al llamar a la función finish() en una actividad lo que hacemos es cerrarla.
Hasta aquí la entrada de hoy. Con esta entrada podemos decir que hemos acabado el desarrollo de la aplicación. La siguiente entrada consistirá en todos los pasos necesarios para publicar la aplicación, desde la creación de una cuenta de Google Checkout hasta la publicación.
Quedarían varias cosas que hacer en la aplicación, como por ejemplo la creación de una pequeña inteligencia artificial para la raqueta derecha, uso del multitáctil para poder jugar dos personas tocando a la vez o localización de idioma para los botones del menú principal.
Sin embargo sería sobrecargar demasiado esta serie de tutoriales puesto que estamos intentando hacer una pequeña introducción a Android con la excusa de crear un juego. Nuestra intención no es crear una aplicación con todo lo que tiene Android a la vez.
Como siempre podéis descargar el proyecto de Eclipse completo para poder importarlo y usarlo pulsando aquí (MD5: 209d4e4393a45dadd15c18bfb22621c1).
Una vez más, ¡muchas gracias por vuestro tiempo!