Android Jetpack Compose First Impression

Nachdem ich auf Google IO 2019 einen Vortrag über Android Jetpack Compose gesehen hatte , wollte ich ihn sofort ausprobieren. Darüber hinaus erinnerte der darin implementierte Ansatz Flutter sehr, an dem ich früher interessiert war .



Die Compose-Bibliothek selbst befindet sich in der Pre-Alpha-Phase, sodass nicht viele Dokumentationen und Artikel darüber verfügbar sind. Als nächstes werde ich mich auf mehrere Ressourcen stützen, die ich gefunden habe, sowie auf die Open-Source-Bibliothek .


Diese Ressourcen sind:



Was ist Android Jetpack Compose?


Bisher basierte die gesamte Android-Benutzeroberfläche auf der View-Klasse. Dies ist seit den Anfängen von Android der Fall. In dieser Hinsicht haben sich viele Vermächtnisse und architektonische Mängel angesammelt, die verbessert werden könnten. Dies zu tun ist jedoch ziemlich schwierig, ohne den gesamten Code zu brechen, der auf ihrer Basis geschrieben wurde.


In den letzten Jahren sind in der Welt der Clientanwendungen (einschließlich Frontend-Trends) viele neue Konzepte aufgetaucht. Daher hat das Google-Team einen radikalen Weg eingeschlagen und die gesamte UI-Ebene in Android von Grund auf neu geschrieben. So erschien die Android Jetpack Compose-Bibliothek, die konzeptionelle Tricks von React, Litho, Vue, Flutter und vielen anderen enthält.


Lassen Sie uns einige der Funktionen der vorhandenen Benutzeroberfläche durchgehen und sie mit Compose vergleichen.


1. Unabhängigkeit von Android-Versionen


Die vorhandene Benutzeroberfläche ist eng mit der Plattform verbunden. Als die ersten Komponenten von Material Design erschienen, funktionierten sie nur mit Android 5 (API21) und höher. Um an älteren Versionen des Systems arbeiten zu können, müssen Sie die Support-Bibliothek verwenden.


Compose ist Teil von Jetpack, wodurch es unabhängig von den Systemversionen ist und auch in älteren Android-Versionen (zumindest mit API21) verwendet werden kann.


2. Die gesamte Kotlin-API


Zuvor mussten Sie sich mit verschiedenen Dateien befassen, um eine Benutzeroberfläche zu erstellen. Wir haben das Markup in XML beschrieben und dann den Java / Kotlin-Code verwendet, damit es funktioniert. Dann kehrten wir zu anderen XML-Dateien zurück, um Themen, Animationen, Navigation usw. festzulegen. Und versuchten sogar, Code in XML (Datenbindung) zu schreiben.


Mit Kotlin können Sie Benutzeroberflächen im deklarativen Stil direkt in Code anstelle von XML schreiben.


3. Composable = Composite: Verwenden von Komposition anstelle von Vererbung


Das Erstellen von benutzerdefinierten UI-Elementen kann ziemlich umständlich sein. Wir müssen von View oder seinem Nachkommen erben und uns um viele wichtige Eigenschaften kümmern, bevor es richtig gestartet wird. Beispielsweise enthält die TextView-Klasse ungefähr 30.000 Zeilen Java-Code. Dies liegt an der Tatsache, dass es eine Menge unnötiger Logik in sich enthält, die von untergeordneten Elementen geerbt wird.


Compose tauchte dagegen auf und ersetzte die Vererbung durch Komposition.


Padding eignet sich am besten, um zu veranschaulichen, worum es geht:


Um in der vorhandenen Benutzeroberfläche die mit TextView eingerückte 30dp zu 30dp :


Bild

Wir müssen den folgenden Code schreiben:


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

Dies bedeutet, dass irgendwo in TextView.java oder seinen Oberklassen eine Logik vorhanden ist, die weiß, wie Einrückungen gezählt und gezeichnet werden.


Mal sehen, wie Sie dasselbe in Compose tun können:


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

