viernes, 29 de julio de 2011

Nuevo diseño de "Say It Quietly"

El primer post donde anuncié el proyecto "Say It Quietly" mostraba una interfaz gráfica poco trabajada. No es que ahora haya trabajado mucho más, pero es que yo no soy diseñador y para mi el mundo de las imágenes es complicado.
Bueno, se que no es nada del otro mundo y que queda mucho por hacer. Algo mejorada con respecto al anterior diseño sí que está.
Espero que siga siendo intuitiva!
Creo que sea como fuere, se quedará así algún tiempecito...


¿Se te ocurre algún consejo o mejora?

Comportamientos del Canvas.drawBitmap()

Intentando dar soporte a mi aplicación Say It Quietly para todas las versiones de Android he dado con un problema al que, al comienzo de este post, no tengo muy claro como arreglarlo. Al menos se me ocurre una solución posible.

El problema:

Antes de nada, voy a intentar describir el problema. El texto es escrito en un bitmap. En cada repetición del thread voy recorriendo el bitmap con un Rect de ancho fijo (el alto de la pantalla, o el ancho si hablamos de landscape). El problema viene cuando el bitmap entra en escena, el Rect tiene valores negativos; o cuando el bitmap sale de escena, el right del Rect tiene valores que no pertenecen al bitmap.


En este gráfico se ve que tanto al aparecer el bitmap en pantalla como al salir de ella el Rect abarca un espacio vacío. Esto causa que en versiones de Android anteriores a la 2.2 drawBitmap estire la imagen al pintarla en la pantalla. Esto es lo que se ve:

Pantallazos de la entrada del texto en escena. Se puede apreciar como se estira la "S":
  


Pantallazos de la salida del texto de la escena. Se puede apreciar como se estira la "Y":
  



Solución fácil:

Es fácil porque no hay que pensar nada, simplemente programar para ajustar los Rect pasados al drawBitmap, de esta forma no habrá stretching.

Solución difícil:

Buscar si existe algún parámetro en el Canvas o el Paint o en algún sitio para cambiar el comportamiento del drawBitmap dependiendo de la versión de Android en que estemos corriendo la aplicación para que no haga stretching. Esto es "dificil" porque no se si existe algo así.

Cualquier idea para solucionar esto será bien recibida. Mientras tanto yo seguiré por el camino "fácil" que es un lío con los Rect, las posiciones y demás.

jueves, 21 de julio de 2011

Mi primer proyecto, "Say It Quietly"

Por fin puedo enseñaros algo de mi aplicación Say It Quietly.
La empecé hace ya casi un año, septiembre de 2010. No es que lleve todo este tiempo trabajando en ella. La he dedicado más bien poco tiempo.

La idea:

La idea es poder escribir un texto con la letra más grande que quepa en la pantalla. Parece muy sencillo, no? Pues veréis como la puedo liar parda. Al menos tengo la excusa que estoy aprendiendo... jeje

El market:

Después de unas búsquedas por Internet y el Market no encontré más que una aplicación que hiciera lo que había pensado, y casi incluso coincidimos en el nombre, la otra se llama "Say It Laud". Como veis los conceptos son diferentes jeje.

Funcionamiento:

Al ejecutar la aplicación aparece la pantalla donde escribir el texto a mostrar, elegir la velocidad de scroll, el color de fondo y color de texto (en próximas versiones) y un botón para cambiar a la otra pantalla.
La otra pantalla será la de mostrar el texto a pantalla completa quitando la barra de estado y haciendo el texto lo más grande posible.

Interfaz de usuario actual (en desarrollo, son sólo pruebas de diseño):
Pantalla 1:

Pantalla 2:


