image

El gran libro de Android Avanzado

Acceda a www.marcombo.info

para descargar gratis

los fundamentos de JavaScript que todo

informático debería conocer

complemento imprescindible de este libro

Código: ANDROID8

El gran libro de Android Avanzado

Jesús Tomás

Gonzalo Puga

David Santamaría

Jorge Barroso

image

El gran libro de Android Avanzado

5a edición, 2018

©2018 Jesús Tomás, Gonzalo Puga, David Santamaría y Jorge Barroso

©2018 MARCOMBO, S.A.

www.marcombo.com

Diseño de la cubierta: ENEDENÚ DISEÑO GRÁFICO

«Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra».

eISBN: 978-84-267-2738-1

A mi madre por creer en mí.

JESÚS TOMÁS

A Sole y Gonzalo, pilares y motores de mi vida.

GONZALO PUGA

A Carmen, por compartir este viaje conmigo.

DAVID SANTAMARÍA

Al equipo de Karumi que cada día me enseñáis algo nuevo y fascinante. Y sin vosotros no hubiera sido posible.

JORGE BARROSO

Índice general

¿Cómo leer este libro?

CAPÍTULO 1.Diseño avanzado de la interfaz de usuario

1.1.Acceder a objetos globales de la aplicación

1.1.1.La clase Application

1.2.Material Design

1.2.1.Definición de la paleta de colores de la aplicación

1.3.RecyclerView

1.4.Fragments

1.4.1.Insertar fragments desde XML

1.4.2.Insertar fragments desde código

1.4.3.Comunicación e intercambio de fragments

1.5.Otros elementos de la interfaz de usuario

1.5.1.Menús contextuales

1.5.2.La barra de acciones (ActionBar / Toolbar)

1.5.2.1. Barra de acciones con ActionBar

1.5.2.2. Barra de acciones con Toolbar

1.5.2.3. Configurar la barra de acciones desde un fragment

1.5.3.Vistas animadas: CoordinationLayout, AppBarLayout, FloatingActionButton y SnackBar

1.5.4.Pestañas con TabLayout

1.6.Navigation Drawer

1.6.1.Ocultar elementos de la interfaz de usuario

1.7.Servicios de búsquedas

1.7.1.Añadiendo preferencias con PreferenceFragment

1.8.Animaciones

1.8.1.Animaciones de vistas: transiciones entre actividades

1.8.1.1. Aplicando animaciones de vistas en Audiolibros

1.8.2.Animaciones de propiedades

1.8.2.1. El motor básico de animación: ValueAnimator

1.8.2.2. Automatizando las animaciones: ObjectAnimator

1.8.2.3. Combinando animaciones: AnimatorSet

1.8.2.4. Definiendo animaciones en XML

1.8.2.5. Nuevas propiedades de la clase View

1.8.2.6. Aplicando animaciones de propiedades en Audiolibros

1.8.3.Animaciones en RecyclerView

1.9.Otros aspectos introducidos en la versión 5.0

1.9.1.Extraer paleta de colores desde imágenes

1.9.2.Uso de gráficos vectoriales

CAPÍTULO 2.Diseño personalizado de vistas

2.1.Algunos conceptos básicos

2.2.Una vista como la composición de varias vistas

2.2.1.Creación de escuchadores de eventos

2.3.Modificación de vistas existentes

2.3.1.Algo más de información sobre TextView

2.4.Creación de nuevos atributos XML

2.5.Una vista creada desde cero

2.5.1.Diseño y dibujo de la vista

2.5.2.Gestión de eventos

2.5.3.Cómo Android dibuja las vistas y obtiene sus tamaños

2.5.4.Interactuando con otros objetos

2.6.Creación de widgets de escritorio

2.6.1.Pasos a seguir para crear un widget

2.6.1.1. Definir las características del widget

2.6.1.2. Diseñar el layout del widget

2.6.1.3. Crear una clase descendiente de AppWidgetProvider

2.6.1.4. Declarar el widget en AndroidManifest

2.6.1.5. Crear una actividad para configurarlo

2.6.2.Creación de un widget de escritorio sencillo

2.6.3.Actualizando el widget de escritorio

2.6.4.Actuando ante el evento onClick

2.6.5.Añadiendo una actividad de configuración

2.7.Notificaciones con widgets personalizados

CAPÍTULO 3.Hilos de ejecución

3.1.Hilos de ejecución en Android

3.1.1.Programación basada en eventos y el hilo de ejecución de usuario

3.1.1.1. Cola de eventos y bucle de eventos

3.1.2.Concurrencia en programación orientada a eventos

3.1.2.1. Hilos para el manejo de eventos

3.1.2.2. Hilos en segundo plano sin acceso a la GUI

3.1.2.3. Utilizando runOnUiThread para actualizar la interfaz gráfica

3.1.2.4. Utilizando post() para actualizar la interfaz gráfica

3.1.2.5. Utilizando Handler para actualizar la interfaz gráfica

3.1.2.6. Receptores de anuncios para actualizar la interfaz gráfica

3.2.La clase AsyncTask

3.2.1.Extendiendo AsyncTask

3.2.2.Secuencia de operaciones

3.2.3.Inicialización y ejecución de AsyncTask

3.2.4.Cancelar una tarea asíncrona

3.2.5.Estados de una tarea asíncrona

