El gran libro de Android Avanzado - Jesus Tomás Gironés - E-Book

El gran libro de Android Avanzado E-Book

Jesús Tomás Gironés

0,0

Beschreibung

El libro que tiene en las manos es una guía para aquellos programadores de Android que, teniendo nociones básicas, quieran profundizar en algunos aspectos de especial interés. Kotlin: Programa de forma más rápida y concisa con el nuevo lenguaje de programación oficial para el desarrollo de aplicaciones Android. Es expresivo, seguro e interoperable con Java. Arquitecturas de software: Comprende los principios S.O.L.I.D. y S.T.U.P.I.D. Aplica los principales patrones de diseño. Utiliza patrones de arquitectura como MVC, MVP o CLEAN. Testing: ¿Puedes asegurar que tu código está libre de errores? Crea test unitarios con JUnit y Mockito. Testea tu IU con Espresso. Crea un test de forma automática con Firebase Test Lab. Hilos de ejecución: Comprende el papel de los hilos de ejecución en Android. Aprende a utilizar AsyncTask. Realiza tareas en segundo plano utilizando servicios. Conoce las restricciones introducidas con Android 8. Crea animaciones con SurfaceView. Diseño avanzado de la interfaz de usuario: Aprende las novedades incorporadas en las últimas versiones de Android, como Material Dessign, Fragments, ActionBar, Navigation Drawer, animación de propiedades… Personaliza tus propias vistas y crea Widgets de escritorio. El libro propone un aprendizaje activo, con actividades, muchas a través de Internet: Vídeo [Tutorial]: Videos elaborados por los autores. Ejercicio paso a paso: La mejor forma de aprender es practicando. Práctica: Para afianzar lo aprendido hay que practicar. Recursos adicionales: Localiza rápidamente la información clave. Preguntas de repaso y reflexión: ¿Lo has entendido todo correctamente? Trivial programación Android: Juega en red con varios oponentes.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern

Seitenzahl: 630

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



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

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:

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

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.

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.

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

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.

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

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.

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.

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:

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:

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:

o incluso directamente:

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

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:

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:

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, …).

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.

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.

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.

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.

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/

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>

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

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

<colorname="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. -->

<stylename="AppTheme"parent="Theme.AppCompat.Light.DarkActionBar">

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

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

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

<itemname="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)

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

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

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.

LinearLayoutManager

GridLayoutManager

StaggeredGridLayoutManager

Aprovecharemos el siguiente ejercicio para desarrollar estos cinco pasos.

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:

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:

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

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:

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:

El segundo parámetro indica que queremos trabajar con dos columnas.

2.Ejecuta la aplicación y verifica el resultado coincide con la captura mostrada al principio del ejercicio anterior. También puedes probar a añadir más columnas.

3.También puedes conseguir que la lista se muestre de izquierda a derecha en lugar de arriba abajo. Para ello utiliza el siguiente constructor:

Si quieres que la lista se muestre de derecha a izquierda, indica en el último parámetro true.

4.Tras verificar el resultado vuelve a dejar esta línea como se indica en el punto 1.

Ejercicio:Selección de un elemento en un RecyclerView.

En este ejercicio veremos cómo detectar que se ha pulsado sobre uno de los elementos del RecyclerView. En las vistas ListView y GridView podíamos realizar esta tarea usando el método setOnItemClickListener(). Sin embargo, en RecyclerView no se ha incluido este método. Google prefiere que asignemos un escuchador de forma independiente a cada una de las vistas que va a contener RecyclerView. Existen muchas alternativas para hacer este trabajo. A continuación, explicamos una de ellas.

1.Añade a la clase AdaptadorLibros la siguiente declaración:

private View.OnClickListener onClickListener;

En este campo guardaremos el escuchador de evento que queremos aplicar cuando se pulse sobre un elemento.

2.Para poder modificar el campo anterior añade el siguiente setter:

3.Solo nos queda aplicar este escuchador a cada una de las vistas creadas. Añade la línea subrayada en el método onCreateViewHolder():

4.Desde la clase MainActivity vamos a asignar un escuchador. Para ello añade el siguiente código al método onCreate():

app.getAdaptador().setOnItemClickListener(new View.OnClickListener(){

@Overridepublic void onClick(View v) {

Toast.makeText (MainActivity.this, "Seleccionado el elemento: "

+ recyclerView.getChildAdapterPosition(v),

Toast.LENGTH_SHORT).show();

}

});

