Cómo Personalizar el título de tu Aplicación Android

Hay varias maneras de personalizar el título de tu aplicación android, yo trataré de explicar aquí la forma mas sencilla que he encontrado.

La pesonalización del título de tu aplicación consiste básicamente en dos pasos:

  1. Crear un layout donde tendremos nuestro título.
  2. Decirle a nuestra aplicación que lo muestre cuando se cree.

Layout (con una imagen y un título):

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android&#8221;
android:id=”@+id/main_title_layout”
android:layout_width=”match_parent” android:layout_height=”wrap_content” android:orientation=”horizontal”
android:background=”@android:color/black”>
<ImageView
android:layout_width=”22dp”
android:layout_height=”22dp”
android:contentDescription=”@string/app_name”
android:src=”@drawable/icon”
android:gravity=”center_vertical” />

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:paddingLeft=”4dp”
android:text=”@string/app_name”
android:textSize=”12sp”
android:textColor=”@android:color/white”
/>
</LinearLayout>

Supongamos que guardamos el layout con el nombre de “main_title.xml”.

Código para usar el layout anterior en cada Activity

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Habilitamos la personalización del título
this.requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.main);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.main_title);
//Otras acciones de nuestra app
}

Bien, esta sería la teoría, pero en la práctica, si añades un fondo a al layout, se puede apreciar que no encaja del todo:

title_black_padding

Esto parece ser que ocurre, porque realmente lo que hacemos con las instrucciones anteriores es insertar ese layout como título, pero dentro del “título” original.
Para poder solucionarlo añadimos las siguientes instrucciones:

View view = getWindow().findViewById(R.id.main_title_layout);
((FrameLayout) view.getParent()).setPadding(0,0, 0,0);

Donde main_title_layout sería el identificador del Layout contenedor de nuestro título personalizado.

Eso es todo!

A disfrutar!

Google Analytic y EasyTraker

En el artículo de hoy vamos a ver como integrar en nuestra aplicación android las herramientas que nos proporciona google para monitorizar y analizar el uso de nuestras aplicaciones.
Seguro que todos conoceis como funcionan la herramienta de google analytic para monitorizar nuestras páginas web. De forma similar, google proporciona herramientas que nos permiten tanto monitorizar desde donde acceden al google play para instalar nuestras aplicaciones android y ver por qué páginas (pantallas en android) se mueven los usuarios.

Lo primero que tenemos que hacer, si no la tenemos ya, crear una cuenta en Google Analytic (esto queda fuera del tutorial). Desde la pestaña “Admisitracion” creremos un nuevo proyecto (o cuenta) seleccionando el tipo Aplicación introducimos los datos de la aplicación. Al finalizar se nos dará un número de identificación que debemos añadir a nuestro proyecto. Para ello crearemos un fichero analytics.xml que añadiremos en nuestra carpeta values.

<?xml version="1.0" encoding="utf-8" ?>
<resources>
  <!--Replace placeholder ID with your tracking ID-->
  <string name="ga_trackingId">UA-XXXXXXXX-X</string>
  <!--Enable automatic activity tracking-->
  <bool name="ga_autoActivityTracking">true</bool>
  <!--Enable automatic exception tracking-->
  <bool name="ga_reportUncaughtExceptions">true</bool>
</resources>

En la etiqueta de nombre ga_trackingId introduciremos el código devuelto por la página google analytics.

Ahora lo que haremos será añadir la librería libGoogleAnalyticsV2.jar a nuestro proyecto. Desde el administrador del SDK de android podemos bajar la librería seleccinándola desde la sección de extras:

AndroidSdkManager

Una vez que la tengamos descargada la añadimos a nuestras librerias añadiendolas al buil path de nuestro proyecto y además de esto debemos añadirla a la carpeta libs de nuestro proyecto, si no, aunque compile, no funcionará en el dispositivo android.

Ahora queda la última parte, añadir el código a nuestra aplicación. Lo que haremos será sobreescribir los métodos onStart y onStop de la siguiente forma en aquellos Activity que queramos monitorizar:

    @Override
    protected void onStart() {
        super.onStart();
        EasyTracker.getInstance().activityStart(this);
    }
    @Override
    protected void onStop() {
        super.onStop();
        EasyTracker.getInstance().activityStop(this);
    }

