jueves, 30 de octubre de 2014

Uso de comillas angulares en Word


En la notación UML, los estereotipos se definen con su nombre entre comillas angulares (o francesas, o latinas, o guillemets): «Estereotipo».

Como estas comillas no aparecen en los teclados, es habitual utilizar dos signos de mayor o menor para sustituirlas (<< y >>), pero sí que podemos escribirlas mediante la combinación de teclas alt + 174 y alt + 175, respectivamente.
Si tenemos un documento Word en el que hemos utilizado << y >>, y queremos reemplazarlos por los caracteres « y », podemos tener problemas con las opciones de autocorrección. Lo habitual si usamos la opción reemplazar, es que Word utilice las comillas tipográficas o inglesas. Para evitar este comportamiento, podemos acceder a las opciones de Word (Archivo - Opciones, en Word 2010), al apartado Revisión, y abrir el cuadro de configuración Opciones de Autocorrección. En las pestañas Autoformato y Autoformato mientras escribe, se debe desmarcar la opción Comillas rectas como comillas tipográficas.
De esta forma, si usamos la opción de reemplazar con los caracteres « y », Word los respetará.

jueves, 13 de marzo de 2014

ArtifactTransferException: Failure to transfer

Al crear un proyecto con Maven en Eclipse, me encontré que el pom.xml marcaba montones de errores del tipo:

ArtifactTransferException: Failure to transfer...

La solución al problema la encontré rápidamente en stackoverflow. Basta con eliminar todos los ficheros con extensión lastUpdated del repositorio maven, y actualizar el proyecto. Lo primero lo puedes hacer con los comandos:

cd %userprofile%\.m2\repository
for /r %i in (*.lastUpdated) do del %i

Y luego basta con ir a Eclipse y, sobre el proyecto, seleccionar Maven - Update project, y aceptar el diálogo.

jueves, 27 de febrero de 2014

Escribiendo un log

Podemos añadir un fichero de log a nuestra aplicación Java muy fácilmente utilizando la clase Logger.

1
2
3
4
5
FileHandler fh = new FileHandler("Log.log");
Logger logger = Logger.getLogger("TestLog");
logger.addHandler(fh);
SimpleFormatter formatter = new SimpleFormatter();
fh.setFormatter(formatter); 

Con este código creamos el Logger, y podemos añadir mensajes con la sentencia:

1
logger.info("Mensaje de log");

Este tipo de mensajes incluye información sobre la fecha, clase y método en que se está escribiendo el log. En general esta información es muy útil, pero si quieres escribir un log con muchas entradas, y vas a tener que procesarlas, puede que prefieras que no aparezca. Si es así, puedes crear tu propio formateador extendiendo de la clase Formatter.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class MySimpleFormatter extends Formatter 
{   
    public MySimpleFormatter() { 
        super(); 
    }

    @Override 
    public String format(final LogRecord record) 
    {
        return record.getMessage();
    }   
}

Basta con cambiar el SimpleFormatter estándar por el nuestro, y ya tendremos los mensajes a nuestro gusto.

Por supuesto, podemos modificar el método format para configurar la información del log como queramos.

miércoles, 10 de julio de 2013

Referencia a la clase contenedora en una clase anónima

Al programar interfaces gráficas, es muy habitual el uso de clases anónimas para definir el listener de algún componente.

JButton button = new JButton("OK");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
   System.out.println("Ok!");
}});      
 
Tampoco es raro que desde la clase anónima, queramos acceder a algún elemento de la clase contenedora. En ese caso, podemos vernos tentados de usar this; pero, obviamente, si estamos dentro de la clase anónima, this hará referencia a la instancia de esa clase, no a la de la contenedora.

En este caso podemos usar como alternativa ClaseContenedor.this, quedando algo así.

public class ClaseContenedora extends JPanel{
   int value = 0;

   public ClaseContenedora() {
      ...

      JButton button = new JButton("OK");
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent e)
         {
            ClaseContenedora.this.value++;
         }});    

      ...
   } 
}

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.

miércoles, 12 de junio de 2013

Key Adapter y JPanel

El método addKeyListener de la clase Component de Swing nos permite agregar oyentes de teclado a cualquier componente. Por tanto, si estamos creando un JPanel, perfectamente podríamos intentar hacer algo así:

public class MyPanel extends JPanel {

public MyPanel() {
MyKeyAdapter keyAdapter = new MyKeyAdapter();
this.addKeyListener(keyAdapter);
}
...
}

Siendo MyKeyAdapter algo de este estilo.