3.2.6.Proteger la tarea asíncrona del cambio de orientación

3.2.7.Ejecución concurrente de tareas asíncronas

3.2.8.Gestionando las excepciones

3.2.9.Resumen de errores comunes con AsyncTask

3.2.10. Aplicaciones comunes de AsyncTask

3.3.Servicios en Android

3.3.1.Introduciendo los Servicios

3.3.1.1. Creación y ejecución

3.3.1.2. Ciclo de vida

3.3.2.Servicios enlazados

3.3.2.1. Administración del ciclo de vida de un servicio enlazado

3.3.2.2. Comunicándonos con un servicio enlazado

3.3.2.3. Comunicándonos con un servicio local (Local Binding)

3.3.2.4. Comunicándonos con un servicio remoto

3.3.3.Devolviendo el resultado de la ejecución del servicio

3.3.3.1. Comunicación mediante una interfaz

3.3.3.2. Comunicación de resultados mediante Intents

3.3.4.Detectando comunicaciones sin gestionar

3.3.5.¿Por qué utilizar servicios para ejecuciones asíncronas?

3.3.5.1. Eligiendo una técnica asíncrona

3.3.6.Aplicaciones de los servicios

3.3.7.Nuevas restricciones a la ejecución en segundo plano

3.3.7.1. Consideraciones genéricas

3.3.7.2. Restricciones sobre los servicios

3.3.7.3. Restricciones sobre receptores de anuncios

3.3.7.4. Restricciones sobre ubicación en segundo plano

3.3.7.5. Guía de migración

3.3.7.6. Poner servicios en primer plano

3.4.Animaciones con SurfaceView

3.4.1.Arquitectura de gráficos en Android

3.4.2.¿Qué es la clase SurfaceView?

3.4.3.Llamando al método onDraw()

3.4.4.Programación con SurfaceViews

3.4.5.Estructura de la aplicación

CAPÍTULO 4.Introducción al Testing en Android

4.1.Introducción al desarrollo guiado por test

4.1.1.Realización de test en Android Studio

4.2.Test unitarios

4.2.1.Test unitario con parámetros

4.2.2.Test que captura excepciones

4.2.3.Creación de las clases MathExpression y MathCalculator

4.2.4.Test doble

4.2.5.Creación de la clase CalculatorPresenterImp

4.3.Test de interfaz de usuario

4.3.1.Test de interfaz de usuario con Espresso

4.3.2.Test de instrumentación con Firebase Test Lab

CAPÍTULO 5.Introducción a Kotlin

5.1.Introducción

5.1.1.Un poco de historia

5.1.2.Características de Kotlin

5.1.2.1. Conciso

5.1.2.2. Legible y expresivo

5.1.2.3. Código más seguro

5.1.2.4. Interoperable con Java

5.1.3.Uso de Kotlin en Android Studio

5.2.Variables y constantes

5.3.Estructuras de control

5.4.Funciones

5.4.1.Lambdas y funciones anónimas

5.4.2.Funciones inline

5.5.Clases

5.5.1.Clases de datos

5.5.2.Declaraciones desestructuradas

5.5.3.Extensiones

5.5.4.Type Alias

5.6.Anko

5.6.1.Funciones comunes

5.6.2.Layouts

5.7.Tratamiento de null

5.7.1.Añadiendo una comprobación previa

5.7.2.Llamada segura mediante operador ?

5.7.3.El operador Elvis ?:

5.7.4.El operador !!

5.7.5.El operador let

5.7.6.Interoperabilidad con Java

5.7.7.Nulidad y colecciones

5.7.8.Lateinit vs lazy

5.7.9.Cómo evitar el operador !!

5.7.9.1. Utiliza val en lugar de var

5.7.9.2. Utiliza lateinit

5.7.9.3. Utiliza el operador let

5.7.9.4. Crear funciones globales para gestionar casos complejos

5.7.9.5. Utilizar el operador Elvis

5.7.9.6. Reafirmarte en tu solución

5.8.Chequeos de tipo y Smart Cast

5.8.1.Chequeo de tipos

5.8.2.Smart Cast

5.8.3.Casting explícitos

5.9.Colecciones

5.9.1.Principales interfaces

5.9.2.Listas

5.9.3.Conjuntos

5.9.4.Mapas

5.9.5.Funciones sobre colecciones

5.10. apply vs with

5.10.1. with

5.10.2. apply

5.10.3. Diferencias entre apply y with

5.11. Clases abstractas selladas y enumeradas

5.11.1. Clases abstractas

5.11.2. Clases anidadas

5.11.3. Clases selladas (sealed)

5.11.4. Clases enumeradas

5.12. Métodos estáticos

5.13. Inmutabilidad en propiedades de clases

5.14. Buenas prácticas de programación con Kotlin

5.14.1. Soporte para expresiones (idioms) y patrones

5.14.2. Programación funcional

5.14.3. Utilización de expresiones

5.14.4. Funciones de extensión para utilidades

5.14.5. Argumentos nombrados en lugar de setter fluido

5.14.6. Utilizar apply() para inicializar objetos de agrupamiento

5.14.7. No utilizar la sobrecarga en parámetros por defecto

5.14.8. Gestionar apropiadamente la nulidad

5.14.8.1. Evitar las comprobaciones if-null

