Android Jetpack Composer la première impression

Après avoir vu un exposé sur Android Jetpack Compose sur Google IO 2019, j'ai voulu l'essayer tout de suite. De plus, l'approche qui y était mise en œuvre rappelait beaucoup Flutter, ce qui m'intéressait plus tôt .



La bibliothèque Compose elle-même est au stade pré-alpha, donc peu de documentation et d'articles à ce sujet sont disponibles. Ensuite, je vais compter sur plusieurs ressources que j'ai réussi à trouver, plus la bibliothèque open source .


Ces ressources sont:



Qu'est-ce que Android Jetpack Compose?


Auparavant, l'intégralité de l'interface utilisateur Android était basée sur la classe View. C'est le cas depuis les débuts d'Android. Et à cet égard, de nombreux défauts hérités et architecturaux se sont accumulés, qui pourraient être améliorés. Mais faire cela est assez difficile sans casser tout le code écrit sur leur base.


Ces dernières années, de nombreux nouveaux concepts sont apparus dans le monde des applications clientes (y compris les tendances Frontend), de sorte que l'équipe de Google est allée radicalement et a réécrit le niveau d'interface utilisateur entier dans Android à partir de zéro. La bibliothèque Android Jetpack Compose est donc apparue, qui comprend des astuces conceptuelles de React, Litho, Vue, Flutter et bien d'autres.


Passons en revue certaines des fonctionnalités de l'interface utilisateur existante et comparons-la avec Compose.


1. Indépendance par rapport aux versions Android


L'interface utilisateur existante est étroitement liée à la plate-forme. Lorsque les premiers composants de Material Design sont apparus, ils ne fonctionnaient qu'avec Android 5 (API21) et versions supérieures. Pour travailler sur des versions plus anciennes du système, vous devez utiliser la bibliothèque de support.


Compose fait partie de Jetpack, ce qui le rend indépendant des versions du système et peut être utilisé même dans les anciennes versions d'Android (au moins avec API21).


2. L'ensemble de l'API Kotlin


Auparavant, vous deviez gérer différents fichiers pour créer une interface utilisateur. Nous avons décrit le balisage en xml, puis utilisé le code Java / Kotlin pour le faire fonctionner. Ensuite, nous sommes revenus sur d'autres fichiers xml afin de définir des thèmes, l'animation, la navigation, ... Et même essayé d'écrire du code en xml (Data Binding).


L'utilisation de Kotlin vous permet d'écrire des interfaces utilisateur de style déclaratif directement dans du code au lieu de xml.


3. Composable = Composite: utiliser la composition au lieu de l'héritage


La création d'éléments d'interface utilisateur personnalisés peut être assez lourde. Nous devons hériter de View ou de son descendant et prendre soin de nombreuses propriétés importantes avant de démarrer correctement. Par exemple, la classe TextView contient environ 30 000 lignes de code Java. Cela est dû au fait qu'il contient en lui-même beaucoup de logique inutile héritée par les éléments descendants.


Composer est venu d'un autre côté, remplaçant l'héritage par la composition.


Padding est le mieux adapté pour illustrer de quoi il s'agit:


Dans l'interface utilisateur existante, afin de rendre le TextView retrait à 30dp :


image

nous devons écrire le code suivant:


 <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" /> 

Cela signifie que quelque part dans TextView.java ou ses superclasses, il existe une logique qui sait compter et dessiner des retraits.


Voyons comment vous pouvez faire de même dans 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") } 

Changements
TextView devenu juste Text() . La propriété android:padding s'est transformée en un android:padding qui enveloppe le Text .


Les avantages
Ainsi, Text est uniquement responsable du rendu du texte lui-même. Il ne sait pas compter les retraits. Padding , en revanche, n'est responsable que du rembourrage et rien de plus. Il peut être utilisé autour de tout autre élément.


4. Flux de données unidirectionnel


Le flux de données unidirectionnel est un concept important si nous parlons, par exemple, de contrôler l'état d'une CheckBox dans un système d'interface utilisateur existant. Lorsque l'utilisateur appuie sur la CheckBox , son état devient checked = true : la classe met à jour l'état View et appelle un rappel à partir du code qui surveille le changement d'état.


Ensuite, dans le code lui-même, par exemple, dans ViewModel , vous devez mettre à jour la variable d' state correspondante. Vous disposez maintenant de deux copies de l'état activé, ce qui peut créer des problèmes. Par exemple, la modification de la valeur de la variable d' state à l'intérieur du ViewModel entraînera la mise CheckBox jour du CheckBox , qui peut se terminer par une boucle sans fin. Pour éviter cela, nous devrons trouver une sorte de béquille.


L'utilisation de Compose aidera à résoudre ces problèmes, car elle est basée sur le principe de l'unicité. Le changement d'état sera traité dans le cadre: nous donnons simplement le modèle de données vers l'intérieur. De plus, le composant de Compose ne modifie plus son état de lui-même. Au lieu de cela, il n'appelle que le rappel, et maintenant c'est la tâche de l'application de changer l'interface utilisateur.


5. Amélioration du débogage


Étant donné que l'interface utilisateur entière est maintenant écrite dans Kotlin, vous pouvez maintenant déboguer l'interface utilisateur. Je n'ai pas essayé cela moi-même, mais dans le podcast, ils ont dit que le débogueur et les points d'arrêt fonctionnent dans Compose.


Assez de mots, montrez le code