Así cada vez que el usuario acceda a los Activity se trazará, dándonos información de las pantallas mas utilizadas.

Espero que os haya sido de utilidad!

Mas info en: https://developers.google.com/analytics/devguides/collection/android/v2/?hl=es#tracking-methods

Saludos!!

TuLista 2.19 – Tu Lista de la compra

¡Versión 2.19 ya disponible en Google Play!

En esta versión se ha añadido soporte a varias listas de la compra.  Ahora podrás tener más de una lista: una para la tienda de al lado de tu casa, otra para el supermecado, otra para la ferretería, etc, etc

Para poder crear una lista de la compra nueva, sólo tienes que acceder a través del menú “Listas” que ahora aparecerá en el menú principal.

Captura10_v25_menu_principalCaptura11_v25_gestion_listas

Para crear una lista nueva solo tienes que introducir el nombre de la nueva lista y pulsar el botón +.

Para editar el nombre de una lista, borrar una lista, o mejor aun, duplicar una lista ya existente, sólo tienes que usar el menú contextual, que aparecerá manteniendo la pulsación sobre una lista del listado.

Captura9_v25_contextual_listas

¿Qué significa duplicar? Pues que creará una nueva lista pero incluyendo todos los productos que ya contiene en el histórico, así no tendrás que comenzar con una lista vacía si no quieres.

Recuerda que si quiere sugerir algo o simplemente saludar, puedes acceder a la página de facebook
https://www.facebook.com/TuLista

No la tienes aun, ¿a qué esperas a descargartela? ¡Es gratis!


Android app on Google Play

https://play.google.com/store/apps/details?id=es.blackpent.tulista

TuLista 2.17

¡Versión 2.17 ya disponible en Google Play!

¿Te gusta personalizar tu movil? Pues ahora TuLista te permite seleccionar entre tres temas nuevos.

Para ver la tercera tendrás que probar la aplicación 😛

Recuerda que si quiere sugerir algo o simplemente saludar, puedes acceder a la página de facebook
https://www.facebook.com/TuLista

No la tienes aun, ¿a qué esperas a descargartela? ¡Es gratis!


Android app on Google Play

https://play.google.com/store/apps/details?id=es.blackpent.tulista

TuLista 2.15

¡Versión 2.15 ya disponible en Google Play!

Además de lo que se mejoró en la versión 2.14

Para facilitarte la confección de la lista, se muestra en el historico los productos que has seleccionado actualmente con letra cursiva (o italica lis lo prefieres) junto con la cantidad, si has seleccionado mas de uno. De esta forma no tendrás que estar volviendo a la lista para revisar si habias o no añadido un producto.

Se ha cambiado el color de la entrada para que sea mas fácil de distingir.

Captura2_v22

 

Recuerda que si quiere sugerir algo o simplemente saludar, puedes acceder a la página de facebook
https://www.facebook.com/TuLista

No la tienes aun, ¿a qué esperas a descargartela? ¡Es gratis!


Android app on Google Play

https://play.google.com/store/apps/details?id=es.blackpent.tulista

TuLista 2.14

¡Versión 2.14 ya disponible en Google Play!

Para facilitarte la confección de la lista, se muestra en el historico los productos que has seleccionado actualmente con letra cursiva (o italica lis lo prefieres) junto con la cantidad, si has seleccionado mas de uno. De esta forma no tendrás que estar volviendo a la lista para revisar si habias o no añadido un producto.

Recuerda que si quiere sugerir algo o simplemente saludar, puedes acceder a la página de facebook
https://www.facebook.com/TuLista

No la tienes aun, ¿a qué esperas a descargartela? ¡Es gratis!


Android app on Google Play

https://play.google.com/store/apps/details?id=es.blackpent.tulista

TuLista 2.12 y 2.13

¡Versión 2.13 ya disponible en Google Play!

En la versión 2.12 se modifica el historico para que no se mueva mientras añades productos en tu lista, lo cual era un poco molesto. Seguidamente, la versión 2.13 añade una funcionalidad que facilita el acceso al sistema de votación de aplicaciones en Google Play.