El método getChildAdapterPosition(), nos indicarán la posición de una vista dentro del adaptador.

5.Ejecuta la aplicación y verifica el resultado.

Preguntas de repaso:RecyclerView.

1.4. Fragments

Vídeo[Tutorial]:Los Fragments en Android.

Con la versión 3.0 de Android, diseñada específicamente para tabletas, surgió un problema: las diferencias en los tamaños de pantalla entre los móviles y las tabletas complicaban los diseños de la interfaz de usuario. En una tableta, pueden caber muchos elementos de diseño al mismo tiempo, mientras que en un móvil estamos más limitados. Para resolver este problema, en la versión 3.0 de Android, se introdujeron los fragments. Los fragments son bloques de interfaz de usuario que pueden utilizarse en diferentes sitios, simplificando así la composición de una interfaz de usuario.

Los fragments nos permiten diseñar y crear cada uno de los elementos de nuestra aplicación por separado. Luego, dependiendo del tamaño de pantalla disponible, mostraremos uno solo, o más de uno a la vez. Por ejemplo, podríamos diseñar dos fragments, uno que nos permitiera elegir entre una lista de libros y otro que mostrara los detalles de uno de estos libros. En una tableta se podría mostrar ambos fragments a la vez mientras que en un móvil tendríamos que mostrar uno, y luego el otro.

Figura 1.Uso de fragments en tableta y móvil.

Es importante resaltar que no cambia el papel de las actividades. Sigue siendo el elemento básico que representa cada pantalla de una aplicación y nos permite navegar por ella. La novedad introducida es que cuando diseñemos una actividad, esta puede estar formada por uno o más fragments.

Cuando diseñemos un fragment, este ha de gestionarse a sí mismo, recibiendo eventos de entrada y modificando su vista sin necesidad de que la actividad que lo contiene intervenga. De esta forma, el fragment se podrá utilizar en diferentes actividades sin tener que modificar el código.

Los fragments son muy importantes dado que a partir de ahora Android los utiliza como elemento base en el diseño de la interfaz de usuario. Por ejemplo, la última versión de Google Maps o la visualización de preferencias de usuario se basan en fragments. El problema es que esta característica aparece en una versión que todavía no está disponible en muchos dispositivos. Para resolver este problema se ha creado una librería de compatibilidad para poder utilizar fragments en versiones anteriores a la 3.0. Esta librería se incluye de manera automática a un proyecto, siempre que el requerimiento mínimo de SDK sea inferior al nivel 11 (3.0); pero lo desarrollemos con una versión superior a la 3.0 (Target SDK).

Cada fragment ha de implementarse en una clase diferente. Esta clase tiene una estructura similar a la de una actividad, pero con algunas diferencias. La primera es que tiene que extender Fragment. El ciclo de vida es muy parecido al de una actividad; sin embargo, dispone de unos cuantos eventos más, que le indican cambios en su estado con respecto a la actividad que lo contiene. El ciclo de vida de un fragment va asociado al de la actividad que lo contiene (por ejemplo, si la actividad es destruida, todos los fragments que contiene son destruidos); pero también es posible destruir un fragment sin modificar el estado de la actividad. La mayor diferencia que encontramos en el ciclo de vida de un fragment es la introducción del método onCreateView. Es llamado para que devolvamos la vista asociada al fragment. De esta forma, lo que en una Actividad solíamos hacer en onCreate, ahora lo hacemos en onAttach (inicialización de la clase) y onCreateView (creación de la vista). El siguiente esquema ilustra la función de estos métodos. Para más detalles sobre el ciclo de vida de un fragment consultar https://github.com/xxv/androidlifecycle.

Es recomendable definir esta vista del fragment en un fichero XML. Por lo tanto, para crear un fragment usaremos una clase Java para definir su comportamiento y un fichero XML para definir su apariencia.

Los fragments se pueden introducir en una actividad de dos formas diferentes: por código o desde XML. Ambas formas tienen sus ventajas y sus inconvenientes. Introducir un fragment desde XML es más sencillo. Además, el diseño queda diferenciado del código, simplificando el trabajo del diseñador. Sin embargo, trabajar de esta forma tiene un inconveniente: una vez introducido ya no podremos reemplazar el fragment por otro. Por lo tanto, un fragment añadido desde XML será siempre estático. Si lo añadimos desde código, ganamos la posibilidad de intercambiar el fragment por otro. En los siguientes ejercicios veremos cómo añadir fragments de las dos formas.

