Novedades en Modificadores — Notas de ADS’22 | de Akash Khunt | noviembre 2022

Foto de Darwin Vegher en UnsplashEste año Cumbre de desarrolladores de Android (ADS) comenzó el 24 de octubre con un discurso de apertura y una pista de desarrollo de Android moderno. Cubrieron muchos temas de Jetpack Compose, la arquitectura de la aplicación, el rendimiento de la aplicación, la compatibilidad con las herramientas de Android Studio (tanto para Jetpack Compose como para Crashlytics), Relay, una herramienta para el envío de diseños (recomiendo encarecidamente esta charla) y mucho más. Si aún no lo has visto, te recomiendo que los revises usando este enlace.

NOTA: ADS aún no ha terminado. Este evento tendrá más pistas como Factores de forma (9 de noviembre en Londres) & Plataforma (14 de noviembre) y posteriormente en diciembre también en Asia. Así que estad atentos. 🙂

Aunque todo lo que se cubrió fue muy importante y esclarecedor, el tema que me llamó la atención fue Compose Modifiers Deep Dive de Leland Richardson, en el que cubre la historia del origen de los modificadores (junto con algunas cosas a considerar al diseñar/construir un nuevo sistema de interfaz de usuario) , los problemas relacionados con su estado actual y cómo están evolucionando (afortunadamente sin cambios en la superficie de la API a menos que esté utilizando modificador.compuesto{ }). Como se trataba de un tema complejo, decidí tomar notas al respecto para que, si necesito hacer referencia a algo en el futuro, no tenga que buscar en el video de YouTube (aunque recomiendo encarecidamente a las personas que vean la charla al menos una vez para ver ). Entonces, la contribución restante consiste en mis notas y algunos comentarios de mi parte para comprender las cosas cubiertas en la conferencia.

Contenidos

Cómo surgieron los modificadores

  • Uno de los enfoques para proporcionar relleno (todas las direcciones), color de fondo, degradado, borde y otras propiedades para cada nodo de Compose UI se puede realizar a través de variables de clase, pero eso puede ser rápido debido a la gran cantidad de propiedades que tiene un componente de UI. fuera de control puede apoyar.

Implementación hipotética de Compose UI Node

  • Entonces, otro enfoque puede ser usar cada una de estas propiedades, p. padding, color de fondo, para que se pueda hacer clic como componible independiente, pero este enfoque requiere que cada uno de esos componibles tenga un parámetro lambda componible que pueda aceptar niños, lo que lleva a un problema que requiere que se pueda hacer clic/padding para hacer algo con una política de diseño de arte (que se presentará durante la diseño fase para colocar los niños componibles)

Implementación hipotética de un componible

  • Luego vino el modificadores que usamos hoy en día que se puede concatenar/conectar en cascada para proporcionar componibles con múltiples propiedades, p. B. relleno, color de fondo, borde, forma, oyente de clic o incluso gestos.

Uso de modificadores para proporcionar relleno, color de fondo y detector de clics para una fila componible

  • El uso de modificadores está bien, pero podría causar un problema si un modificador con un estado específico (p. ej., en el que se puede hacer clic y tiene una animación de efecto dominó) se almacena como una variable y luego se pasa a dos o más composables diferentes, ya que cada componible debe tener su propio estado de animación. Este problema se solucionó usando una API Modifier.composed { } & materialize() (que es una de las primeras cosas llamadas en Layout() componible) de modo que incluso si se pasa el mismo modificador a varios componibles, cada uno de ellos obtendrá su propio estado.

modificador.compuesto {} & materializar() uso de la API

  • La API Modifier.composed { } junto con materialize() resolvió el problema de tener estados separados incluso cuando se usaba el mismo modificador para múltiples componibles, pero esto causaba un problema de rendimiento por las siguientes razones.

– Porque Modifier.composed { } tiene un tipo de valor devuelto, es decir, Modifier, y dado que los componibles con tipo de valor devuelto no se pueden omitir durante la recomposición. – Dado que Modifier.composed () no es componible, ni siquiera podemos usar el todopoderoso compilador de composición para almacenar en caché el lambda usado anteriormente para crear un nuevo modificador, lo que hace que el modificador recién compuesto no sea igual al generado previamente, incluso si no se ha realizado el cambio. Esto también hace que muchos de los recordatorios internos de los modificadores compuestos se consideren innecesarios.

Aquí nosotros propina Omita la composición porque la función en la que se puede hacer clic tiene un tipo de retorno, es decir, modificadorya que compuesto es No una función componible, no podemos guardar la lambda usando la API RememberPara habilitar un estado único por elemento componible, el modificador lambda compuesto devuelto nunca es el mismoComo compuesto La API se usa para almacenar algún tipo de estado de modificadores. Recuerde que las llamadas son necesarias para almacenar en caché el estado

Aunque parecía un gran problema, la suposición era tan modificador.compuesto {} La API solo se necesita en muy pocos casos especiales, pero estaba mal. 😅

