Mostrando entradas con la etiqueta android. Mostrar todas las entradas
Mostrando entradas con la etiqueta android. Mostrar todas las entradas

domingo, 7 de julio de 2013

Un único Toast en Android

Los Toasts de Android son una opción muy socorrida cuando se quieren mostrar mensajes al usuario sin molestar demasiado. Por ejemplo, cada vez que el usuario modifica una opción de la aplicación.

Los Toast se pueden crear mediante la llamada:

Toast.makeText(context, context.getString(id), Toast.LENGTH_SHORT)

El último parámetro indica el tiempo durante el que el Toast será visible en pantalla, y generalmente se usan las contastes LENGTH_SHORT y LENGTH_LONG de la propia clase Toast para darle valor.

Hasta aquí todo muy bien, pero un problema habitual se produce cuando se quieren mostrar varios Toast de forma consecutiva. En ese caso el sistema encola los Toast, y cada uno se va mostrando cuando desaparece el anterior. Esto implica que, seguramente, el usuario esté viendo en pantalla un mensaje correspondiente a una acción que no es la última que ha realizado.

Ante esto, yo prefiero la opción de que cada nuevo Toast se presente inmediatamente en pantalla, haciendo desaparecer al anterior. Así, adaptamos la información mostrada al ritmo al que el usuario utiliza la aplicación.

Viendo el API de la clase Toast, parece que la opción más lógica para conseguir esto es utilizar el método cancel. Pero la verdad es que por lo que lo he podido probar, no siempre da el resultado esperado.

Así que la solución que me he planteado es hacer una especie de Singleton sobre los Toast. La idea es que sólo exista un Toast en mi aplicación; así, cada vez que tengo que mostrar un mensaje, en lugar de crear una nueva instancia, que acabaría encolada si había otras anteriores, cojo mi única instancia, le cambio el texto, y la vuelvo a mostrar.

El resultado es una clase tal que así.

public class Toasts {

private static Toast myToast = null;

public static void showToast(Context context, int id) {
if (myToast != null) {
myToast.setText(context.getString(id));
} else {
myToast = Toast.makeText(context, context.getString(id),
Toast.LENGTH_SHORT);
}
myToast.show();
}
}

Esta clase puedo usarla de forma sencilla desde cualquier actividad de mi aplicación, simplificando además la  llamada al necesitar sólo el contexto y el texto.

Toasts.showToast(this, R.string.text);

Una posible mejora sería crear un segundo método que permitiese especificar también la duración del Toast, pero eso ya depende de las necesidades de cada uno.

martes, 2 de julio de 2013

Android Tip: setVisibility y Animations

En los últimos días he tenido que lidiar con uno de esos problemas simplotes que te cuesta horas solucionar.

Tengo un RelativeLayout, invoco el método setVisibility(View.INVISIBLE) sobre él, y no desaparece.

En mi caso esto se debía a que había ejecutado previamente una animación sobre ese RelativeLayout y, al parecer, la visibilidad de una vista queda bloqueada hasta que las animaciones son limpiadas.

Con añadir una llamada a clearAnimation() sobre el RelativeLayout se soluciona el problema.

sábado, 1 de junio de 2013

Android Tip: NullPointerException en android.widget.ArrayAdapter.createViewFromResource(ArrayAdapter.java:355)

Este error me ha traído de cabeza más de una vez, y tanto su causa como su solución son muy simples. Seguramente tengas en tu aplicación un ListView con un ArrayAdapter, y le estés añadiendo un valor nulo.

En mi caso suelo usar esta solución para mostrar valores sacados de la base de datos como, por ejemplo, una lista de jugadores. Si hay algún registro con el valor vacío, se recuperará como null, y al agregarlo al ArrayAdapter provocará la excepción.

Basta con controlar los valores nulos con algo tan simple como esto:

if (player != null) {
     playersArrayAdapter.add(player);
}

miércoles, 22 de mayo de 2013

Android Tip: Impedir que se puedan seleccionar las filas de un ListView