5.14.8.2. Evitar las comprobaciones de tipo if-type

5.14.8.3. Evitar las reafirmaciones de not-null !!

5.14.8.4. Utiliza let

5.14.9. Mapeado conciso con funciones de expresión simple

5.14.10. Hacer referencia a los parámetros del constructor en la inicialización de propiedades

5.14.11. Desestructuración

5.14.12. Creación de estructuras de datos

5.14.13. Definir varias clases en un fichero

5.14.14. Value Objects

CAPÍTULO 6.Arquitecturas de software

6.1.S.T.U.P.I.D.

6.1.1.Singleton Invasion

6.1.2.Tight coupling

6.1.3.Untestability

6.1.4.Premature Optimization

6.1.5.Indescriptive Naming

6.1.6.Duplication

6.2.Los principios S.O.L.I.D.

6.2.1.Single Responsibility Principle

6.2.2.Open/Closed principle

6.2.3.Liskov substitution principle

6.2.4.Interface segregation principle

6.2.5.Dependency inversion principle

6.3.Patrones de diseño comunes

6.3.1.El patrón de delegación (Delegation)

6.3.2.El patrón Observer

6.3.2.1. El patrón Observer con un solo escuchador

6.3.2.2. El patrón Observer con varios escuchadores

6.3.3.El patrón Null Object

6.3.4.El patrón Command

6.3.5.El patrón Adapter

6.3.6.El patrón Decorator

6.3.6.1. El patrón decorador en Kotlin

6.3.7.El patrón Builder

6.3.8.El patrón Singleton

6.3.8.1. El patrón Singleton en Kotlin

6.4.Patrones de arquitectura

6.4.1.Model View Controller

6.4.2.Model View Presenter

6.4.3.Modelo View View-Model

6.5.Arquitectura CLEAN

6.5.1.Reglas de dependencias

6.5.2.Interface Adapters

6.5.3.Capas

6.5.3.1. Capa de presentación

6.5.3.2. Casos de uso y capa de dominio

6.5.3.3. Capa de datos

6.6.Data Binding

¿Cómo leer este libro?

Este libro se ha estructurado en seis capítulos que abordan temas específicos sobre la programación en Android. No es precisa una lectura secuencial, el lector puede ir directamente al capítulo que le interese.

En este libro se da por supuesto que el lector tiene experiencia en el desarrollo de aplicaciones en Android. No se tratan temas relativos al desarrollo básico como los componentes o estructura de las aplicaciones. Si el lector está interesado en un texto que aborde la programación en Android desde el principio, le recomendamos El gran libro de Android publicado en esta misma editorial. También puede ser de interés la lectura de otros libros de esta colección como “Firebase: trabajando en la nube”, “Plataformas Android: Wear, TV, Auto y Google Play Games” o “Android Things y Visión Artificial”.

El libro que tienes entre las manos no ha sido concebido solo para ser leído. Es más bien una guía estructurada que te irá proponiendo una serie de ejercicios, actividades, vídeos explicativos, test de autoevaluación, etc. Parte de este material y algunos recursos adicionales están disponibles en la web www.androidcurso.com. En ella se publicarán las novedades, erratas e información complementaria relativas a este libro. Además encontrarás:

Material adicional sobre Android: Encontrarás, además, nuevos tutoriales, vídeos, referencias, etc., no incluidos en el libro.

Código abierto de proyectos Android: Muchos alumnos que han realizado cursos con nosotros han tenido la generosidad de compartir sus proyectos. Te recomendamos que consultes la lista de proyectos disponibles de código abierto: puedes aprender mucho estudiando su código.

Cursos online: Si te interesa ampliar tu formación, puedes matricularte en cursos sobre Android impartidos por la Universidad Politécnica de Valencia. Incluso puedes obtener un título de Especialización o de Máster de forma 100% online.

A lo largo del libro se utilizan los siguientes iconos para indicar distintos tipos de recursos:

image Objetivos: Antes de empezar cada capítulo lee con detenimiento la introducción y los objetivos.

image Ejercicio: La mejor forma de aprender es haciendo. No tendrás más que ir siguiendo los pasos uno tras otro para descubrir cómo se resuelve el ejercicio propuesto. Para que no se te haga pesado teclear todo el código, te proponemos que lo copies y pegues desde la página web del curso.

image Práctica: Este será el momento de que tomes la iniciativa y trates de resolver el problema que se propone. Recuerda que para aprender hay que practicar.

image Vídeo: Enlaces a YouTube (algunos grabados por los autores) donde se muestran aspectos clave de los temas desarrollados.

image Enlaces de interés: Internet te será de gran ayuda para completar la información necesaria para programar en Android. Te proponemos las páginas más interesantes de cada apartado.

image Preguntas de repaso: ¿Has comprendido correctamente los aspectos clave? Sal de dudas haciendo los test de autoevaluación.

image Trivial programación Android: Instálate esta app y mide tus conocimientos jugando en red contra otros oponentes.

CAPÍTULO 1.

Diseño avanzado de la interfaz de usuario

Por JESÚS TOMÁS

