Diseño personalizado de Jetpack Compose con Kotlin DSL | de Evgeny Landarsky | diciembre 2022

Si bien Jetpack Compose ofrece una amplia gama de componentes para crear una hermosa interfaz de usuario, es posible que algún día descubras que tu siguiente diseño debe implementarse con un componente personalizado. Por ejemplo, Jetpack Compose no tiene un componente práctico listo para trabajar con una interfaz de usuario basada en cuadrículas. Qué entendemos por comodidad:

  • forma flexible de definir los tamaños de fila y columna
  • Colocación de contenido en una ubicación específica sin tener que colocar vecinos

Nos gustaría tener un componente que nos permita crear una interfaz de usuario como la siguiente imagen:En Jetpack Compose ya tenemos componentes similares: LazyVerticalGrid y LazyHorizontalGrid. Aquí hay un uso común de LazyHorizontalGrid: mira de cerca, pero no es lo mismo que lo que necesitamos. Las cuadrículas perezosas solo funcionan con el conteo de filas y columnas y colocan elementos secuencialmente. No solo queremos controlar el número, sino también los tamaños. Además, debemos tener la capacidad de colocar un elemento en cualquier posición. Basándonos en la API de Lazy Grids, definamos la API de nuestro componente y llamémosla GridPad: dado que queremos controlar la ubicación del contenido, nuestro componente creará artículos adicionales con Kotlin DSL a través de secciones de artículos. Echemos un vistazo más de cerca a GridPadCells. Esta clase contiene información sobre los tamaños de filas y columnas. Retrocedamos un poco y recordemos lo que necesitamos. Necesitamos poder especificar un tamaño para cada fila y columna en una cuadrícula.Por lo tanto, GridPadCells cubre todos nuestros requisitos. Puede notar que RowSizes y ColumnSizes se almacenan con ImmutableList. Gracias a esta implementación de una lista, el compilador de composición marca esta clase como estable y no necesitamos marcar GridPadCells con la anotación @Immutable o @Stable. ¿Por qué es importante? Esto es lo que dice la documentación oficial:

No todas las clases necesitan ser estables, pero una clase que es estable le da al compilador de composición mucha flexibilidad para optimizar cuando se usa un tipo estable en algunos lugares, razón por la cual este es un concepto tan importante en composición.

Aquí hay un excelente artículo que explica los conceptos básicos de Jetpack Compose con más detalle, como: estabilidad, saltabilidady capacidad de reinicio. Recomiendo leerlo para comprender mejor los conceptos básicos de Jetpack Compose y sus implicaciones. Hay otras dos clases que aún no hemos cubierto: GridPadCellSize y TotalSize. Tamaño de la celda del pad de cuadrícula es solo un contenedor que contiene información sobre el tamaño de una fila o columna específica: el tamaño total es una clase auxiliar que contiene información sobre la suma de todos los tipos de tamaños para una fila o columna y se usa en una fase de medición: Y la última clase aquí es una clase constructora. Esta clase no es obligatoria, pero ayuda a reducir el código repetitivo. Nuestro GridPad requiere listas de dos tamaños, que serán iguales en el caso de uso básico. Por esta razón, debemos crear una clase mutable con inicialización predeterminada y con la capacidad de anular los tamaños seleccionados: el segundo objetivo principal que queremos lograr es colocar contenido en cada celda de la cuadrícula con un alcance y una altura arbitrarios. Cubriremos algunos casos límite importantes:

  • una celda puede contener más de un elemento
  • el contenido puede superponerse
  • El contenido fuera de la cuadrícula sería ignorado

Aquí está la firma de GridPad: Echemos un vistazo más de cerca a GridPadScope. GridPadScope es el alcance de DSL que limita lo que los usuarios pueden poner en nuestros diseños componibles. Solo se acepta el contenido del artículo: se ve bastante básico en la parte superior, pero es un poco engañoso en los detalles. Aquí está la definición de GridPadScope:Aquí hay dos cosas que tienen sentido: @GridPadScopeMarker y GridPadItemScope. La marca @GridPadScope es un @DslMarker: The GridPadItemScope es un receptor de contexto: la combinación anterior ayuda a usar nuestra API de manera estricta: un desarrollador no puede colocar otros componibles sin envolverlos en un elemento. El desarrollador tampoco puede insertar un elemento en otro elemento. Si quieres aprender más sobre los receptores de contexto, te recomiendo leer este artículo. Como puede ver, todo lo que acabamos de explorar es una interfaz. Veamos las implementaciones en detalle. Antes de continuar, considera cómo es el ciclo de vida componible. Para un diseño personalizado, necesitamos implementar tres subfases de la fase de diseño:Por lo tanto, debemos medir a los niños, dimensionar el diseño y colocar el contenido. Aquí hay un nivel muy alto de implementación de GridPad: la definición de interfaz única no es suficiente para implementar la lógica para agregar contenido. Debido a esto, necesitamos una implementación, y esa implementación es GridPadScopeImpl. Aquí todas las llamadas desde el uso de GridPad se redirigen a la implementación de la interfaz: GridPadScopeImpl es como un contenedor que recopila todas las emisiones de DSL de nivel superior, crea una lista para mostrar y ofrece la posibilidad de agregar componibles: en el código anterior, es el más parte importante una conversión del elemento de llamada DSL {} dentro de GridPad a la metaclase GridPadContent, que almacena información para futuras mediciones y ubicación. Volvamos a GridPad y terminar la pieza. Después de recopilar toda la información sobre el contenido de ubicación, todo lo que tenemos que hacer es calcular los tamaños exactos para cada celda, medir componibles y colocarlos en la posición correcta. Aquí no profundizaremos en el cálculo de la lógica de ubicación, sino que nos centraremos en la API relacionada con Jetpack Compose. El primer paso es medir a los niños de acuerdo con su posición y tamaño de tramo: la mayor parte del código anterior calcula los límites de los componibles para llamar a la medida del elemento colocado. El último paso después de medir a todos los niños es definir el tamaño del componente y colocar los objetos: aquí se ha trabajado mucho. Los ejemplos anteriores pueden parecer un poco complicados, pero Jetpack Compose combinado con Kotlin DSL ofrece formas de implementar API elegantes para sus diseños personalizados. Si se enfrenta a un problema, puede consultar el código fuente de los componentes ya completados e inspirarse allí, es la mejor manera de aprender algo nuevo.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.