public MyKeyAdapter extends KeyAdapter {

public void onKeyPressed(KeyEvent event) {
...
}

...
}

Pero si probamos este código, nos encontramos que por más que aporreemos el teclado, los eventos no se lanzan. El motivo es simple. Los eventos de teclado se lanzan siempre sobre el elemento de la interfaz que tiene el foco, y los JPanel, por defecto, no son enfocables (focusables). Así que una solución rápida al problema sería hacer el JPanel focusable de la siguiente manera.

public MyPanel() {
this.setFocusable(true);

MyKeyAdapter keyAdapter = new MyKeyAdapter();
this.addKeyListener(keyAdapter);
}
...
}

Ahora, al hacer click sobre el panel, ganará el foco. Si queremos dárselo desde algún rincón de nuestro código, también podemos hacerlo con la llamada panel.requestFocus().

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...

miércoles, 18 de abril de 2012

Windows no busca en ficheros con extensión sql

Hace unas semanas me tocó revisar unos 300 ficheros sql para tratar de sacar información sobre una base de datos que íbamos a migrar. Sin documentación, con tablas y campos en desuso, sin integridad referencial... un trabajo duro. Así que la mejor opción que teníamos para sacar el esquema de datos era analizar consultas que sabíamos que se ejecutaban sobre ella, y funcionaban correctamente.

Estaba centrándome en una tabla concreta, y quería obtener sus relaciones, por lo que pretendía filtrar los ficheros para quedarme con los que hiciesen referencia a ella. Abrí mi explorador de archivos de XP, me fui a la carpeta en la que estaban todos los ficheros, y busqué metiendo el nombre de la tabla en el campo 'Una palabra o frase en el archivo'.

Cero resultados.

Tras un par de comprobaciones (ni había escrito mal el nombre de la tabla, ni estaba distinguiendo mayúsculas...), empecé a olerme que algo iba mal. Abrí algunos ficheros, y pude ver que en varios aparecía el nombre. Entonces, ¿por qué la búsqueda no arrojaba resultados?

Pues porque los ficheros tenían extensión .sql. Si cambiaba alguno a .txt, automáticamente aparecía en los resultados.

Todavía no he podido averiguar a qué se debe esto. Si es algún problema de configuración, o simplemente a Windows no le caen bien estos ficheros. El caso es que he comprobado todas las opciones de la búsqueda, y no veo nada raro. En la opción de Tipos de Archivo tengo marcado Todos los archivos y carpetas, y no se me ocurre mucho más.

Por tanto, la única solución que he podido aplicar es la de utilizar un programa para cambiar la extensión de todos los ficheros de la carpeta. A partir de ahí, pude seguir con mi infernal base de datos.

martes, 13 de marzo de 2012

Diferencia entre dos flujos en Spoon

Hacía mucho tiempo que no tocaba Spoon, una herramienta que siempre me dejó con ganas de poder comprobar de qué es capaz realmente. Así que ha sido muy gratificante encontrarme con la posibilidad de utilizarla en mi trabajo, y de tener encima al lado a una persona con experiencia previa, como es el gran Isvapi.

Así que ya llevo unos días con un caso real. El clásico para este tipo de herramientas: tenemos que migrar los datos de un sistema a punto de morir, a otro nuevo y maravilloso. Si todo va bien y logro sacar un poco de tiempo, es posible que en las próximas semanas me anime a hacer una segunda parte de mi tutorial de iniciación. Pero hoy, como adelanto, la solución a un caso concreto que nos hemos encontrado.

El tema es que la migración la hacemos en dos fases. En la primera migramos todo el contenido de una tabla, y en la segunda el incremental. Por tanto, necesitábamos poder obtener la diferencia entre dos flujos de datos con la misma estructura (que sería el incremental a migrar). La realidad es que hacer algo así con Spoon es bastante fácil, gracias al paso Fundir filas.

Para explicar el proceso, vamos a suponer que trabajamos con una entidad Persona, que consta de un identificador, el nombre y los apellidos.

Lo primero que haríamos sería leer los dos repositorios de entrada, ya sean tablas de una base de datos, ficheros de texto, Excels... A continuación tendríamos que utilizar el paso indicado. Aquí su ayuda contextual:

Fundir dos flujos de filas, ordenadas por un campo clave. Se comparan los dos flujos y se señalan las filas que son iguales, han cambiado, se han eliminado y las nuevas.