Las diferentes versiones de Android han ido incorporando nuevas herramientas para el diseño de la interfaz de usuario. A partir de la versión 3.0 se añaden nuevos mecanismos de interacción con el usuario, como la barra de acciones o los fragments. La mayoría de estos mecanismos han sido introducidos para permitir un diseño que se adapte a diferentes tamaños de pantalla. Con la versión 5.0, Google lanza Material Design, un marco de diseño que no solo se aplica a las aplicaciones móviles, si no que Google lo está aplicando a aplicaciones Web, de escritorio o para wearable. Con Material Design se incorporan nuevas vistas y formas de trabajar que serán estudiadas en este capítulo. Aunque estas novedades aparecen en diferentes versiones, pueden usarse en todos los dispositivos gracias a las librerías de compatibilidad.

En esta unidad comenzaremos describiendo el uso de la clase Application para almacenar información global a una aplicación. Luego se introducen los principios de diseño usados en Material Design.

En el tercer apartado repasaremos la vista RecyclerView, que nos permite mostrar una lista o cuadrícula de elementos deslizables.

En el cuarto apartado aprenderemos a usar fragments. Su utilización es fundamental, dado que el nuevo planteamiento de diseño de la interfaz de usuario en Android se basa en fragments. Se trata de elementos constructivos básicos que podremos combinar dentro del layout de una actividad.

En el quinto apartado se introduce la barra de acciones que se muestra en la parte superior de las aplicaciones. Con Material Design aparece Toolbar, muy similar a la anterior, pero incorpora algunas mejoras. También se describe cómo podemos incorporar un servicio de búsquedas y pestañas dentro de esta barra.

En el siguiente apartado incorporaremos un nuevo elemento a nuestra interfaz de usuario, el Navigation Drawer. Este mecanismo de navegación consiste en un menú lateral deslizable que podemos mostrar pulsando un botón de la barra de acciones.

En el último apartado veremos las alternativas para introducir animaciones. Comenzaremos repasando las animaciones de vistas y cómo aplicarlas para introducir transiciones entre actividades. Finalmente, se estudiarán con detalle las animaciones de propiedades. Una API que nos facilitará la realización de animaciones sobre cualquier tipo de objeto.

image Objetivos:

Mostrar el uso de la clase Application para almacenar información global de la aplicación.

Introducir los principios de diseño usados en Material Design.

Describir el uso de RecyclerView para visualizar una lista de vistas.

Mostrar el uso de fragments para reutilizar elementos de la IU.

Aprender a intercambiar dinámicamente los fragments mostrados en una actividad.

Describir el uso de la barra de acciones.

Enumerar los pasos a seguir para insertar un Navigation Drawer.

Implementar un servicio de búsquedas en un RecyclerView.

Repasar las alternativas para hacer animaciones en Android.

Mostrar cómo podemos hacer transiciones entre actividades mediante animaciones de vistas.

Describir la API para animaciones de propiedades.

Introducir otros aspectos interesantes como la extracción de paleta de colores o el uso de gráficos vectoriales.

1.1. Acceder a objetos globales de la aplicación

Cada uno de los componentes de una aplicación se escribe en una clase separada. Esto hace que en muchas ocasiones resulte complicado compartir objetos entre estos componentes.

Para poder acceder a una información global desde cualquier clase de nuestro proyecto, podemos utilizar el modificador static. De esta forma, no será necesario conocer la referencia a un objeto de la clase, solo con indicar el nombre de la clase podremos acceder a esta información.

Otra alternativa, muy similar a la anterior, es utilizar el patrón Singleton. Una clase definida con este patrón solo dispondrá de una instancia a la que se podrá acceder desde cualquier sitio utilizando un método estático. Lo veremos en el capítulo 3. Una tercera alternativa específica de Android consiste en crear un descendiente de la clase Application. En el siguiente punto se explica cómo hacerlo.

1.1.1. La clase Application

Esta clase ha sido creada en Android para almacenar información global a toda la aplicación.

image Vídeo[Tutorial]: La clase Application en Android.

Veamos cómo usarla en tres pasos:

1.Crea un descendiente de Application que contenga la información global y los métodos asociados para acceder a esta información. Mira el ejemplo:

public class Aplicacion extends Application {

private int saldo;

@Override public void onCreate() {

SharedPreferences pref = getSharedPreferences("pref", MODE_PRIVATE);

int saldo = pref.getInt("saldo_inicial", 0);

}

public int getSaldo(){

return saldo;

}

public void setSaldo(int saldo){

this.saldo=saldo;

}

}

En nuestra aplicación queremos que el usuario disponga de un saldo de puntos, con los que podrá ir desbloqueando ciertas características especiales. La clase Application es descendiente de Context, por lo que tendremos acceso a todos los métodos relativos a nuestro contexto. Entre estos métodos se incluye getSharedPreferences, para acceder a un fichero de preferencias almacenado en la memoria interna de nuestra aplicación. Además de poder sobrescribir el método onCreate(), la clase Application permite sobrescribir los siguientes:

onCreate() llamado cuando se cree la aplicación. Puedes usarlo para inicializar los datos.

onConfigurationChanged(Configuration nuevaConfig) llamado cuando se realicen cambios en la configuración del dispositivo, mientras que la aplicación se está ejecutando.

onLowMemory() llamado cuando el sistema se está quedando sin memoria. Trata de liberar toda la memoria que sea posible.

