Android Jetpack Compose Primera impresión

Después de ver una charla sobre Android Jetpack Compose en Google IO 2019, quise probarlo de inmediato. Además, el enfoque implementado en él recordaba mucho a Flutter, que me interesaba anteriormente .



La biblioteca Compose está en la etapa pre alfa, por lo que no hay mucha documentación y artículos disponibles. A continuación, confiaré en varios recursos que logré encontrar, además de la biblioteca de código abierto .


Estos recursos son:



¿Qué es Android Jetpack Compose?


Anteriormente, toda la interfaz de usuario de Android se basaba en la clase View. Este ha sido el caso desde los primeros días de Android. Y a este respecto, se han acumulado muchos defectos heredados y arquitectónicos, que podrían mejorarse. Pero hacer esto es bastante difícil sin romper todo el código escrito sobre su base.


En los últimos años, han aparecido muchos conceptos nuevos en el mundo de las aplicaciones de los clientes (incluidas las tendencias de Frontend), por lo que el equipo de Google ha tomado un camino radical y reescribió todo el nivel de IU en Android desde cero. Entonces apareció la biblioteca Android Jetpack Compose, que incluye trucos conceptuales de React, Litho, Vue, Flutter y muchos otros.


Veamos algunas de las características de la interfaz de usuario existente y compárela con Compose.


1. Independencia de las versiones de Android


La interfaz de usuario existente está estrechamente relacionada con la plataforma. Cuando aparecieron los primeros componentes de Material Design, solo funcionaban con Android 5 (API21) y versiones posteriores. Para trabajar en versiones anteriores del sistema, debe usar la Biblioteca de soporte.


Compose es parte de Jetpack, lo que lo hace independiente de las versiones del sistema y es posible usarlo incluso en versiones anteriores de Android (al menos con API21).


2. Toda la API de Kotlin


Anteriormente, tenía que lidiar con diferentes archivos para crear una interfaz de usuario. Describimos el marcado en xml, y luego usamos el código Java / Kotlin para que funcione. Luego volvimos nuevamente a otros archivos xml para configurar temas, animación, navegación, ... E incluso intentamos escribir código en xml (Enlace de datos).


El uso de Kotlin le permite escribir UI de estilo declarativo directamente en código en lugar de xml.


3. Composable = Composite: usando composición en lugar de herencia


Crear elementos de IU personalizados puede ser bastante engorroso. Necesitamos heredar de View o su descendiente y cuidar muchas propiedades importantes antes de que comience correctamente. Por ejemplo, la clase TextView contiene aproximadamente 30 mil líneas de código Java. Esto se debe al hecho de que contiene mucha lógica innecesaria dentro de sí misma que es heredada por los elementos descendientes.


Compose surgió por otro lado, reemplazando la herencia con la composición.


Padding es el más adecuado para ilustrar de qué se trata:


En la interfaz de usuario existente, para renderizar TextView sangría a 30dp :


imagen

necesitamos escribir el siguiente código:


 <TextView android:id="@+id/simpleTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/cyan" android:padding="30dp" <------------------------ NOTE THIS android:text="Drag or tap on the seek bar" /> 

Esto significa que en algún lugar dentro de TextView.java o sus superclases hay una lógica que sabe contar y dibujar sangrías.


Veamos cómo puedes hacer lo mismo en Compose:


 // note: the cyan background color is omitted for now to keep it simple Padding(30.dp) { Text("Drag or tap on the seek bar") } 

Cambios
TextView convertido solo en Text() . La propiedad android:padding ha convertido en un Padding que envuelve el Text .


Los beneficios
Por lo tanto, Text solo es responsable de representar el texto en sí. No sabe contar sangrías. Padding , por otro lado, solo es responsable del relleno y nada más. Se puede usar alrededor de cualquier otro elemento.


4. Flujo de datos unidireccional


El flujo de datos unidireccional es un concepto importante si hablamos, por ejemplo, de controlar el estado de un CheckBox en un sistema de IU existente. Cuando el usuario toca el CheckBox , su estado se checked = true : la clase actualiza el estado Ver y llama una devolución de llamada desde el código que supervisa el cambio de estado.