1.4.1. Insertar fragments desde XML

Ejercicio:Un primer ejemplo con fragments.

En este ejercicio modificaremos el ejercicio anterior que visualizaba una lista de libros, pero ahora trabajando con fragments. Su funcionalidad será idéntica. El objetivo es ver cómo se define un fragment sencillo y cómo se utiliza una vez creado.

1.Haz una copia del proyecto Audiolibros_v1 y renómbralo como Audiolibros_v2. Para ello, haz clic con el botón derecho sobre el explorador de proyecto y selecciona File Path o Directory Path (Ctrl+Alt+F12) y luego Audiolibros_v1. Se abrirá un explorador de ficheros. Haz una copia de la carpeta y renómbrala como Audiolibros_v2. Selecciona la opción File / Open y abre el nuevo proyecto.

2.El único fragment que vamos a mostrar contendrá lo que antes mostraba MainActivity. Crea un layout con nombre fragment_selector.xml e introduce el siguiente código:

<android.support.v7.widget.RecyclerView

xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/recycler_view"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="#ffffff"

android:scrollbars="vertical" />

3.Ahora nos falta definir la clase para el fragment. Para que el código quede organizado, colocaremos los fragments en otro paquete. Esto es totalmente opcional; pero en nuestro caso nos ayudará a mantener por separado los fragments de otras clases. Para crear un nuevo paquete usa la opción del menú File / New / Package e introduce como nombre fragments. El resultado de muestra a continuación:

4.Crea una nueva clase dentro de este paquete. Para ello pulsa con el botón derecho dentro de la carpeta fragments, selecciona New / Java Class e indica como nombre SelectorFragment. La clase ha de tener el siguiente código:

Nota:Tras incluir nuevas clases tendrás que indicar los imports adecuados usando Alt+Intro. La clase fragment que se encuentra en dos paquetes.

Utiliza el segundo que corresponde a la librería estándar. El primero es el de la librería de compativilidad y debes gastarlo solo cuando trabajes con una versión mínima inferior a la 3.0. Es muy importante que utilices el mismo paquete en todo el proyecto. Aunque posiblemente la definición de un fragment en los dos paquetes sea idéntica, para Java se trata de dos clases distintas.

Nota:El método onAttach(Activity) ha sido marcado como obsoleto (deprecated). Se recomienda utilizar en su lugar onAttach(Context). No obstante, con un valor de minSdk-Version < 23 el nuevo método puede dar problemas3. Por lo tanto, preferimos seguir utilizando la versión obsoleta.

El código de esta clase es similar al que teníamos antes en MainActivity, salvo que ahora extendemos a Fragment en vez de a Activity, y que los métodos del ciclo de vida son diferentes. Estudiaremos más adelante el ciclo de vida de un fragment. De momento comentar que el método onAttach() es llamado cuando el fragment se asocia a la actividad. En él se nos pasa una referencia a la actividad, que guardaremos para usarla más adelante. También aprovecharemos este método para obtener dos referencias a los dos objetos declarados en Aplicacion. De igual manera como en una actividad un fragment también tiene una vista asociada. En la actividad asociábamos la vista en el método onCreate(), llamando a setContentView(). En un fragment también disponemos del método onCreate(), pero no es aquí donde hay que asociar la vista. Se ha creado un nuevo método en el ciclo de vida, onCreateView(), con la finalidad de asociar su vista. En este método se nos pasan tres parámetros: un LayoutInflater que nos permite crear una vista a partir de un layout XML, el contenedor donde será insertado el fragment (en el punto siguiente veremos que se trata de un LinearLayout) y posibles valores guardados de una instancia anterior4. El método onCreateView() ha de devolver la vista ya creada. El hecho de disponer de estos dos métodos va a resultar muy interesante, dado que nos va a permitir cambiar la vista de un fragment sin tener que volverlo a crear.

5.Reemplaza el contenido de content_main.xml por el siguiente código:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="horizontal"

app:layout_behavior="@string/appbar_scrolling_view_behavior"

tools:context="com.example.audiolibros.MainActivity"

tools:showIn="@layout/activity_main">