No es exactamente lo indicado en el título, pero el caso es que al mostrar una lista con un ListView, si el usuario pulsa sobre un elemento éste se selecciona y queda resaltado. Si lo único que queremos es evitar este efecto, basta con añadir estas propiedades en la etiqueta ListView.

android:cacheColorHint="@android:color/transparent"
android:listSelector="@android:color/transparent"

jueves, 9 de mayo de 2013

CalledFromWrongThreadException


Uno de los requisitos clásicos de Android es que cualquier modificación sobre una vista debe ser realizada desde el hilo que la creó (generalmente referido como el UI Thread). Esto hace que no sea raro que cuando empecemos a trabajar con hilos acabemos encontrándonos con la excepción:

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Ante esta excepción lo primero que tenemos que preguntarnos es si realmente queremos modificar la vista desde donde lo estamos haciendo. Si la respuesta es afirmativa, podemos recurrir al método runOnUiThread de la clase Activity. Un posible ejemplo de uso sería el siguiente.

Tenemos una actividad de una aplicación que permite conexiones por bluetooth. En pantalla se muestra un botón, y cuando lo pulsamos, ponemos en marcha un servidor, que se mantiene a la espera de conexiones. Este trabajo se realiza en un hilo independiente que es lanzado desde nuestra actividad. Lo que queremos conseguir es mostrar un texto informando de que el servidor está esperando conexiones, y ocultarlo cuando éste se detenga.

Damos a nuestro hilo una referencia a la actividad, y en ésta añadimos un método para mostrar u ocultar el texto informativo. Al final del método run() del hilo añadimos una llamada a ese nuevo método.

public class XXX extends Thread {
....

public void run() {
...
activity.setTextVisible(false);
}
...
}