Änderungen
TextView nur noch Text() . Die android:padding Eigenschaft wurde in ein Padding , das Text umschließt.


Die Vorteile
Daher ist Text nur für das Rendern des Textes selbst verantwortlich. Er weiß nicht, wie man Einrückungen zählt. Padding hingegen ist nur für das Auffüllen verantwortlich und nicht mehr. Es kann um jedes andere Element verwendet werden.


4. Unidirektionaler Datenstrom


Der unidirektionale Datenfluss ist ein wichtiges Konzept, wenn wir beispielsweise über die Steuerung des Status einer CheckBox in einem vorhandenen UI-System CheckBox . Wenn der Benutzer auf das CheckBox , wird sein Status checked = true : Die Klasse aktualisiert den CheckBox und ruft einen Rückruf von dem Code auf, der die CheckBox überwacht.


Dann müssen Sie im Code selbst, beispielsweise in ViewModel , die entsprechende Statusvariable aktualisieren. Sie haben jetzt zwei Kopien des gedrückten Status, was zu Problemen führen kann. Wenn Sie beispielsweise den Wert der Statusvariablen im ViewModel CheckBox wird die CheckBox aktualisiert, was in einer Endlosschleife enden kann. Um dies zu vermeiden, müssen wir uns eine Art Krücke einfallen lassen.


Die Verwendung von Compose hilft bei der Lösung dieser Probleme, da es auf dem Prinzip der Einseitigkeit basiert. Die Zustandsänderung wird innerhalb des Frameworks verarbeitet: Wir geben das Datenmodell einfach nach innen. Außerdem ändert die Komponente in Compose jetzt ihren Status nicht mehr von selbst. Stattdessen wird nur ein Rückruf aufgerufen, und jetzt ist es die Aufgabe der Anwendung, die Benutzeroberfläche zu ändern.


5. Verbessern des Debuggens


Da die gesamte Benutzeroberfläche jetzt in Kotlin geschrieben ist, können Sie die Benutzeroberfläche jetzt debuggen. Ich habe das nicht selbst versucht, aber im Podcast wurde gesagt, dass Debugger und Haltepunkte in Compose funktionieren.


Genug Wörter, zeigen Sie den Code


Ich weiß, ich möchte schnell sehen, wie die Benutzeroberfläche im Code aussieht (Spoiler: sehr ähnlich zu Flutter, wenn Sie versucht haben, darauf zu schreiben).


Zunächst erstellen wir einige einfache View und vergleichen dann, wie sie in der vorhandenen Benutzeroberfläche und in Compose aussehen.


1. FrameLayout vs Wrap + Padding + Background


Wir verwenden unser Beispiel oben erneut und versuchen, diese TextView mit 30dp und türkisfarbenem Hintergrund 30dp :


`TextView` wird bearbeitet in` 30dp` und türkisfarbenem Hintergrund

Bestehende Benutzeroberfläche:


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

Schauen Sie sich nun den Code an, der in Compose dasselbe tut:


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

Hier sind ein paar neue Dinge. Da Text nur das Rendern von Text kennt, sind Auffüllen und Hintergrund nicht wichtig. Um sie hinzuzufügen, müssen wir daher drei separate Funktionen verwenden:


  • DrawRectangle Hintergrund
  • Padding auffüllen
  • Wrap ist eine Funktion, die Parameter wie FrameLayout überlagert.

Einfach. Es unterscheidet sich jedoch geringfügig von dem bestehenden UI-System, an das wir alle gewöhnt sind.


2. Vertikales LinearLayout gegen Column


Versuchen wir nun, etwas zu tun, das unserem guten alten LinearLayout .
Um zwei Elemente wie im Bild unten untereinander zu platzieren, können Sie Column :


Zwei elemente untereinander

Der Code sieht folgendermaßen aus:


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

Das im Column Element Column Element befindet sich vertikal untereinander.


2a. Einrückung


Sie haben wahrscheinlich bemerkt, dass sich Text und Schaltfläche zu nahe am Rand befinden. Padding daher Padding .


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