<fragment

android:id="@+id/selector_fragment"

android:name="com.example.audiolibros.fragments.SelectorFragment"

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1" />

</LinearLayout>

Como puedes ver, introducir el fragment desde un XML es muy sencillo. Simplemente añadimos una etiqueta <fragment> y en el atributo name le indicamos el nombre de la clase del fragment.

6.Modifica la clase MainActivity eliminando las líneas que habíamos añadido en onCreate() para crear el RecyclerView y el adaptador.

7.Ejecuta el proyecto. Verifica que la aplicación tiene la misma funcionalidad que antes.

Ejercicio:Implementando un segundo fragment.

Recordemos que la aplicación que queremos hacer tiene que mostrar una serie de audiolibros para elegir, y que, al pulsar sobre uno de ellos, nos muestre la información detallada sobre él y nos permita escucharlo. Para esto, vamos a crear un segundo fragment para ver los detalles de un audiolibro.

1.La vista que mostraremos estará definida en fragment_detalle.xml. Crea este archivo dentro de res/layout y complétalo con el siguiente código:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:id="@+id/fragment_detalle"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="#ffffff"

tools:context=".MainActivity" >

<TextView

android:id="@+id/titulo"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentLeft="true"

android:layout_alignParentRight="true"

android:layout_alignParentTop="true"

android:gravity="center"

android:text="Título"

android:textAlignment="center"

android:textColor="@android:color/black"

android:textAppearance="?android:attr/textAppearanceLarge"/>

<TextView

android:id="@+id/autor"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentLeft="true"

android:layout_alignParentRight="true"

android:layout_below="@+id/titulo"

android:gravity="center"

android:text="Autor"

android:textAlignment="center"

android:textColor="@android:color/darker_gray"

android:textAppearance="?android:attr/textAppearanceMedium"/>

<ImageView

android:id="@+id/portada"

android:layout_width="220dp"

android:layout_height="220dp"

android:layout_below="@id/autor"

android:layout_centerHorizontal="true"

android:scaleType="fitCenter"/>

</RelativeLayout>

2.Modifica content_main.xml para que muestre ambos fragments. Para ello, añade el siguiente elemento al final del linear layout:

<fragment

android:id"@+id/detalle_fragment"

android:name"com.example.audiolibros.fragments.DetalleFragment"

android:layout_width"0dp"

android:layout_height"match_parent"

android:layout_weight"2"/>

3.Crea una nueva clase llamada DetalleFragment dentro del paquete creado para los fragments. Esta clase deberá contener el siguiente código:

La finalidad de este fragment es visualizar los detalles de un audiolibro y permitir su reproducción. Para la reproducción declaramos el objeto mediaPlayer y para que el usuario pueda poner pausa, pararlo o mover el punto de reproducción declaramos el objeto mediaControler.

En el método onCreateView() vamos a generar la vista a representar. Comenzamos inflando el XML definido en el primer punto de este ejercicio. Luego, miramos si se han pasado argumentos al fragment mediante el método getArguments(). Si nos han pasado el argumento “id_libro”, visualizaremos el audiolibro con este id, si no, visualizaremos el que tiene id 0.

El método ponInfoLibro() es el que se encarga de introducir la información del libro en la vista. Primero obtenemos una referencia del libro a representar, haciendo uso de la clase Aplicacion definida al principio del capítulo. El siguiente paso consiste en actualizar la información del título, autor y portada dentro de la vista. Luego, activamos un escuchador de evento onTouch, para cuando se pulse sobre esta vista. Finalmente, son configurados los objetos mediaPlayer y mediaControler. El método ponInfoLibro() dispone de una sobrecarga donde solo se indica el id del libro a visualizar, de forma que se modifique la vista actual del fragment.

Dado que el audio es reproducido desde Internet, hay que prepararlo de forma asíncrona (prepareAsync()). Si utilizáramos el método síncrono (prepare()), podríamos dejar la aplicación bloqueada demasiado tiempo5. Actuando de esta manera, continuará la ejecución del programa y cuando se tenga preparado el audio para reproducir se ejecutará el método onPrepared(). Desde este método ponemos en reproducción el mediaPlayer y configuramos el mediaControler asociado a este. Aunque activamos la visualización del mediaController, este desaparecerá a los tres segundos.