public class XXX extends Activity {
...
public void setTextVisible(boolean visible) {
text.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}

Excepción asegurada.

Pero si cambiamos la implementación del método setTextVisible a:

public void setTextVisible(boolean visible) {
runOnUiThread(new Runnable() {
public void run(){
text.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
});
}

Todo marchará como la seda.

martes, 7 de mayo de 2013

Abrir una url usando LibGDX


Cuando hacemos un juego en Android, es muy normal que queramos dar al usuario la posibilidad de abrir una página Web. Por ejemplo, para tratar de convencer a nuestros jugadores de que pierdan un minuto en valorar nuestro trabajo en Google Play.

Abrir una url desde Android no tiene ningún misterio; basta con ejecutar este código desde alguna actividad.

Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(myIntent);
  
Pero, ¿qué pasa si hemos desarrollado nuestro juego con LibGDX? Pues que la librería nos abstrae de la implementación concreta (Android en nuestro caso), por lo que cuando estemos programando una pantalla no tendremos acceso a las clases de Android, ni actividades, ni ninguna de esas historias. Y además, a día de hoy, que yo conozca, la librería no provee ningún método que se encargue de esto.

Una posible solución sería hacer que nuestro juego delegue la acción de abrir la url en el componente encargado de lanzarlo para cada implementación (una actividad para Android, una clase para escritorio...). Para conseguirlo, crearemos una interfaz en el proyecto principal de nuestro juego, que declarará el método para abrir una url.

public interface IOperations {
 public void openUrl(String url);
}

A continuación, añadiremos a nuestro juego una atributo de tipo IOperations, y modificaremos el constructor para que requiera este valor como parámetro.

public class MyGame extends Game {

 private IOperations myOperations;
 
 public SaveMeGame(IOperations myOperations){
  this.myOperations = myOperations;
 }
 
 //... Resto de la clase ...
}
 
Ahora podemos agregar a la clase MyGame un método que delegue en IOperations la apertura de la url.

public void openUrl(String url){
 myOperations.openUrl(url);
}
 
Una vez hecho esto, tendremos que modificar los lanzadores de las distintas tecnologías (Android, HTML5, J2SE...) para implementar la interfaz IOperations, y proveer por tanto una implementación concreta del método que abre una url. Para el caso de Android sería algo así.

public class MyActivity extends AndroidApplication implements
  IOperations {

 @Override
 public void onCreate(Bundle savedInstanceState) {
  // ... Preparación del juego ... 

  View gameView = initializeForView(new MyGame(this), config);

  setContentView(gameView);
 }

 @Override
 public void openUrl(String url) {
  Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        startActivity(myIntent);
 }
}

Con esto tendríamos la estructura completa, y podemos usar este método desde cualquier pantalla de nuestro juego.

public class MyScreen implements Screen {

 private final String gameURI = "http://play.google.com/store/apps/details?id=package.game";
 private Game game;

 //... En algún lugar de la pantalla ...
 
 ((MyGame) game).openUrl(gameURI);
 
 //...
}
 
Y eso sería todo. Una vez completado el círculo, un pequeño repaso para asentar la idea.

En nuestra situación de partida estamos escribiendo código en una pantalla de nuestro juego (implementación de Screen), y queremos abrir una url, pero la librería no nos ofrece un método para hacerlo, y la forma de conseguirlo depende de la tecnología en que despleguemos finalmente el juego.

Por tanto, definimos una interfaz IOperations con un método para abrir una url. En esta interfaz podríamos añadir cualquier otro método que necesitemos, y que sea dependiente de la tecnología.

Añadimos a nuestra clase Game un nuevo atributo de tipo IOperations, y modificamos el constructor para que requiera un objeto que implemente la interfaz.

Por último, implementamos la interfaz IOperations en los proyectos correspondientes a cada una de las tecnologías en que vayamos a desplegar nuestro juego. En el ejemplo hemos visto una forma de hacerlo para Android, con la actividad de inicio implementando directamente IOperations. También se podría haber creado una nueva clase que contuviese exclusivamente la implementación de IOperations (y de hecho sería recomendable, sobre todo si empieza a crecer el número de operaciones disponibles).

Y ya podemos invocar el método openUrl sobre una instancia de nuestro juego.

viernes, 5 de abril de 2013

Save Me!

Ya está disponible en Google Play mi nuevo juego: Save Me!

La idea del juego es muy simple; Me es un personaje que avanza sin pausa por la pantalla, y tu objetivo es conseguir que llegue al final de cada nivel. Para ello tendrás que preparar el terreno, moviendo bloques y activando distintos dispositivos.

Parece fácil, ¿no?

Esta primera versión cuenta con 6 niveles cuya dificultad va creciendo desde el primero, que simplemente servirá para que el jugador se familiarice con los controles, hasta el último, que pondrá de verdad a prueba sus habilidades.

Mi intención es ir haciendo nuevas versiones que incluyan nuevos niveles y nuevos elementos en el mundo de Me.

Como siempre, cualquier comentario que pueda ayudar a hacer de Save Me! un juego mejor será bienvenido.

¡Todo el mundo a salvar a Me!


viernes, 25 de enero de 2013

Los barquitos o battleship

Pues nada, para seguir con la racha, hoy he publicado en Google Play el segundo de mi colección de juegos casuales por bluetooth para la plataforma Android: Los Barquitos. Un clásico al que seguro que todos habréis jugado más de una vez cuando estabais en el cole.


Me gustaría seguir ampliando la colección, y además hay muchos posibles candidatos esperando su momento: el cuatro en raya, el ajedrez, tic-tac-toe... Pero todavía no me he arrancado con ninguno, y tengo otras cosas entre manos, así que por el momento, esto es lo que hay.

El juego tiene tanto modo individual, en el que competirás contra una IA muy básica, como juego por bluetooth.

¡Todo el mundo a probarlo!

lunes, 21 de enero de 2013

Memory Battle


Bueno, pues hoy he cumplido un objetivo que llevaba mucho tiempo persiguiendo: publicar mi primera aplicación en Google Play. He estado mucho tiempo probando, y he empezado varios proyectos en paralelo, pero por fin he dado por buena la versión 1.0 de Memory Battle.


La aplicación no es más que una versión del clásico juego en el que tienes que encontrar las parejas de un grupo de cartas, volteándolas de dos en dos. El juego individual tiene varias opciones de configuración que el usuario puede ir desbloqueando, y hacen que vaya siendo cada vez más complicado (dentro de lo que puede dar de sí un Memory). Además, incluye la posibilidad de jugar con otros dispositivos mediante bluetooth, lo que está muy bien para echar un buen rato picándote con los amigos.

Mi idea es iniciar una línea de juegos casuales con multijugador por bluetooth, y ya tengo alguna otra cosa en camino.

Por cierto, este juego en concreto lo he desarrollado basándome en el framework descrito en la primera parte del libro Begining Android 4 Games Development, de Mario Zechner y Robert Green, cuya lectura recomiendo, y a los que agradezco desde aquí que hayan dedicado su tiempo a escribir un libro tan interesante.

A los que lo probéis, ¡espero que lo disfrutéis!

martes, 28 de agosto de 2012

Error generating final archive: Debug certificate expired

Si tenemos un poco abandonado el tema de programar con Android, y después de un tiempo queremos retomarlo, es muy probable que al intentar ejecutar un proyecto antiguo nos encontremos con que Eclipse nos larga este error:

Error generating final archive: Debug certificate expired ...

Tal como indica el mensaje, el problema es que el certificado de depuración de Android ha expirado. En esta url podemos ver que los de Google ya lo tienen registrado como bug, y que la solución temporal que se propone es la eliminación del fichero /.android/debug.keystore, para obligar a Android a regenerarlo.

Eso sí, dentro de un año, a repetir el proceso...

jueves, 16 de febrero de 2012

Animaciones con frames en Android

Este post lo tenía pendiente desde hace tiempo, con idea de profundizar en el tema y hacerlo más completo. Pero dado que las circunstancias me han hecho dejar bastante de lado el desarrollo con Android por el momento, aquí va lo que he podido hacer hasta ahora.

Lo que quería conseguir cuando me metí en este jaleo era dar un toque más atractivo al jueguecillo con el que estaba cacharreando metiendo una animación de fondo. Para ello lo primero, y lo que a mí me parece más complicado por mi escasas aptitudes artísticas, es disponer de una serie de imágenes que compongan la animación (los frames). Para no complicarme, lo que acabé haciendo fue buscarme una secuencia que me gustaba, y sacar de ella los frames para probar.

El tema es bastante sencillo. Lo que tenemos que hacer es crear un fichero en la carpeta drawable, y ponerle el nombre que le queramos dar a la animación. Por ejemplo: background_animation.xml. En el diálogo de creación de ficheros XML de Eclipse veremos que uno de los tipos disponibles es Animation.

El formato del fichero tampoco tiene mayor complicación. Lo único que hace es definir los frames que forman la animación, cada uno en una etiqueta item.





Podemos definir una duración distinta para cada uno de los elementos de la animación, expresada en milisegundos. El parámetro oneshot del elemento animation-list indica si la animación debe ejecutarse en bucle (valor falso), o no.


Para poner en marcha la animación no tenemos más que ponerla como fondo de una vista. Por ejemplo, desde una ActivityView:


this.setBackgroundResource(R.drawable.background_animation);

sábado, 27 de agosto de 2011

Android - Error en R.java - Syntax Error on Tokens

De vuelta tras vacaciones y líos varios, y justo un mes después de la última entrada, retomo las publicaciones contando un pequeño problema que me he encontrado jugueteando un poco más con Android.

Resulta que estaba haciendo ligeras modificaciones en el main.xml para añadir un simple TextView a la pantalla, y cuando he terminado y me disponía a probar los cambios en el móvil, me he encontrado con que la clase R.java tenía un error.

Lo primero que hay que decir es que esta clase es generada de forma automática, y Eclipse se cuida mucho de que no la toquemos. Si lo intentamos nos avisará de que no debemos, pero no queda ahí la cosa. Si somos temerarios y le decimos que nos dan igual sus advertencias, nos encontraremos con que tras guardar los cambios el entorno volverá a generar la clase perdiendo lo que hemos hecho. Vamos, que el tratar de corregir la clase por nuestra cuenta es una batalla perdida.

Así que para hacer las cosas bien, lo que necesitamos es saber por qué se está generando la clase con un error. En este caso el que se me presentaba era:


La clase R.java, explicado sucintamente, es un índice con todos los recursos del proyecto Android. Luego si hay un error de sintaxis en ella por un token no reconocido, seguramente se deba a que hemos utilizado un carácter extraño en algún sitio.

En mi caso, por ejemplo, tras rebuscar un poco, conseguí darme cuenta de que había declarado el nuevo texto de la siguiente manera:


Lo que a simple vista parece correcto. Pero si nos fijamos otra vez, podemos observar que en el identificador pone @+ìd/enemyLifeText. Esa tilde sobre la primera i es la que está causando el problema. Si la eliminamos, la clase R.java se regenera y los problemas desaparecen.

martes, 8 de marzo de 2011

Android - Gestión de aplicaciones

Después de un tiempo bicheando con Android, desarrollando y probando aplicaciones, te puedes encontrar que el emulador esté hasta arriba de iconos, y quieras eliminar algunos. Si no estás familiarizado con el funcionamiento de estos móviles, como me pasa a mí, puede que no atines a encontrar la forma de hacerlo, aunque es bastante simple.

Lo primero es pulsar el botón Menú, con lo que aparecerán una serie de opciones. Seleccionamos Manage Apps. Esto nos lleva a una pantalla en la que se muestran todas las aplicaciones del emulador (con los iconos de la sección superior se puede restringir la vista para mostrar las que se encuentran en la tarjeta SD, por ejemplo). Si buscamos en esa lista (ordenada alfabéticamente) la aplicación que queremos eliminar, y la seleccionamos, accederemos a una pantalla donde se mostrará información sobre ella, y en la que podemos ver un botón Uninstall.


Si pulsamos este botón y confirmamos, la aplicación desaparecerá de nuestro emulador.

Sin llegar al extremo de eliminarla, nos puede pasar que ejecutemos una aplicación y al salir de ella se quede ejecutándose. Me ha pasado, por ejemplo, con algunos juegos con sonidos; si sales con el botón de vuelta atrás, el juego sigue ejecutándose, y por tanto sonando.

Para parar la aplicación podemos volver a la pantalla de gestión de aplicaciones, y encontramos una opción en la botonera superior para mostrar las aplicaciones que se están ejecutando. Sin embargo, es muy posible que no encontremos la aplicación que queremos parar. Para verla tendremos que volver a pulsar el botón Menú desde esa pantalla, y seleccionar la opción Show Cached Processes. En la nueva lista sí que podremos ver todas las aplicaciones que se están ejecutando y, al seleccionarlas, tendremos un botón Stop, que detendrá su ejecución.

sábado, 5 de febrero de 2011

Uno de los errores que me he encontrado habitualmente desarrollando para Android con el plugin de Eclipse ADT es el siguiente:

emulator: ERROR: the user data image is used by another emulator. aborting

Cuando esto pasa resulta imposible desplegar una aplicación por más que reiniciemos el emulador, el eclipse, o lo que queramos. El porqué empieza a aparecer este error la verdad es que no he conseguido averiguarlo, pero sí que he encontrado una solución satisfactoria en los foros de stackoverflow.

Como ahí se explica, para solucionar el problema basta con eliminar dos carpetas ubicadas en la ruta C:\Documents and Settings\%user%\.android\avd\%nombreTelefono%.avd, cache.img.lock y userdata-qemu.img.lock.

En mi caso funcionó perfectamente, aunque en un principio obtuve un comportamiento extraño: cada vez que arrancaba la aplicación se lanzaba una nueva instancia del emulador. No duró mucho.

Otras alternativa que parece algo más limpia es la de reiniciar el servidor ADB. La he probado y funciona igualmente. Se puede hacer yendo a la vista DDMS, a la pestaña Devices, y al View Menu (la flecha blanca que apunta hacia abajo). Ahí tenemos la opción Reset ADB.

jueves, 13 de enero de 2011

Configurar barras de progreso en Android

En estas primeras horas de mi andadura con Android me he encontrado un gran problema a la hora de trabajar con las barras de progreso.

Como ya comenté, la idea que tengo entre manos es desarrollar un juego para foguearme con la tecnología, y necesito mostrar en pantalla varias barras de progreso. Lógicamente interesa que no sean todas iguales, para que se pueda identificar cada una de ellas con lo que expresa. El caso más típico es el de mostrar la vida con una barra roja, por ejemplo.

El primer paso es definir el tipo de barra de progreso. Por defecto Android trabaja con barras circulares (o barras paradójicas). Para utilizar barras de verdad, tenemos que indicar explícitamente el valor del atributo style del elemento ProgressBar del fichero de layout que estemos utilizando (main.xml, o el que corresponda).


Sí, el valor es así de raro, y agradezco enormemente a Daniel Yankowsky el aporte.

Lo siguiente que nos plantearemos normalmente, ahora que tenemos la barra, es cómo cambiarle el color. El amarillo sobre gris está muy bien, pero hay todo un mundo más allá. En este caso vamos a necesitar crear un nuevo fichero, que ubicaremos en la carpeta res/drawable, y llamaremos progress.xml (por poner). El fichero tendremos que crearlo directamente desde el sistema operativo, y en él podemos definir con mayor detalle el estilo de la barra de progreso, mediante el siguiente código:



En este caso tenemos dos items, que se corresponden con el fondo y el progreso de la barra. Se podría añadir otro elemento secondaryProgress si lo necesitásemos. Para cada elemento se configura el radio de las esquinas, para que queden redondeadas, y se añade un gradiente, para que el color no sea uniforme, sino una degradación de los tres indicados. El ángulo indica cómo se aplica el gradiente (0 para hacerlo en horizontal, y 270 para vertical). Respecto al color, se identifica mediante ocho dígitos hexadecimales, donde los dos primeros indican la transparencia.

Existen otras opciones como poner relleno, y están bastante bien descritas aquí.

El último detalle que puede ayudar a personalizar la barra es el tamaño. En mi caso me interesaba presentar barras más finas para determinada información. Para conseguir este resultado nos vamos de nuevo al fichero de layout, y añadimos los atributos maxHeight y minHeight.



De esta forma conseguimos barras con distintas presentaciones. Como muestra, una captura (botones no me quedan ahora mismo).


Hope this helps!

Android TIP: refrescar recursos

En las últimas semanas he empezado a meterme en el mundo de Android con la idea de hacer algunas pequeñas aplicaciones, y poder publicarlas en el market.

Como punto de partida me he marcado el objetivo de crear un juego, y aprender así a manejarme con el entorno y herramientas de desarrollo. En futuros posts iré dando más detalles sobre el proyecto (si puede llegar a tener esa categoría). De momento, simplemente una pequeña ayuda para reducir tiempos de espera al trabajar.

Uno de los principales problemas que le veo al desarrollo para Android con Eclipse, mediante el plugin ADT, es que el emulador es bastante lento, sobre todo a la hora de arrancar, y en ocasiones puede hacerte perder la paciencia.

Si estás desarrollando una aplicación, y especialmente si es un juego, es normal que manejes ficheros de recursos, y concretamente imágenes. Y también es normal que las modifiques a menudo para ir metiendo mejoras en el apartado gráfico. Ante una situación de cambio en cualquier fuente del proyecto durante el desarrollo, siempre surge la duda de qué hay que hacer para que ésta se refleje en nuestra ejecución. ¿Compilo? ¿Redespliego? ¿Simplemente guardo? ¿Reinicio el ordenador?

Depende de con qué estés desarrollando. En el caso de Android, hasta donde sé, si haces un cambio en el código, simplemente tienes que volver a ejecutar la aplicación, pero no el emulador. Sin embargo, esto no es suficiente cuando el cambio se hace en un fichero de recursos. No vale ejecutar la aplicación, ni tampoco reiniciar el emulador, si no has refrescado antes la carpeta de recursos. Esto lo hacemos desde el explorador de paquetes, con el menú contextual.

Tras el refresco sí que podemos ejecutar de nuevo sobre el emulador que tengamos arrancado, y veremos que los cambios han sido aplicados.