No la tienes aun, ¿a qué esperas a descargartela? ¡Es gratis!


Android app on Google Play

https://play.google.com/store/apps/details?id=es.blackpent.tulista

TuLista 2.10

Hola de nuevo.

Ya han pasado bastantes días desde la última versión 2.9 pero aquí está la nueva versión 2.10!!

Novedades:  Se añade un panel de configuración con dos opciones:

– Se podrá deshabilitar el menú de edición de precio y cantidades cuando un producto es marcado en el panel principal. Además se deshabilita por defecto. Varios usuarios me han indicado que es un poco molesto.

– Se permite cambiar la imagen de fondo del listado de productos. Esta opción te permite seleccionar una imagen de la galería y ponerla de fondo. Estoy seguro que a muchos os gustará llevar vuestra propia imagen personalizada 😉

Espero que os guste la mejora.

Si tienes alguna sugerencia será bienvenida! 😀


Android app on Google Play

https://play.google.com/store/apps/details?id=es.blackpent.tulista

Usando MockCursor de Android

Estos dias me he decidido a implementar algunos tests en Android y una de las implementaciones que creo que ha merecido un post ha sido la implementación de un test que ha requerido la personalización de MockCursor, así que aquí está:

Contextualizo un poco:  La idea del test es probar un método que hace uso de un cursor el cual recibe por parámetro.  El método a probar utiliza este cursor para recorrer cada fila resultado y acceder a sus columnas.

El método a probar sería algo como el siguiente (prestad solo atención al uso del cursor)

private BigDecimal calcularTotal(Cursor cursor){
    BigDecimal total = new BigDecimal(0,MathContext.DECIMAL32);
    int unidades = 0;
    float precio = 0;
    if(cursor.moveToFirst()){
        if(DatabaseHelper.TRUE == cursor.getInt(cursor.getColumnIndex(ProductoDbAdapter.KEY_COMPRADO))){
            unidades = cursor.getInt(cursor.getColumnIndex(ProductoDbAdapter.KEY_UNIDADES));
            unidades = unidades <=1? 1:unidades;
            precio = cursor.getFloat(cursor.getColumnIndex(ProductoDbAdapter.KEY_PRECIO));
            if(precio > 0){
                total = total.add(new BigDecimal(unidades*precio));
            }
        }
        while(cursor.moveToNext()){
            if(DatabaseHelper.TRUE == cursor.getInt(cursor.getColumnIndex(ProductoDbAdapter.KEY_COMPRADO))){
                unidades = cursor.getInt(cursor.getColumnIndex(ProductoDbAdapter.KEY_UNIDADES));
                unidades = unidades <=1? 1:unidades;
                precio = cursor.getFloat(cursor.getColumnIndex(ProductoDbAdapter.KEY_PRECIO));
                if(precio > 0){
                    total = total.add(new BigDecimal(unidades*precio));
                }
            }
        }
    }
    return total;
}//Fin calcularTotal

Para testear este método de forma unitaria, es decir, aislada del resto, necesitamos que sea independiente del Cursor, y ahí es donde entra MockCursor para echarnos un cable. He de decir que MockCursor no es un JMock,  solo es una clase que implementa la interfaz  Cursor devolviendo UnsupportedOperationException para cada llamada de sus métodos., por lo tanto para usarlo hay que sobreescribir los métodos que vamos a usar.

Otro punto a destacar es que el método a probar es privado y no podremos probarlo tal cual. Mas adelante veremos como.

Bien, para probar nuestro método debemos personalizar el MockCursor, es decir, extenderlo y personalizar los métodos que queremos usar. Hay varias formas de hacerlo, una clase anónima, una clase anidada, etc. El lector que decida que solución es la que mas le convence.  Yo usaré una clase anidada al test. La idea de la implementación es simular el recorrido por los resultados obtenidos por una supuesta consulta y devolver los valores cuando se acceda a las columnas de cada resultado. El método a probar solo accede a tres columnas, así que no es demasiado complicado.