El método onTouch() es ejecutado cuando se pulse sobre la vista. Queremos que se muestre el mediaControler. Permanecerá visible tres segundos y luego volverá a ocultarse. El resto de métodos corresponden a la interfaz MediaController.MediaPlayerControl donde se realizan las diferentes acciones de este control para asociarlo con el objeto mediaPlayer.

4.Dado que los audios se reproducen directamente desde Internet, es imprescindible solicitar el permiso correspondiente. Añade la siguiente línea a AndroidManifest.xml:

<uses-permissionandroid:name"android.permission.INTERNET"/>

5.Ejecuta la aplicación. Podrás ver como se muestran los dos fragments uno al lado del otro. Pasados unos segundos se ha de poner en reproducción el primer audiolibro.

1.4.2. Insertar fragments desde código

Si ejecutas el proyecto del ejercicio anterior en diferentes dispositivos, comprobarás que tanto en una tableta como en un móvil la apariencia es similar.

Sobre una tableta mostrar los fragmments simultáneamente parece adecuado. Sin embargo, en un móvil, puede ocasionar que su tamaño sea excesivamente pequeño. En este caso, parece más adecuado mostrar primero el fragment de selección y una vez que el usuario ha seleccionado un libro mostrar su detalle.

Para conseguir este comportamiento, cuando estemos en una pantalla grande incluiremos dos fragments desde XML; pero si estamos en una pequeña, los intercambiaremos en tiempo de ejecución. Para diferenciar entre pantallas grandes y pequeñas contamos con los recursos alternativos (más información en el siguiente vídeo). En los siguientes dos ejercicios veremos cómo conseguirlo.

Ejercicio:Insertar un fragment por código.

1.Primero, crearemos un recurso alternativo a content_main.xml para pantallas grande. Para esto, pulsa con el botón derecho sobre el explorador de proyecto y selecciona New / Android resource file. Indica los datos que se muestran a continuación:

Nota:Cuando se asocia el sufijo sw600dp a un recurso, estamos indicando que solo ha de ser cargado en tabletas de 7’ o más. Trabaja de la siguiente forma: Se obtiene el menor entre anchura y altura de pantalla disponible y se compara el valor indicado (600). Solo se cargará el recurso si el valor obtenido es mayor que 600. Más información en El Gran Libro de Android, anexo E.

2.El layout content_main.xml que tenemos actualmente por defecto es el que queremos para pantallas grandes. Copia su código dentro de content_main.xml (w600dp).

3.Introduce en res/layout/content_main.xml, por defecto, el siguiente código:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:id="@+id/contenedor_pequeno"

android:orientation="horizontal"

app:layout_behavior="@string/appbar_scrolling_view_behavior"

tools:context="com.example.audiolibros.MainActivity"

tools:showIn="@layout/activity_main">

</LinearLayout>

Este layout simplemente tiene un solo LinearLayout. En él se insertará un DetalleFragment o un SelectorFragment según nos interese. Para insertar un único elemento también podríamos usar otro tipo de layout, como FrameLayout o RelativeLayout.

4.Finalmente, vamos a añadir el fragment dentro de este layout desde código. Para ello, copia el siguiente código dentro de onCreate() de MainActivity, después de la llamada a setContentView():

Cuando el programa se ejecuta en un dispositivo grande, se cargará el layout que contiene los dos fragments directamente en XML. Además, el idcontenedor_pequeno no será creado, por lo que no se cumplirá la condición. Si se ejecuta en un dispositivo pequeño, se cargará el layout añadido en el punto 3 y sí que existirá contenedor_pequeno. Además, se comprueba una segunda condición antes de crear el fragment, que consiste en verificar si el fragment ya ha sido creado. Observa cómo el fragment es identificado por medio del id de la vista que lo contiene.

Si se cumplen las dos condiciones, creamos una instancia del fragment que vamos a añadir. Luego comenzamos una transacción, término que se utiliza para referirse a una adición, intercambio o eliminación de fragments. En esta transacción añadimos el fragment creado, usando el método add(). Finalmente, llevamos a cabo la transacción usando el método commit().

5.Ejecuta el proyecto en una tableta; se visualizarán dos fragments. Y en un móvil se visualizará solo uno.

1.4.3. Comunicación e intercambio de fragments

En este apartado veremos cómo se pueden comunicar los fragments con la actividad que los contiene. También veremos qué hacer para poder intercambiar dos fragments entre sí desde la actividad.

