Ingeniería en Sprout: creación de un selector de meses de Android
Publicado: 2020-06-26Nota: este artículo se basó en Material Components versión 1.2.0-beta01 a partir del 1 de junio de 2020 .
En mis tres años y medio trabajando en un pequeño equipo de Android en Sprout Social, una de las principales cosas que me motiva a venir a trabajar todos los días es la libertad y la confianza de nuestra empresa para abordar un problema de la manera que consideremos mejor.
La libertad de investigar y explorar muchas soluciones diferentes a un problema que consideramos necesario, teniendo en cuenta un plazo para entregar las actualizaciones del producto, nos permite encontrar la mejor solución tanto para nuestros clientes como para nuestro software.
Uno de esos desafíos involucró la creación de un componente de interfaz de usuario para nuestra nueva función Informes móviles. Este nuevo componente era un selector de meses, lo que permitía a nuestros usuarios seleccionar un intervalo de fechas para un informe de análisis.
El lugar de partida que elegimos fue la Biblioteca de componentes de materiales existente. En lugar de comenzar desde cero, esta biblioteca se mantiene activamente y se alinea con las especificaciones del material. Con esta biblioteca como base, probablemente podríamos reducir la cantidad de lógica que tendríamos que escribir nosotros mismos.
En este artículo, cubriré cómo abordamos este proceso, algunos factores únicos en la creación de la aplicación Sprout para Android, algunos "errores" que surgieron (y se solucionaron) en el camino y qué debe saber si está trabajando en un proyecto similar.
Introducción
El lanzamiento de Android Material Components 1.1.0 introdujo un nuevo componente de interfaz de usuario Selector de fecha. Una de las adiciones bienvenidas de este nuevo MaterialDatePicker
sobre AppCompat CalendarView
es la capacidad de seleccionar un rango de fechas utilizando una vista de calendario o un campo de entrada de texto.
El antiguo AppCompat CalendarView no era muy flexible. Era un buen componente para el caso de uso limitado que estaba destinado a resolver; es decir, seleccionar una sola fecha y fechas mínimas y máximas opcionales para especificar un límite de rango de fechas permitido.
El nuevo MaterialDatePicker se creó con más flexibilidad para permitir el uso de una funcionalidad ampliada de comportamiento. Funciona a través de una serie de interfaces que se pueden implementar para ajustar y modificar el comportamiento del selector.
Esta modificación de comportamiento se realiza en tiempo de ejecución a través de un conjunto de funciones de patrón de constructor en la clase MaterialDatePicker.Builder
.
Esto significa que podemos extender el comportamiento base de este MaterialDatePicker
a través de componentes de interfaz componibles.
Nota: Si bien hay una serie de componentes diferentes que utiliza MaterialDatePicker
, en este artículo solo cubriremos el componente de selección de fecha.
Selector de rango de fechas
El equipo de Android de Sprout Social estaba en el proceso de crear nuestra sección de informes de análisis.
Esta nueva sección permitiría a nuestros usuarios seleccionar un conjunto de filtros y un conjunto de rangos de fechas que cubriría el informe.
El MaterialDatePicker
vino con algunos componentes preconstruidos que pudimos aprovechar para lograr nuestro caso de uso.
Para nuestro caso más común, permitir que un usuario seleccione un rango de fechas, el MaterialDatePicker
preconstruido sería suficiente:
Con este bloque de código, obtenemos un Selector de fechas que permite a los usuarios seleccionar un rango de fechas.
Selector de fecha mensual
Uno de los informes de Sprout Social que tiene una selección de fechas más exclusiva es el Informe de tendencias de Twitter.
Este informe se diferencia de los demás en que, en lugar de permitir cualquier tipo de rango de fechas, impone una selección de un solo mes, lo que significa que un usuario solo puede seleccionar marzo de 2020 frente al 3 de marzo al 16 de marzo de 2020.
Nuestra aplicación web maneja esto usando un campo de formulario desplegable:
El MaterialDatePicker
no tiene una forma de hacer cumplir dicha restricción con el Selector de rango de fechas de material prediseñado que se analiza en la sección anterior. Afortunadamente, MaterialDatePicker se creó con partes componibles que nos permiten expandir el comportamiento predeterminado para nuestro caso de uso particular.
Comportamiento de selección de fecha
El MaterialDatePicker
aprovecha un DateSelector
como la interfaz utilizada para la lógica de selección del selector.
Del Javadoc:
"Interfaz para usuarios de {@link MaterialCalendar<S>}
para controlar cómo se muestra el calendario y devuelve las selecciones..."
Notará que MaterialDatePicker.Builder.dateRangePicker()
devuelve una instancia de generador de RangeDateSelector
, que usamos en el ejemplo anterior.
Esta clase es un selector prediseñado que implementa DateSelector
.
Lluvia de ideas sobre un comportamiento de selección de fecha mensual
Para nuestro caso de uso, queríamos una forma de que nuestros usuarios seleccionaran un mes completo como un rango de fechas seleccionado; por ejemplo, mayo de 2020, abril de 2020, etc.
Pensamos que el RangeDateSelector
al que se hace referencia anteriormente nos llevó la mayor parte del camino hasta allí. El componente permitía a un usuario seleccionar un rango de fechas y aplicar un límite [inferior, superior] .
Lo único que faltaba era una forma de hacer cumplir una selección para seleccionar automáticamente todo el mes. El comportamiento predeterminado de RangeDateSelector
hace que el usuario seleccione una fecha de inicio y una fecha de finalización.
Queríamos un comportamiento para que cuando un usuario seleccione un día en el mes, el selector seleccione automáticamente todo el mes como rango de fechas.
La solución que decidimos fue extender RangeDateSelector
y luego anular el comportamiento de selección de día para seleccionar automáticamente todo el mes.
Afortunadamente, hay una función que podemos anular desde la interfaz DateSelector
llamada: select(selection: Long)
.
Esta función se invocará cuando un usuario seleccione un día en el selector, con el día seleccionado pasado en milisegundos UTC desde la época.
Implementando un comportamiento de selección de fecha mensual
La implementación resultó ser la parte más simple, ya que tenemos una función clara que podemos anular para obtener el comportamiento que queremos.
La lógica básica será esta:
- El usuario selecciona un día.
- La función
select()
se invoca con el día seleccionado en un UTC largo de milisegundos a partir de la época. - Encuentre el primer y último día del mes a partir del día dado que nos pasó.
- Realice una llamada a
super.select(1st of month)
ysuper.select(last day of month)
- El comportamiento principal de
RangeDateSelector
debería funcionar como se esperaba y seleccionar el mes como rango de fechas.
Poniendolo todo junto
Ahora que tenemos nuestro MonthRangeDateSelector
personalizado, podemos configurar nuestro MaterialDatePicker
.
Para llevar el ejemplo más allá, podemos procesar el resultado de la selección así:
El resultado se verá así:
trampas
Solo hubo un problema importante que dificultó llegar a esta solución.
Los componentes principales utilizados para construir nuestro MonthRangeDateSelector
fueron la clase RangeDateSelector
y la interfaz DateSelector
. La versión de la biblioteca utilizada en este artículo (1.2.0-beta01) restringió la visibilidad de estos dos archivos para desalentar su extensión o implementación.
Como resultado, aunque pudimos compilar con éxito nuestro nuevo MonthRangeDateSelector
, el compilador mostró una advertencia muy aterradora para disuadirnos de hacerlo:
Una forma de ocultar esta advertencia del compilador es agregar un @Suppress("RestrictedApi")
así:
Esta experiencia ilustra cómo, a pesar de que la biblioteca de componentes de materiales ha proporcionado algunos componentes nuevos excelentes a la comunidad de desarrolladores de Android, todavía es un trabajo en progreso.
¡Una gran parte de esta biblioteca es la apertura a los comentarios de la comunidad de Android! Después de descubrir esta restricción de visibilidad de componentes, abrí un problema en el Proyecto Github e incluso abrí un PR para abordarlo de inmediato.
Este circuito abierto de comentarios entre el equipo de componentes de materiales y la comunidad de Android genera una excelente colaboración y resultados para todos.
Conclusión
El nuevo MaterialDatePicker
tiene una excelente funcionalidad lista para usar que probablemente cubrirá la mayoría de los casos de uso de selección de fecha.
Sin embargo, la mejor parte de algo como AppCompat CalendarView es que está construido de manera componible. Por lo tanto, se puede ampliar y modificar fácilmente para casos de uso específicos, mientras que sería mucho más difícil lograr tales cosas en CalendarView
.
Gracias especiales
Me gustaría destacar a algunas personas que ayudaron a revisar este artículo:
- Nick Rout (Github)
- Mike Wolfson (Github)
- Ryan Phillips (LinkedIn)
- Lucas Moellers (Github)
- Mit Patel (LinkedIn)