Es sieht besser aus:


Zwei elemente untereinander besorgten sich

2b. Intervalle


Wir können auch einige Einrückungen zwischen Text und Button hinzufügen:


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

Wie unser Bildschirm jetzt aussieht:


Zwei Elemente untereinander, Vertrauensstellungen und beabstandet

2c. Horizontales LinearLayout gegen Row


Platzieren Sie den zweiten Knopf neben dem ersten:


Eine zweite schaltfläche wurde hinzugefügt

Code dafür:


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

Innerhalb der Row beiden Schaltflächen horizontal. WidthSpacer fügt den Abstand zwischen ihnen hinzu.


2d. Gravity gegen Alignment


Richten Sie unsere Elemente in der Mitte aus, wie es die gravity in der aktuellen Benutzeroberfläche tut. Um diff zu zeigen, werde ich die alten Zeilen auskommentieren und durch neue ersetzen:


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

Wir werden Erfolg haben:


Zentrale ausrichtung

Mit crossAxisAlignment = CrossAxisAlignment.Center verschachtelte Elemente horizontal zentriert. Wir sollten den Row Parameter auch auf mainAxisSize = FlexSize.Min , ähnlich wie layout_width = wrap_content damit er sich aufgrund des Standardwerts mainAxisSize = FlexSize.Max , der sich wie layout_width = match_parent verhält, nicht über den Bildschirm layout_width = match_parent .


2d. Bemerkung


Anhand der obigen Beispiele können Sie erkennen, dass alle Elemente aus separaten Funktionen zusammengesetzt sind: padding ist eine separate Funktion, spacer ist eine separate Funktion, anstatt Eigenschaften in Text , Button oder Column .


Komplexere Elemente wie RecyclerView oder ConstraintLayout befinden sich in der Entwicklung. Daher konnte ich in den Demoquellen kein Beispiel dafür finden.


3. Stile und Themen


Sie haben wahrscheinlich bemerkt, dass die obigen Schaltflächen standardmäßig lila sind. Dies liegt daran, dass sie Standardstile verwenden. Mal sehen, wie Stile in Compose funktionieren.


In den obigen Beispielen ist FormDemo mit der Annotation @Composable . Jetzt werde ich zeigen, wie dieses Element in Activity :


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

Anstelle der Funktion setContentView() verwenden wir setContent() , eine Erweiterungsfunktion aus der Compose.kt Bibliothek.


CraneWrapper enthält den Compose-Baum und bietet Zugriff auf Context , Density , FocusManager und TextInputService .


MaterialTheme können Sie das Thema für Elemente anpassen.


Zum Beispiel kann ich die Primärfarbe des Themas wie folgt in Braun ändern:


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

Jetzt sieht unser Bildschirm folgendermaßen aus:


Kastanienbraun als Grundfarbe

Andere Farben und Schriftarten, die geändert werden können: MaterialTheme.kt # 57


Die Rallye-Aktivität bietet ein gutes Beispiel für das Anpassen eines Themas: Quellcode an RallyTheme.kt


Was zu sehen / zu lesen


Wenn Sie mehr möchten, können Sie das Beispielprojekt gemäß den Anweisungen hier zusammenstellen .


Wie Windows-Benutzer schreiben, gibt es jetzt keine offizielle Möglichkeit, Compose zu starten, aber es gibt eine inoffizielle Anleitung von kotlinlang Slack .


Fragen zu Compose können Entwicklern im Kanal #compose kotlinlang Slack gestellt werden.


Lassen Sie andere Links in den Kommentaren - die nützlichsten werden hier hinzugefügt.


Schlussfolgerungen


Die Entwicklung dieser Bibliothek ist in vollem Gange, daher können sich die hier gezeigten Schnittstellen ändern. Es gibt noch viele Dinge, die Sie im Quellcode lernen können, wie z. B. @Model und unidirektionaler Datenfluss (unidirektionaler Datenstrom). Vielleicht ist dies ein Thema für zukünftige Artikel.

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


All Articles