onTrimMemory(int nivel) (desde nivel API 14) llamado cuando el sistema determina que es un buen momento para que una aplicación recorte memoria. Esto ocurrirá, por ejemplo, cuando está en el fondo de la pila de actividades y no hay suficiente memoria para mantener tantos procesos en segundo plano. Además, se nos pasa como parámetro el nivel de necesidad. Algunos valores posibles son: TRIM_MEMORY_COMPLETE, TRIM_MEMORY_BACKGROUND, TRIM_MEMORY_MODERATE, …

2.Registra la clase creada en AndroidManifest. Para ello busca la etiqueta <application> y añade el atributo name, con el nombre de la clase creada:

...

<application

android:name="Aplicacion"

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme">

...

3.Puedes obtener una referencia a tu clase Application con este código:

Aplicacion aplicacion = (Aplicacion) contexto.getApplicationContext();

Donde contexto es una referencia a la clase Context. En caso de estar en un descendiente de esta clase (como Activity, Service,…) no es necesario disponer de esta referencia, la misma clase ya es un Context. Por lo tanto, podríamos escribir:

Aplicacion aplicacion = (Aplicacion) getApplication();

o incluso directamente:

int miSaldo = ((Aplicacion) getApplication()).getSaldo();

image Ejercicio: Acceso a información global con la clase Application.

1.Crea un nuevo proyecto con los siguientes datos:

Application name: Audiolibros

Package name: com.example.audiolibros

Project location: <ruta a mis proyectos>\Audiolibros_v1

image Phone and Tablet

Minimum SDK: API 16 Android 4.1 (Jelly Bean)

Add an activity: Basic Activity

Utiliza los valores por defecto en el resto de los campos.

2.Vamos a empezar creando una nueva clase. Para ello pulsa con el botón derecho sobre el java / com.example.audiolibros y selecciona New / Java Class. Introduce como nombre de la clase Aplicacion. En ella vamos a almacenar dos objetos que queremos usar globalmente en toda la aplicación, listaLibros y adaptador. Reemplaza su código por el siguiente:

public class Aplicacion extends Application {

private List<Libro> listaLibros;

private AdaptadorLibros adaptador;

@Override

public void onCreate() {

super.onCreate();

listaLibros = Libro.ejemploLibros();

adaptador = new AdaptadorLibros (this, listaLibros);

}

public AdaptadorLibros getAdaptador() {

return adaptador;

}

public List<Libro> getListaLibros() {

return listaLibros;

}

}

Nota: Tras incluir nuevas clases tendrás que indicar los imports adecuados. Pulsa «Alt+Intro» en Android Studio para que lo haga automáticamente.

Aparecerán algunos errores dado que las clases Libro y AdaptadorLibros aún no han sido creadas. No te preocupes, lo haremos más adelante.

3.Registra el nombre de la clase en AndroidManifest, añadiendo la línea que aparece en negrita.

<application

android:name="Aplicacion"

android:allowBackup="true"

...

4.El siguiente paso va a ser crear la clase Libro, que definirá las características de cada audiolibro. Para ello usa el siguiente código:

public class Libro {

pribate String titulo;

public String autor;

public int recursoImagen;

public String urlAudio;

public String genero; // Género literario

public Boolean novedad; // Es una novedad

public Boolean leido; // Leído por el usuario

public final static String G_TODOS = "Todos los géneros";

public final static String G_EPICO = "Poema épico";

public final static String G_S_XIX = "Literatura siglo XIX";

public final static String G_SUSPENSE = "Suspense";

public final static String[] G_ARRAY = new String[] {G_TODOS, G_EPICO, G_S_XIX, G_SUSPENSE };

public Libro(String titulo, String autor, int recursoImagen, String urlAudio, String genero, Boolean novedad, Boolean leido) {

this.titulo = titulo; this.autor = autor;

this.recursoImagen = recursoImagen; this.urlAudio = urlAudio;

this.genero = genero; this.novedad = novedad; this.leido = leido;

}

public static List<Libro> ejemploLibros() {

final String SERVIDOR = "http://mmoviles.upv.es/audiolibros/";

List<Libro> libros = new ArrayList<Libro>();

libros.add(new Libro("Kappa", "Akutagawa",

R.drawable.kappa, SERVIDOR+"kappa.mp3",

Libro.G_S_XIX, false, false));

libros.add(new Libro("Avecilla", "Alas Clarín, Leopoldo",

R.drawable.avecilla, SERVIDOR+"avecilla.mp3",

Libro.G_S_XIX, true, false));

libros.add(new Libro("Divina Comedia", "Dante",

R.drawable.divinacomedia, SERVIDOR+"divina_comedia.mp3",

Libro.G_EPICO, true, false));

libros.add(new Libro("Viejo Pancho, El", "Alonso y Trelles, José",

R.drawable.viejo_pancho, SERVIDOR+"viejo_pancho.mp3",

Libro.G_S_XIX, true, true));

libros.add(new Libro("Canción de Rolando", "Anónimo",

R.drawable.cancion_rolando, SERVIDOR+"cancion_rolando.mp3",

Libro.G_EPICO, false, true));

libros.add(new Libro("Matrimonio de sabuesos", "Agata Christie",

R.drawable.matrimonio_sabuesos,SERVIDOR+"matrim_sabuesos.mp3",

Libro.G_SUSPENSE, false, true));

libros.add(new Libro("La iliada", "Homero",

R.drawable.iliada, SERVIDOR+"la_iliada.mp3",

Libro.G_EPICO, true, false));

return libros;

}

}