Je sais, je veux voir rapidement à quoi ressemble l'interface utilisateur dans le code (spoiler: très similaire à Flutter si vous avez essayé d'écrire dessus).


Nous commencerons par créer des View simples, puis comparerons leur apparence dans l'interface utilisateur existante et dans Compose.


1. FrameLayout vs Wrap + Padding + Background


Nous réutilisons notre exemple ci-dessus et essayons de rendre ce TextView retrait à 30dp avec un fond turquoise:


`TextView` indenté en` 30dp` et fond turquoise

Interface utilisateur existante:


 <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" /> 

Regardez maintenant le code qui fait la même chose dans Compose:


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

Voici quelques nouveautés. Étant donné que Text ne connaît que le rendu du texte, il ne se soucie pas du remplissage et de l'arrière-plan. Par conséquent, pour les ajouter, nous devons utiliser trois fonctions distinctes:


  • DrawRectangle arrière-plan
  • Padding
  • Wrap est une fonction qui superpose des paramètres comme FrameLayout .

C'est facile. Mais il est légèrement différent du système d'interface utilisateur existant auquel nous sommes tous habitués.


2. LinearLayout vs Column


Essayons maintenant de faire quelque chose d'équivalent à notre bon vieux LinearLayout .
Pour placer deux éléments l'un en dessous de l'autre, comme dans l'image ci-dessous, nous pouvons utiliser Column :


Deux éléments l'un en dessous de l'autre

Le code ressemblera à ceci:


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

Imbriqué dans l'élément Column sera situé verticalement l'un au-dessous de l'autre.


2a. Indentation


Vous avez probablement remarqué que le texte et le bouton sont trop proches du bord. Par conséquent, ajoutez un Padding .


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

Ça a l'air mieux:


Deux éléments en retrait l'un au-dessous de l'autre

2b. Intervalles


Nous pouvons également ajouter une indentation entre le Text et le Button :


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

À quoi ressemble notre écran maintenant:


Deux éléments, l'un en dessous de l'autre, en retrait et espacés

2c. LinearLayout vs Row


Placez le deuxième bouton à côté du premier:


Ajout d'un deuxième bouton

Code pour cela:


 @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") } } } } 

À l'intérieur de la Row deux boutons seront horizontaux. WidthSpacer ajoute une distance entre eux.


2d. Gravity vs Alignment


Alignez nos éléments au centre, comme le fait la gravity dans l'interface utilisateur actuelle. Pour montrer les différences, je commenterai les anciennes lignes et les remplacerai par de nouvelles:


 @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") } } } } 

Nous réussirons:


Alignement central

Avec crossAxisAlignment = CrossAxisAlignment.Center les éléments imbriqués seront centrés horizontalement. Nous devons également définir le paramètre Row sur mainAxisSize = FlexSize.Min , similaire en comportement à layout_width = wrap_content , afin qu'il ne s'étende pas sur l'écran en raison de la valeur par défaut mainAxisSize = FlexSize.Max , qui se comporte comme layout_width = match_parent .


2d. Remarque


D'après ce que nous avons vu dans les exemples ci-dessus, vous pouvez voir que tous les éléments sont constitués de fonctions distinctes: le padding est une fonction distincte, l' spacer est une fonction distincte, au lieu d'être des propriétés à l'intérieur du Text , du Button ou de la Column .


Des éléments plus complexes tels que RecyclerView ou ConstraintLayout sont en cours de développement: je n'ai donc pas pu trouver d'exemple avec eux dans les sources de démonstration.


3. Styles et thèmes


Vous avez probablement remarqué que les boutons ci-dessus sont violets par défaut. En effet, ils utilisent des styles par défaut. Voyons comment fonctionnent les styles dans Compose.


Dans les exemples ci-dessus, FormDemo balisé avec l'annotation @Composable . Je vais maintenant montrer comment cet élément est utilisé dans Activity :


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

Au lieu de la fonction setContentView() , nous utilisons setContent() , une fonction d'extension de la bibliothèque Compose.kt .


CraneWrapper contient l'arborescence Composer et donne accès à Context , Density , FocusManager et TextInputService .


MaterialTheme vous permet de personnaliser le thème des éléments.


Par exemple, je peux changer la couleur primaire du thème en marron comme suit:


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

Maintenant, notre écran ressemblera à ceci:


Bordeaux comme couleur primaire

Autres couleurs et polices pouvant être modifiées: MaterialTheme.kt # 57


Rally Activity fournit un bon exemple de la façon de personnaliser un sujet: du code source à RallyTheme.kt


Que voir / lire


Si vous en voulez plus, vous pouvez assembler l'exemple de projet selon les instructions ici .


Comme l'écrivent les utilisateurs de Windows, il n'y a maintenant aucun moyen officiel de lancer Compose, mais il existe un guide non officiel de kotlinlang Slack .


Des questions sur Compose peuvent être posées aux développeurs de la chaîne #compose kotlinlang Slack.


Laissez d'autres liens dans les commentaires - les plus utiles seront ajoutés ici.


Conclusions


Le développement de cette bibliothèque bat son plein, donc toutes les interfaces présentées ici sont sujettes à changement. Il y a encore beaucoup de choses que vous pouvez apprendre dans le code source, comme @Model et le flux de données unidirectionnel (flux de données unidirectionnel). C'est peut-être un sujet pour de futurs articles.

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


All Articles