Luego, en el propio código, por ejemplo, en ViewModel , debe actualizar la variable de state correspondiente. Ahora tiene dos copias del estado presionado, lo que puede crear problemas. Por ejemplo, cambiar el valor de la variable de state dentro del ViewModel hará que CheckBox actualice, lo que puede terminar en un bucle sin fin. Para evitar esto, tendremos que idear algún tipo de muleta.


Usar Compose ayudará a resolver estos problemas, ya que se basa en el principio de un punto. El cambio de estado se procesará dentro del marco: simplemente damos el modelo de datos hacia adentro. Además, el componente en Compose ahora no cambia su estado por sí solo. En cambio, solo llama a la devolución de llamada, y ahora es tarea de la aplicación cambiar la interfaz de usuario.


5. Mejora de la depuración


Como toda la interfaz de usuario ahora está escrita en Kotlin, ahora puede depurar la interfaz de usuario. No lo intenté yo mismo, pero en el podcast dijeron que el depurador y los puntos de interrupción funcionan en Compose.


Palabras suficientes, muestra el código


Lo sé, quiero ver rápidamente cómo se ve la interfaz de usuario en el código (spoiler: muy similar a Flutter si intentas escribir en él).


Comenzaremos creando algunas View simples, luego compararemos cómo se ven en la IU existente y en Redactar.


1. FrameLayout vs Wrap + Padding + Background


Reutilizamos nuestro ejemplo anterior e intentamos que este TextView a 30dp con un fondo turquesa:


`TextView` sangrado en` 30dp` y fondo turquesa

IU existente:


 <TextView android:id="@+id/simpleTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/cyan" <-------------- NOTE THIS android:padding="30dp" <------------------------ AND THIS android:text="Drag or tap on the seek bar" /> 

Ahora mira el código que hace lo mismo en Compose:


 @Composable fun MyText() { Wrap { Padding(30.dp) { DrawRectangle(color = Color.Cyan) Text("Drag or tap on the seek bar") } } } 

Aquí hay algunas cosas nuevas. Como Text solo sabe sobre renderizar texto, no le importan el relleno ni el fondo. Por lo tanto, para agregarlos, necesitamos usar tres funciones separadas:


  • DrawRectangle fondo
  • Padding sangrantes
  • Wrap es una función que superpone parámetros como FrameLayout .

Fácil Pero es ligeramente diferente del sistema de IU existente al que todos estamos acostumbrados.


2. LinearLayout vertical vs Column


Ahora intentemos hacer algo equivalente a nuestro viejo LinearLayout .
Para colocar dos elementos uno debajo del otro, como en la imagen a continuación, podemos usar Column :


Dos elementos uno debajo del otro

El código se verá así:


 @Composable fun FormDemo() { Column(crossAxisAlignment = CrossAxisAlignment.Start) { Text("Click the button below: ") Button(text = "Next") } } 

Anidado en el elemento Column se ubicará verticalmente uno debajo del otro.


2a. Sangría