Problemas encontrados:

  • Selector de color. Todavía estoy pensando como hacer un selector de color chulo que no sea una simple lista con colores predefinidos ya que no he encontrado ningún widget para esto.
  • Cálculo del tamaño del texto. Hay que sacar el tamaño de la pantalla y la densidad para saber qué tamaño de letra establecer. Pongo un gráfico explicativo de las dimensiones de un glifo (glyph) sacado de la wikipedia.



    public void buildBitmap(int width, int height) {

        // Establezco algunos parámetros del  Paint que 
        // usaré para pintar el texto.
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setAntiAlias(true);
        paint.setTypeface(Typeface.MONOSPACE);

        // Obtengo la densidad para calcular el tamaño del texto
        float density = getContext().getResources()
                                  .getDisplayMetrics().density;
        // Establezco el tamaño del texto
        paint.setTextSize(height * density);

        // Obtengo las FontMetrics para ver cuanto desciende 
        // la fuente desde la línea base y así poder 
        // centrar el texto verticalmente.
        FontMetricsInt fmi = new FontMetricsInt();
        paint.getFontMetricsInt(fmi);

        // Para sacar el ancho del texto uso el measureText.
        // La función paint.getTextBounds() me devolvía el 
        // ancho pero añadiendo por delante espacio y quitándolo 
        // de detrás, así que se quedaba el último carácter 
        // un poco cortado.
        int measureWidth = (int)paint.measureText(mText);

        // Creo el bitmap del ancho del texto y 
        // el alto de la pantalla
        mBitmap = Bitmap.createBitmap(measureWidth, height,
                                                             Bitmap.Config.RGB_565);

        // Creo un canvas que me permitirá editar el bitmap creado
        Canvas canvas = new Canvas(mBitmap);
        // Color de fondo y pinto el fondo
        paint.setColor(mBackColor);
        canvas.drawRect(0, 0, measureWidth, height, paint);


        // Color de texto y pinto el texto.
        // Los parámetros del drawText son el texto, 
        // la posición X e Y y el objeto Paint.
        // La Y tiene que ser donde quieres que esté la 
        // línea base de tu texto. Y hay que contar con lo que 
        // desciende, es decir que si quieres que se vean los 
        // palitos de las "p", las "q" etc, tienes que hacer
        // esta operación.
        paint.setColor(mForeColor);
        canvas.drawText(mText, 0,
                                    (float)mBitmap.getHeight() - fmi.descent,
                                    paint);

    }

    Bueno en el código ya he explicado todo, sólo añadir que si alguien ve algo raro como el cálculo del tamaño del texto (height * density) que me lo diga porque no tengo muy claro que sea así, sólo he probado en 1 emulador y en mi HTC Desire y va bien!.
  • Procedimiento de mover el texto:
    • Un simple loop. Bloquea la aplicación. La primera versión del programa no podía regresar a la pantalla anterior porque el loop bloqueaba todo.
    • Un thread. Buena opción, pero no va suave. Ya podía regresar de la pantalla del texto pero el texto en movimiento iba a tirones y con problemas de refrescos. Todo debido a que todavía escribía el texto en la pantalla en cada iteración del thread.
    • Escribir el texto cada iteración. Demasiados drawText y new Paint en la hebra de pintado. Con un drawText en cada iteración ya era suficiente para ralentizar. Mejor crear bitmap con el texto en el constructor y que la hebra maneje objetos creados anteriormente.
Aún así no estoy contento con el resultado, sigo buscando como hacer esto para que el texto se vea fluido y suave (Corrijo: Sí estoy contento! he probado el programa en el HTC y va genial! el emulador es una castaña comparado)
De todas formas el siguiente paso será trabajar con las animaciones (clase Animator) y probar OpenGL.

    martes, 19 de julio de 2011

    Optimización del For (for-each)

    Hace tiempo que conozco el for-each y nunca me he parado a pensar qué utilidad podría tener ese for teniendo uno que hace lo que quiero y no necesito ni pensar como usarlo.
    En las plataformas móviles parece que es aún más útil usar estas estructuras por lo de ahorrar tiempo de proceso y no gastar bateria.

    El for-each puede usarse para colecciones que implementan el interfaz Iterable y para Arrays. Con las colecciones, cada elemento implementa en su interfaz las llamadas a hasNext() y next(). Con un ArrayList, un for escrito a mano es cerca de 3 veces más rápido, y para las colecciones el for-each será exáctamente equivalente al uso del for con los ArrayList.
    Ejemplos:

    static class Foo {
         int mSplat;
    }
    Foo[] mArray = ...

    public void zero() {
         int sum = 0;
         for (int i = 0; i < mArray.length; ++i) {
             sum += mArray[i].mSplat;
         }
    }

    public void one() {
        int sum = 0;
        Foo[] localArray = mArray;
        int len = localArray.length;

        for (int i = 0; i < len; ++i) {
            sum += localArray[i].mSplat;
        }
    }

    public void two() {
        int sum = 0;
        for (Foo a : mArray) {
            sum += a.mSplat;
        }
    }

    • zero() es la más lenta porque el JIT no puede optimizar el coste de pedir el tamaño del array cada iteración.
    • one() es más rápida. Guarda todo en variables locales, evitando tener que ir a buscarlas fuera del ámbito de la función. Y optimiza la petición del tamaño del Array.
    • two() es la más rápida para dispositivos sin JIT, y distinguiblemente mejor que la one() para los dispositivos con JIT. Usa el for-each introducido en la versión 1.5 del lenguaje de programación Java.


    En definitiva que me tendré que acostumbrar a usar for-each por defecto, salvo excepciones que necesite algo especial en iteraciones con ArrayList.



    JIT: Compilador al vuelo de la máquina virtual Dalvik (Just In Time compiler).

    Texto original y ejemplos via Use Enhanced For Loop Syntax


    Related Posts Plugin for WordPress, Blogger...