public class MockCursorAdapted extends MockCursor{
        int actualIndex = 0; //Indice para el recorrido por los resultados
        Map<Integer,String> entry;
        List<Map<Integer,String>> entryList;
        Map <String,Integer> columnIndexes;
        //Value initialization
        {
            //Column indexes: Se configuran las posiciones de las columnas en función de sus nombres.
            columnIndexes = new HashMap<String,Integer>();
            columnIndexes.put(ProductoDbAdapter.KEY_UNIDADES, 0);
            columnIndexes.put(ProductoDbAdapter.KEY_PRECIO, 1);
            columnIndexes.put(ProductoDbAdapter.KEY_COMPRADO, 2);
            //Creación de los resultados. Se crea una lista de mapas. Cada entrada en la lista es
            //un resultado. Y cada resultado, es un mapa con los valores de cada columna.  
            entryList = new ArrayList<Map<Integer,String>>();

            Map<Integer,String> m = new HashMap<Integer,String>();
            m.put(getColumnIndex(ProductoDbAdapter.KEY_UNIDADES),"1");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_PRECIO),"22.40");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_COMPRADO),"1");
            entryList.add(m);

            m = new HashMap<Integer,String>();
            m.put(getColumnIndex(ProductoDbAdapter.KEY_UNIDADES),"2");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_PRECIO),"5.50");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_COMPRADO),"1");
            entryList.add(m);

            m = new HashMap<Integer,String>();
            m.put(getColumnIndex(ProductoDbAdapter.KEY_UNIDADES),"1");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_PRECIO),"100.0");
            m.put(getColumnIndex(ProductoDbAdapter.KEY_COMPRADO),"1");
            entryList.add(m);
        }

        @Override
        public String getString(int columnIndex) {
            return getValue(columnIndex);
        }

        @Override
        public float getFloat(int columnIndex) {
            return Float.parseFloat(getValue(columnIndex));
        }

        @Override
        public int getInt(int columnIndex) {
            return Integer.parseInt(getValue(columnIndex));
        }

        /**
         * Obtención del valor de la columna indicada para la fila actual
         * @param columnIndex
         * @return
         */
        private String getValue(int columnIndex){
            entry = entryList.get(actualIndex);
            String value = entry.get(columnIndex);
            if(value == null){
                Log.e(TAG, "Sin valor para columna "+columnIndex+" y actualIndex "+actualIndex+" entrada "+entry.toString());
            }
            return value;
        }

        @Override
        public int getColumnIndex(String columnName) {
            return columnIndexes.get(columnName);
        }

        @Override
        public boolean moveToFirst() {
            return true;
        }

        @Override
        public boolean moveToNext() {
            // TODO Auto-generated method stub
            return ++actualIndex < entryList.size();
        }
    }//FIN MockCursorAdapted

Bien, ya tenemos el Mock del cursor personalizado. Creando la clase que ejecutará el test ya lo tendriamos resuelto… bueno… falta el acceso al método privado…

Bien, la forma de ejecutar el método privado es mediante reflexión, digamos que le pedimos a la clase que nos de el método que tiene dicho nombre. De forma muy resumida y sin controlar excepciones:

 Cursor cursorAdapted = new MockCursorAdapted();
 ActividadConMetodoCalcularTotal mActivity = new ActividadConMetodoCalcularTotal();
 Method m = this.mActivity.getClass().getDeclaredMethod("calcularTotal", new Class[]{Cursor.class});
 m.setAccessible(true); //Le decimos que nos deje ejecutarlo desde fuera 😛
 BigDecimal resultado = (BigDecimal) m.invoke(mActivity , new Object[]{cursorAdapted});

Listo!

Un apunte mas:

OJO! MockCursor está disponible a partir de la API 8, por lo que debeis ejecutar los test en un emulador con la versión correspondiente (creo que a partir de la 2.3.3). Si no lo haceis, dará un error en ejecución de NoClassDefFoundError sobre la clase personalizada (en este caso $MockCursorAdapted). Mirando mas detenidamente las trazas se puede ver al principio un warning “unable to resolve superclass” refiriendose a MockCursor… este error me ha tenido ocupado un buen rato.

Espero que os haya gustado la aportación, suerte con los test!