Nota: Tras incluir nuevas clases tendrás que indicar los imports adecuados. Para que Android Studio lo haga automáticamente pulsa «Alt+Intro».

Observa cómo se han añadido diferentes campos para definir las características de un audiolibro. La carátula del libro se almacena, en local, como un recurso de imagen, mientras que se accede a su audio por medio de una URL. El constructor nos permite inicializar todos los campos del audiolibro. También se ha incluido el método estático ejemploLibros() que nos devuelve una lista de audiolibros que contiene varios ejemplos.

5.Nos falta añadir en los recursos las carátulas de los libros. Descarga el fichero http://mmoviles.upv.es/audiolibros/imagenes.rar. Descomprime las imágenes y cópialas en la carpeta app/src/main/res/drawable del proyecto.

6.La clase AdaptadorLibros no ha sido declarada. Lo haremos más adelante. Por lo tanto, no podremos ejecutar la aplicación hasta terminar ese apartado.

1.2. Material Design

A partir de la versión 5.0 de Android (API 21), se introduce Material Design. Se trata de una guía para el diseño visual de las aplicaciones, que Google no quiere aplicar exclusivamente a dispositivos móviles, sino que pretende utilizar material design en todo tipo de contenidos digitales (páginas Web, aplicaciones para ordenadores, vídeos, …).

image

Se basa en diseños y colores planos. Uno de sus principios es dar peso o materialidad a los elementos del interfaz de usuario. Para ello va a tratar de darle volumen o profundidad utilizando sombras, capas y animaciones. Observa como los botones flotantes (ver botón con estrella en el siguiente ejercicio) se visualizan con una sombra para que parezcan que están en una capa superior y suelen visualizarse con animaciones. La idea es que parezcan que están construidos de material físico. Para más información puedes consultar la especificación de Material Design que se incluye en enlaces de interés.

image

image Vídeo[Tutorial]: Tutorial Desarrollo de apps para Android con Material Design: Características.

Desde el punto de vista de la programación destacamos que se incorporan nuevos widgets : RecyclerView, Toolbar, FloatingActionButton, … Explicaremos su uso a lo largo de este capítulo.

Cuando creas un nuevo proyecto con Android Studio, tendrá un diseño inicial basado en Material Design. Por ejemplo, utilizará un tema que hereda de android:Theme.Material. Si has escogido una actividad del tipo Basic Activity, se incorporan varios widgets basados en este diseño, como: Toolbar o FloatingActionButton. Además, se incluyen las dependencias adecuadas para poder usar estos widgets:

dependencies {

implementation 'com.android.support:appcompat-v7:26.1.0'

implementation 'com.android.support:design:26.1.0'

implementation 'com.android.support:recyclerview-v7:26.1.0'

}

NOTA: Es interesante que reemplaces :26.1.0 por la última versión disponible

La primera es la librería de compatibilidad v7 que seguro que ya conoces. Incorpora las clases más importantes como: AppCompatActivity, Toolbar o CardView. La segunda es la librería de compatibilidad de diseño. Incorpora otras clases como: FloatingActionButton, AppBarLayout, TabLayout, NavigationDrawer O Snackbar. La tercera incorpora la clase RecyclerView. Gracias al uso de estas librerías podremos utilizar estas clases con un nivel mínimo de API 7, a pesar de que la mayoría han sido introducidas en la versión 5.0 de Android.

Hay una diferencia entre estas librerías: Las clases definidas en la librería de compatibilidad v7 son del API de Android. Cuando en una aplicación la versión mínima de API sea mayor o igual que 21 (v5.0) ya no tiene sentido usar esta librería. Por el contrario, las clases definidas en la librería de diseño son solo de esta librería. Has de lincarla siempre que necesites una de sus clases.

image Enlaces de interés:

Especificaciones de diseño de Material Design:
http://www.google.com/design/spec/material-design/introduction.html

Crear aplicaciones con Material Design:
http://developer.android.com/intl/es/training/material/index.html

Aplicación de ejemplo con diseño Material Design (Web / Android):
https://polymer-topeka.appspot.com/

https://play.google.com/store/apps/details?id=com.chromecordova.Topeka

1.2.1. Definición de la paleta de colores de la aplicación

Uno de los principios que se definen en Material Design es el uso del color. Google propone usar colores vivos y alegres. En la Web de Material Design se nos proponen algunas paletas de ejemplo1. Cada aplicación ha de definir su propia paleta de colores que la diferencie del resto de aplicaciones. Incluso la barra de estado de Android cambiará para que combine con los colores de nuestra aplicación. Puedes observar en la imagen inferior cómo ha cambiado el uso del color en la aplicación Gmail. A la izquierda se muestra en una versión 4.x y a la derecha tras la aplicación de Material Design. Esta aplicación ha decidido usar una tonalidad de naranja como color primario o característico. Observa como la barra de estado también se muestra en un color similar algo más oscuro.

image

Por lo tanto, si tu aplicación va a seguir las especificaciones de Material Design, lo primero que has de hacer es escoger la paleta de colores que va a utilizar. Este color puedes seleccionarlo entre la paleta de ejemplo antes comentada, aunque si la empresa para que realizas la aplicación tiene un color de marca, lo ideal es que escojas este color.