Modificadores en la práctica y los problemas asociados con ellos.

  • El texto que se puede componer con solo un Modifier.clickable{} parece muy simple, pero Modifier.clickable{} se conecta en cascada internamente a más modificadores como semántica(), onKeyEvent(),dication(), pointerInput() y más, además de Text Composable, que podría parecer muy inocente, usa TextController internamente, que también crea sus propios modificadores (ver TextController.modifiers para una mejor comprensión) que son responsables de representar el texto, la semántica y la selección (es decir, lo que facilita las funciones de cortar y copiar que obtenemos al seleccionar un texto) . Entonces, un texto simple componible con solo modificadores en los que se puede hacer clic da como resultado más de 20 modificadores en cascada 😲. Consulte las siguientes capturas de pantalla como referencia:

Columna con dos textos componiblesÁrbol de interfaz de usuario con un diseño que contiene dos textos componibles, uno de los cuales tiene un modificador en el que se puede hacer clicModelo mental de la cadena de modificadores generada para un texto componible modificador en el que se puede hacer clic

  • Si agregamos el contenido de Modifier.clickable{} podemos ver el resultado (al menos en Compose 1.3.0-beta01):- 13 Modificador Llamadas Compuestas – 34 Recordar Llamadas – 11 Efectos Secundarios – 16 Modificador Hoja Elementos

Esencialmente, lo que significa el punto anterior es que si aplicamos el modificador {} en el que se puede hacer clic a una composición Composable-On, terminaremos llamando a materialize() por cada 13 llamadas Modifier.composed, luego todo lo contenido dentro será Recordar llamadas también invocadas entonces asigna memoria para objetos de entidad (es decir, objetos que gestionan el comportamiento entre diferentes subsistemas y envían cosas a estos modificadores 😵‍💫😵‍💫…🤷) para cada modificador. Al recomponer, terminamos asignando nuevos modificadores, llamando a materialize() en todos los modificadores compuestos, luego descartando los objetos de entidad antiguos y asignando otros nuevos.

  • La conclusión clave aquí es que muchos de los modificadores deben tener su propio estado, y ese estado se aplica por nodo de diseño. Y esto solo fue posible a través de la API Modifier.composed{} antes del lanzamiento de Compose 1.3.0-beta01, que implementó soporte experimental para Modifier.Node

Desarrollo de modificadores desde Compose 1.3.0-beta01

  • En Compose 1.3.0-beta01 soporte experimental Modificador.Nodo se implementó como una alternativa a Modifier.composed{} que tiene más rendimiento (UNA NOTICIA: Esta migración se produce a lo largo de varias versiones y todavía es WIP)
  • Con la introducción de Modifier.Node, todos los modificadores, como el relleno, el fondo e incluso los que se pueden hacer clic, se tratan como un nuevo tipo de Modifier.Element ligero e inmutable que sabe cómo administrar una instancia de una clase Modifier.Node correspondiente.

Cuando se compone por primera vez, la cadena de modificadores crea su propia Modificador.Elemento ejemplo

  • Estas clases Modifier.Element también obtienen una función create(), que luego crea/asigna instancias de Modifier.Node. Estas instancias de nodo forman parte directamente de la jerarquía de la interfaz de usuario y comparten el mismo ciclo de vida que los diseños a los que se aplican, lo que los convierte en propietarios de estado ideales para los modificadores que lo necesitan, como en los que se puede hacer clic.

Modificador.Elemento vocación crear() instanciar Modificador.Nodo instancias

  • Ahora, hablando del escenario anterior, cuando se lleva a cabo la recomposición, no necesitamos crear nuevos modificadores para toda la cadena de modificadores, ya que estos Modifier.Element son inmutables y se pueden comparar fácilmente. Entonces, al final, creamos y aplicamos Modifier.Element & Modifier.Node solo a los modificadores modificados en toda la cadena. Entonces, en el caso actual, desde entonces solo el tapicería Valor del modificador cambiado por 10.dp -> 16.dpsolamente modificador de relleno.Elemento Llamadas Actualizar() para actualizar PaddingNode y el Modifier.Element restante, no realice cálculos innecesarios.

En la recomposición, la cadena de modificadores se vuelve a evaluarSobre la recomposición solo relleno Modifier.Element llama a update() para actualizar el PaddingNodeventajas del nuevo Modificador.Elemento & Modificador.NodoComida para llevar de lo nuevo Modificador.Elemento & Modificador.Nodo¡Lamentablemente hemos llegado al final! 🙌 En este punto, podrías estar pensando que esto parece un poco más complejo y que no es necesario saberlo tan bien ya que es una implementación interna (como se mencionó, a menos que estés usando modificador.compuesto {} API directamente solo se actualiza en Componer 1.3.0-beta01 versión debería ser suficiente para obtener los beneficios de rendimiento de los cambios internos), pero siempre es mejor tener una idea de cómo funcionan las cosas internamente y cuáles son los desafíos para desarrollarlas. 🙂

Deja una respuesta

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