En particular veremos cómo desde SelectorFragment podemos indicar a MainActivity que ha recibido una pulsación, cómo MainActivity puede intercambiar SelectorFragment por DetalleFragment y, finalmente, cómo MainActivity puede indicar a DetalleFragment el libro a mostrar.

Ejercicio:Comunicación e intercambio de fragments.

1.En el método onCreateView() de SelectorFragment sustituye el Toast por el código indicado:

adaptador.setOnItemClickListener(new View.OnItemClickListener(){

@Override public void onClick(View v) {

Toast.makeText(actividad, "Seleccionado el elemento: "+ recycl…

((MainActivity) actividad).mostrarDetalle(recyclerView.getChildAdapterPosition(v));

}

});

Cuando se pulse sobre un elemento, en lugar de mostrar un Toast, vamos a llamar a un método de nuestra actividad para que se nos muestre el detalle del libro seleccionado.

2.Añade la clase MainActivity el siguiente método:

Comenzamos averiguando si existe el fragmentdetalleFragment. Si existe significa que estaremos en una tableta y solo tendremos que actualizar la información del libro a mostrar. Usaremos para ello el método ponInfoLibro().

En caso de existir, significa que estamos en un móvil. En este caso, crearemos un nuevo DetalleFragment, le pasaremos como argumento el id del libro a mostrar y crearemos una nueva transacción, esta vez con replace(), para reemplazar el fragment actual.

Un detalle que hay que destacar es cómo, antes del método commit(), llamamos a addToBackStack(). Lo que hace este método es guardar esta transacción en la pila de navegación entre actividades; así, cuando el usuario pulse la tecla «atrás», la transacción se deshará, en vez de cerrar la actividad. Desde el punto de vista del usuario, esto será como volver un paso atrás.

3.Ejecuta la aplicación en un dispositivo pequeño. Podrás ver que solo se muestra el fragment que contiene el RecyclerView, y que, al seleccionar un elemento, se sustituye este fragment por el que muestra los detalles del libro seleccionado. Si pulsas la tecla «atrás» mientras ves los detalles, se vuelve al fragment del RecyclerView.

4.Comenta la línea transaction.addToBackStack(null) y ejecuta de nuevo la aplicación en un dispositivo pequeño. Cuando estés visualizando el detalle de un libro pulsa la tecla «atrás». Comprueba como da por finalizada la actividad y regresa a la aplicación anterior. Dado que no es el comportamiento esperado por el usuario, quita el comentario de la línea.

Preguntas de repaso:Fragments.

1.5. Otros elementos de la interfaz de usuario

1.5.1. Menús contextuales

Un menú contextual se utiliza cuando queremos seleccionar alguna acción de un elemento particular. Lo más frecuente es que aparezca cuando se realiza una pulsación larga sobre un elemento. Con este propósito usaremos la clase AlertDialog disponible desde el API 1.

Ejercicio:Añadir un menú contextual al RecyclerView.

En este ejercicio vamos a añadir un menú contextual que permita al usuario realizar varias acciones sobre la biblioteca de libros. En concreto permitiremos compartir, borrar o insertar libros. El menú se aparecerá al hacer una pulsación larga sobre un elemento.

1.Añade dentro de onCreateView() de SelectorFragment el siguiente código:

Aquí estamos definiendo un OnItemLongClickListener() que utilizaremos para detectar una pulsación larga. Cuando ocurra este evento, mostraremos un menú contextual con las opciones de compartir, borrar o insertar. A su vez definimos un OnClickListener en el menú, para detectar las diferentes opciones.

En la primera opción permitimos compartir la URL del libro seleccionado por medio de la intención ACTION_SEND. Se abrirá un menú para que el usuario seleccione alguno de los programas que tenga instalados que admitan esta intención; por ejemplo, Facebook, Twitter o WhatsApp. La segunda opción elimina el libro seleccionado y notifica al adaptador que ha habido cambios para que redibuje el RecyclerView. La última opción crea una copia del libro seleccionado al final de la lista.

Terminamos mostrando el menú y devolviendo true para indicar que hemos procesado la pulsación larga y no hay que seguir propagando el evento.

Nota:Observa como la variable id tienen el modificadorfinal. Si lo eliminas aparece el error: “Cannot refer to a non-final variable id inside an inner class defined in a different method”