image Ejercicio: Definir la paleta de colores de la aplicación.

1.Para definir los colores de nuestra aplicación vamos a utilizar la herramienta que encontrarás en: https://www.materialpalette.com/

image

2.Es muy sencilla de manejar, escoge primero el color primario para tu aplicación y a continuación el color de resalte o acentuación. Observa como a la derecha aparece una previsualización de una aplicación que utilizara estos colores. Aunque esta aplicación solo nos permite seleccionar el color primario (primary color) y el de resalte (accent color), realmente puedes configurar hasta 8 colores en la paleta de tu aplicación. Dark primary color y light primary color son colores derivados del primario. El resto se utilizan para textos e iconos y no es recomendable modificar.

3.Puedes realizar varias pruebas hasta obtener unos colores de tu gusto.

4.Cuando los tengas, puedes pulsar en DOWNLOAD y selecciona XML, para descargarte un fichero de recursos Android donde se definen estos colores.

5.Como solo vamos a cambiar tres valores, va a ser más sencillo que abras el fichero res/values/colors.xml de la aplicación y reemplaces lo tres valores definidos, con los valores en hexadecimal que has seleccionado. Estos valores pueden ser diferentes a los que se muestran a continuación:

<resources>

<color name="colorPrimary">#3F51B5</color>

<color name="colorPrimaryDark">#303F9F</color>

<color name="colorAccent">#8BC34A</color>

</resources>

6.Si abres el fichero res/values/styles.xml podrás observar como estos tres colores son utilizados para configurar los colores del tema aplicado por defecto a tu aplicación:

<resources>

<!-- Base application theme. -->

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<!-- Customize your theme here. -->

<item name="colorPrimary">@color/colorPrimary</item>

<item name="colorPrimaryDark">@color/colorPrimaryDark</item>

<item name="colorAccent">@color/colorAccent</item>

</style>

El resto de colores de la paleta no son definidos dado que se utilizarán los colores por defecto.

7.Si abres el fichero AndroidManifest.xml podrás observar como este tema es asignado a la aplicación.

<manifest …>

<application

android:theme="@style/AppTheme">

8.Todavía no podremos ejecutar la aplicación. Has de esperar a terminar el próximo apartado.

1.3. RecyclerView

Durante muchos años, una de las vistas más utilizadas en Android ha sido ListView, que muestra una lista de elementos deslizable verticalmente. Puedes encontrar información sobre el uso de ListView en androidcurso.com2. Otra vista muy similar a ListView es GridView, que representa una cuadrícula de elementos.

Con el lanzamiento de la versión 5.0 de Android, se añade la vista RecyclerView en una librería de compatibilidad v7. Esta clase ofrece la misma funcionalidad que ListView o GridView pero de forma más eficiente y flexible. Por lo tanto, te recomendamos que a partir de ahora utilices RecyclerView en lugar de ListView o GridView. Resulta algo más compleja de manejar, pero tiene las siguientes ventajas:

Reciclado de vistas (RecyclerView.ViewHolder)

Distribución de vistas configurable (LayoutManager)

Animaciones automáticas (ItemAnimator)

Separadores de elementos (ItemDecoration)

Trabaja conjuntamente con otros witgets introducidos en Material Design (CoordinationLayout)

image Vídeo[Tutorial]: Creación de listas con RecyclerView.

image Vídeo[Tutorial]: El patrón ViewHolder y su uso en RecyclerView.

image

Crear una lista (o cuadrícula) de elementos con un RecyclerView es muy parecido a como se crea un ListView. Necesitamos cinco elementos:

1.Un layout que contiene el RecyclerView con la lista de elementos.

2.La actividad que muestra la lista.

3.El adaptador que le indica que elemento tiene que mostrar en cada posición y rellena la información necesaria de este.

4.La vista que define un elemento genérico.

5.Un manejador de layouts (LayoutManager) que posiciona cada una de las vistas en diferentes configuraciones. Por ejemplo en forma de lista o cuadrícula.

A diferencia ListView o GridView, que muestran los elementos usando una determinada configuración, RecyclerView puede configurar esta configuración por medio de un LayoutManager. El sistema nos proporciona tres descendientes de LayoutManager, que son mostrados en la siguiente figura. También podemos crear nuestro descendiente de LayoutManager.

image

LinearLayoutManager

image

GridLayoutManager

image

StaggeredGridLayoutManager

Aprovecharemos el siguiente ejercicio para desarrollar estos cinco pasos.

image Ejercicio: Primera versión de Audiolibros con un RecyclerView.

En este ejercicio completaremos la aplicación Audiolibros con un RecyclerView que nos mostrará la lista de libros, permitiendo al usuario seleccionar uno de ellos.

1.Añade al fichero Gradle Scripts/Bulid.gradle (Module:app) la siguiente dependencia:

dependencies {

implementation 'com.android.support:recyclerview-v7:26.1.0'

}

NOTA: Es interesante que reemplaces :26.1.0 por la última versión disponible. Otra posibilidad para añadir esta dependencia consiste en seleccionar File / Project Structure… / Modules: app / Dependencies / + / Libray dependency / recyclerview-v7. La ventaja de esta opción es que indicaremos la última versión disponible.