Probablemente haya notado que el texto y el botón están demasiado cerca del borde. Por lo tanto, agregue Padding .


 @Composable fun FormDemo() { Padding(10.dp) { //   Column(crossAxisAlignment = CrossAxisAlignment.Start) { Text("Click the button below: ") Button(text = "Next") } } } 

Se ve mejor:


Dos elementos sangrados uno debajo del otro

2b. Intervalos


También podemos agregar alguna sangría entre Text y Button :


 @Composable fun FormDemo() { Padding(10.dp) { Column(crossAxisAlignment = CrossAxisAlignment.Start) { Text("Click the button below: ") HeightSpacer(10.dp) //    Button(text = "Next") } } } 

Cómo se ve nuestra pantalla ahora:


Dos elementos, uno debajo del otro, sangrados y espaciados

2c. Horizontal LinearLayout vs Row


Coloque el segundo botón al lado del primero:


Se agregó un segundo botón

Código para esto:


 @Composable fun FormDemo() { Padding(10.dp) { Column(crossAxisAlignment = CrossAxisAlignment.Start) { Text("Click the button below: ") HeightSpacer(10.dp) Row { //   Button(text = "Back") //   WidthSpacer(10.dp) //    Button(text = "Next") } } } } 

Dentro de la Row dos botones serán horizontales. WidthSpacer agrega distancia entre ellos.


2d. Gravity vs Alignment


Alinee nuestros elementos en el centro, como lo hace la gravity en la interfaz de usuario actual. Para mostrar diff, comentaré las líneas antiguas y las reemplazaré por otras nuevas:


 @Composable fun FormDemo() { Padding(10.dp) { // Column(crossAxisAlignment = CrossAxisAlignment.Start) { Column(crossAxisAlignment = CrossAxisAlignment.Center) { //  Text("Click the button below: ") HeightSpacer(10.dp) // Row { Row(mainAxisSize = FlexSize.Min) { //    Button(text = "Back") WidthSpacer(10.dp) Button(text = "Next") } } } } 

Tendremos éxito:


Alineación central

Con crossAxisAlignment = CrossAxisAlignment.Center elementos anidados se centrarán horizontalmente. También deberíamos establecer el parámetro Row en mainAxisSize = FlexSize.Min , similar en comportamiento a layout_width = wrap_content , para que no se extienda por la pantalla debido al mainAxisSize = FlexSize.Max predeterminado, que se comporta como layout_width = match_parent .


2d. Observación


De lo que vimos en los ejemplos anteriores, puede ver que todos los elementos están compuestos de funciones separadas: el padding es una función separada, el spacer es una función separada, en lugar de ser propiedades dentro de Text , Button o Column .


Se están desarrollando elementos más complejos como RecyclerView o ConstraintLayout : por lo tanto, no pude encontrar un ejemplo con ellos en las fuentes de demostración.


3. Estilos y temas


Probablemente haya notado que los botones de arriba son morados por defecto. Esto se debe a que usan estilos predeterminados. Veamos cómo funcionan los estilos en Compose.


En los ejemplos anteriores, FormDemo etiquetado con la anotación @Composable . Ahora mostraré cómo se usa este elemento en la Activity :


 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { CraneWrapper{ MaterialTheme { FormDemo() } } } } 

En lugar de la función setContentView() , usamos setContent() , una función de extensión de la biblioteca Compose.kt .


CraneWrapper contiene el árbol Compose y proporciona acceso a Context , Density , FocusManager y TextInputService .


MaterialTheme permite personalizar el tema para los elementos.


Por ejemplo, puedo cambiar el color primario del tema a marrón de la siguiente manera:


 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { CraneWrapper{ // MaterialTheme { MaterialTheme(colors = MaterialColors(primary = Color.Maroon)) { FormDemo() } } } } 

Ahora nuestra pantalla se verá así:


Maroon como color primario

Otros colores y fuentes que se pueden cambiar: MaterialTheme.kt # 57


Rally Activity proporciona un buen ejemplo de cómo personalizar un tema: código fuente para RallyTheme.kt


Qué ver / leer


Si desea más, puede ensamblar el proyecto de muestra de acuerdo con las instrucciones aquí .


Mientras escriben los usuarios de Windows, ahora no hay una forma oficial de iniciar Compose, pero hay una guía no oficial de kotlinlang Slack .


Se pueden hacer preguntas sobre Compose a los desarrolladores en el canal #compose kotlinlang Slack.


Deje otros enlaces en los comentarios: los más útiles se agregarán aquí.


Conclusiones


El desarrollo de esta biblioteca está en pleno desarrollo, por lo que las interfaces que se muestran aquí están sujetas a cambios. Todavía hay muchas cosas sobre las que puede aprender en el código fuente, como @Model y flujo de datos unidireccional (flujo de datos unidireccional). Quizás este sea un tema para futuros artículos.

Source: https://habr.com/ru/post/461101/


All Articles