Para que nos entendamos, lo que vamos a hacer es coger dos colecciones de datos con la misma estructura; en nuestro caso, dos colecciones de personas. El resultado contendrá la unión de las dos colecciones, indicando para cada elemento si:

- Aparece igual en ambas colecciones (identical).
- Aparece en ambas colecciones, pero algún dato ha cambiado (updated).
- Sólo aparece en la primera colección (deleted).
- Sólo aparece en la segunda colección (new).

Si vemos la configuración del paso, lo primero que tenemos que indicar son las colecciones que vamos a comparar; una de ellas será la de referencia. A continuación tenemos que dar un nombre al campo que se añadirá para indicar el resultado de la comparación (identical, updated, deleted o new).

Por último tenemos que especificar los campos que se utilizarán para hacer las comparaciones. Por un lado, hay que definir qué campos identifican a los elementos para poder indicar si estos están presentes en una de las colecciones, o en las dos. En nuestro caso, el campo id.

Por otro lado, tenemos que marcar los campos que se utilizarán para comparar los elementos presentes en ambas colecciones, y determinar si son idénticos, o han sido modificados. En nuestro caso añadiremos tanto el nombre como los apellidos.


Es importante incidir en la necesidad de que ambas colecciones estén ordenadas por los campos indicados como clave para que el paso funcione correctamente.

Una vez unidos los flujos y clasificados los registros, podemos utilizar el paso Switch para hacer lo que necesitemos con cada uno de ellos. En nuestro caso nos interesaba mandar a un fichero de texto aquellos registros que fuesen nuevos o hubiesen sido modificados, y a otro los que hubiesen sido eliminados.


El aspecto del proceso completo sería algo así.


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);

jueves, 5 de enero de 2012

¡Soy Yuzz!

La semana que viene empiezo una nueva experiencia que afronto con muchísima ilusión y ganas de aprender: mi participación en el programa Yuzz.

Mi intención era ir contando en el blog cómo avanzaba el programa, pero voy a hacerlo de una forma diferente. Hace poco ya os hablé de Banana Campus, una red social pensada precisamente para que podamos compartir nuestras experiencias. Así que lo que he hecho es crear una experiencia en Banana, que os animo a que sigáis.

Por cierto, ¡Feliz año!

lunes, 2 de enero de 2012

Movilbank

Hoy en día, gracias a la guerra de las operadoras de telefonía móvil, y al incesante avance tecnológico del sector, seguro que la mayoría de nosotros tiene en su poder un buen puñado de terminales.

En mi caso concreto, estoy seguro de que hay un mínimo de 10 móviles vagabundeando por mi casa. Desde mi querido y añorado Siemens S25, hasta mi último cacharro, el Nokia N73. Porque generalmente, cada 18 meses más o menos, renovamos nuestro terminal, y el antiguo lo dejamos a mano en un cajón los primeros días, como si no quisiéramos darlo por muerto todavía. Pero la realidad es que poco a poco se va hundiendo en las profundidades con tantos otros trastos inservibles.

Así que cuando hace unos meses me pillé mi nuevo LG Optimus Black, decidí desde el primero momento que tenía que hacer algo práctico con mi N73. Seamos realistas, ni lo voy a usar cuando me vaya de juerga, ni es probable que se me estropee el nuevo y lo necesite como suplente, ni nada parecido. La mejor opción, sin duda, es venderlo.

Había visto muchas veces anuncios de empresas que se dedicaban a comprar teléfonos móviles, como Zonzoo. Si quieres conocer algunas no tienes más que poner "vende tu móvil" o algo similar en Google. A porrillo. Así que hice una pequeña comparación para ver por dónde andaba la cotización de mi pieza, y vi que podía sacarme unos 25 eurillos (30 si no hubiese dejado pasar varias semanas en el camino...). Teniendo en cuenta que pagué por él unos 70 hace ¿tres años?, no está nada mal.

La Web que me daba más dinero era Movilbank. El proceso de registro y venta fue simple y rápido; y pocos días después, dentro del intervalo horario que había solicitado, se presentó un mensajero en mi casa para recoger el móvil. Una semana más, y el dinero estaba en mi cuenta.

Así que la conclusión es que la experiencia ha sido muy positiva, y que recomiendo el servicio a todo el que se lo esté pensando. Sin mover un dedo te desharás de tus viejos móviles, y encima ganarás para unos tintitos con los amigos.

Por cierto, si os olvidáis de incluir el recibo de compra en el sobre no os traumaticéis. A mí me pasó con las prisas (había olvidado que venían a recoger el paquete...), y no supuso ningún problema.