2.Para empezar, necesitaremos definir un RecyclerView en el layout a mostrar. En res/layout/content_main.xml encontrarás en la raíz un elemento <ConstraintLayout>; Reemplaza el elemento <TexView> por el siguiente.

<android.support.v7.widget.RecyclerView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:id="@+id/recycler_view"

android:scrollbars="vertical"

android:background="#ffffff" />

Como puedes ver, el layout contiene solo un elemento: el RecyclerView. Además de los atributos habituales, se ha añadido scrollbars, para que visualice una barra de scroll que muestra la posición actual de los elementos en pantalla y el atributo background, para que el fondo sea de color blanco.

3.Ahora modifica la actividad, MainActivity, añadiendo el código subrayado:

public class MainActivity extends AppCompatActivity {

private RecyclerView recyclerView;

private RecyclerView.LayoutManager layoutManager;

@Override protected void onCreate(Bundle savedInstanceState) {

Aplicacion app = (Aplicacion) getApplication();

recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

recyclerView.setAdapter(app.getAdaptador());

layoutManager = new LinearLayoutManager(this);

recyclerView.setLayoutManager(layoutManager);

}

}

Lo que hacemos es buscar en él el RecyclerView y asignarle el adaptador que se definió en la clase Aplicacion. Además, creamos un nuevo LayoutManager de tipo LinearLayoutManager y lo asignamos al RecyclerView.

4.A continuación, tenemos que crear el layout que se utiliza como base para crear las diferentes vistas del RecyclerView. Pulsa con el botón derecho en el explorador del proyecto y selecciona New/Android resource file. El nombre del fichero ha de ser elemento_selector.xml y el tipo Layout. Reemplaza su código por el siguiente:

image

5.El siguiente paso será crear la clase AdaptadorLibros, que se encargará de rellenar el RecyclerView. Crea esta clase con el siguiente código:

public class AdaptadorLibros extends

RecyclerView.Adapter<AdaptadorLibros.ViewHolder> {

private LayoutInflater inflador; //Crea Layouts a partir del XML

protected List<Libro> listaLibros; //Lista de libros a visualizar

private Context contexto;

public AdaptadorLibros(Context contexto, List<Libro> listaLibros) {

inflador = (LayoutInflater) contexto

.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

this.listaLibros = listaLibros;

this.contexto = contexto;

}

//Creamos nuestro ViewHolder, con los tipos de elementos a modificar

public static class ViewHolder extends RecyclerView.ViewHolder {

public ImageView portada;

public TextView titulo;

public ViewHolder(View itemView) {

super(itemView);

portada = (ImageView) itemView.findViewById(R.id.portada);

titulo = (TextView) itemView.findViewById(R.id.titulo);

}

}

// Creamos el ViewHolder con las vista de un elemento sin personalizar

@Override

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

// Inflamos la vista desde el xml

View v = inflador.inflate(R.layout.elemento_selector, null);

return new ViewHolder(v);

}

// Usando como base el ViewHolder y lo personalizamos

@Override

public void onBindViewHolder(ViewHolder holder, int posicion) {

Libro libro = listaLibros.get(posicion);

holder.portada.setImageResource(libro.recursoImagen);

holder.titulo.setText(libro.titulo);

}

// Indicamos el número de elementos de la lista

@Override public int getItemCount() {

return listaLibros.size();

}

}

Un Adapter es un mecanismo estándar en Android que nos permite crear una serie de vistas que han de ser mostradas dentro de un contenedor.

En el constructor se inicializa el conjunto de datos a mostrar (en el ejemplo listaLibros) y otras variables globales a la clase. Luego se crea la clase ViewHolder, que contendrá las vistas que queremos modificar de un elemento (en el ejemplo portada y titulo). Esta clase es utilizada para evitar tener que crear las vistas de cada elemento (portada y titulo) desde cero. Lo va a hacer es utilizar un ViewHolder que contendrá las dos vistas ya creadas, pero sin personalizar. De forma que, gastará el mismo ViewHolder para todos los elementos y simplemente lo personalizaremos según la posición. Es decir, reciclamos el ViewHolder. Esta forma de proceder mejora el rendimiento del RecyclerView, haciendo que funcione más rápido.

El método onCreateViewHolder() devuelve una vista de un elemento sin personalizar. Podríamos definir diferentes vistas para diferentes tipos de elementos utilizando el parámetro viewType. El método onBindViewHolder() personaliza un elemento de tipo ViewHolder según su posicion. A partir del ViewHolder que personalizamos ya es el sistema quien se encarga de crear la vista definitiva que será insertada en el RecyclerView. Finalmente, el método getItemCount() se utiliza para indicar el número de elementos a visualizar.

6.Ejecuta la aplicación para ver el resultado:

image

image Ejercicio: Mostrar una cuadrícula en un RecyclerView.

En el ejercicio anterior hemos mostrado los elementos uno debajo de otro en forma de lista. Para conseguirlo hemos utilizado como manejador de Layouts la clase LinearLayoutManager. Para cambiar la distribución de los elementos no tenemos más que cambiar el manejador de Layouts. Si queremos una cuadrícula usaremos un GridLayoutManager.

1.En la clase MainActivity dentro del método onCreate(): busca la siguiente línea y elimina el texto tachado y añade el subrayado:

layoutManager = new LinearGridLayoutManager(this, 2);