Endlich ist der Moment gekommen, in dem Sie Android Studio nicht selbst erstellen müssen, um das neue deklarative UI-Framework für Android zu testen. Jetpack Compose ist jetzt als erste Dev Preview im Maven Repository von Google verfügbar. Mit diesen Neuigkeiten begann mein Montagmorgen. Und sofort bestand der Wunsch zu sehen, auf welche Werkzeuge sie gewartet hatten.

Ich beschloss, meine Bekanntschaft sofort mit dem Versuch zu beginnen, sie in das auf Google Play veröffentlichte Haustierprojekt einzuführen. Außerdem wollte ich lange Zeit eine Seite „Über die Anwendung“ erstellen. In diesem Artikel werde ich über die Hauptkomponenten und Verbindungsschritte von Compose sprechen:
- Abhängigkeitsverbindung
- Themen und Stile. Integration mit vorhandenen im Projekt.
- Zugänglichkeits- und UI-Tests.
- Die Hauptkomponenten und Analoga der View-Erben.
- Arbeite mit dem Staat.
Abhängigkeitsverbindung
Zu Beginn habe ich das Studio von 3.5 auf 3.5.1 aktualisiert (vergebens) und grundlegende Abhängigkeiten hinzugefügt. Eine vollständige Liste finden Sie in einem Artikel von Cyril .
// build.gradle ext.compose_version= '0.1.0-dev01' //build.gradle dependencies{ ... implementation "androidx.compose:compose-runtime:$compose_version" kapt "androidx.compose:compose-compiler:$compose_version" implementation "androidx.ui:ui-layout:$compose_version" implementation "androidx.ui:ui-android-text:$compose_version" implementation "androidx.ui:ui-text:$compose_version" implementation "androidx.ui:ui-material:$compose_version" }
Und dann habe ich versucht, all dies wegen der verstreuten Versionen von Firebase zu sammeln. Danach bin ich auf Compose-Hindernisse gestoßen:
app/src/main/AndroidManifest.xml Error: uses-sdk:minSdkVersion 16 cannot be smaller than version 21 declared in library [androidx.ui:ui-layout:0.1.0-dev01] .../ui-layout-0.1.0-dev01/AndroidManifest.xml as the library might be using APIs not available in 16 Suggestion: use a compatible library with a minSdk of at most 16, or increase this project's minSdk version to at least 21, or use tools:overrideLibrary="androidx.ui.layout" to force usage (may lead to runtime failures)
Ja, Compose war nur mit minSdk 21 (Lolipop) verfügbar. Vielleicht ist dies eine vorübergehende Maßnahme, aber es wurde erwartet, dass frühere Versionen des Betriebssystems unterstützt werden.
Das ist aber noch nicht alles. Compose arbeitet mit Reflection anstelle des Kotlin Compiler Plugins, wie hier bereits erwähnt . Damit alles beginnen kann, müssen Sie den Kotlin Reflect auch hinzufügen, abhängig von:
implementation "org.jetbrains.kotlin:kotlin-reflect"
Zum Nachtisch. Compose dp implementiert Erweiterungsfunktionen für Int, Long, Float, die mit dem Schlüsselwort inline gekennzeichnet sind. Dies kann einen neuen Kompilierungsfehler verursachen:
Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option * https://stackoverflow.com/questions/48988778/cannot-inline-bytecode-built-with-jvm-target-1-8-into-bytecode-that-is-being-bui
Um dies zu lösen, müssen Sie die JVM-Version für Kotlin explizit registrieren:
android { … kotlinOptions { jvmTarget = "1.8" } }
Das scheint alles zu sein. Viel einfacher als ein eigenes Studio zu bauen
Lassen Sie uns versuchen, Hello World auszuführen (ebenfalls aus Cyrils Artikel, aber im Gegensatz zu ihm Compose inside Fragment hinzufügen). Das Layout für das Fragment ist ein leeres FrameLayout.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val fragmentView = inflater.inflate(R.layout.fragment_about, container, false) (fragmentView as ViewGroup).setContent { Hello("Jetpack Compose") } return fragmentView } @Composable fun Hello(name: String) = MaterialTheme { FlexColumn { inflexible {
Wir starten, der folgende Bildschirm stellt sich heraus:
Aufgrund der Tatsache, dass Composable das Standardmaterial-Design verwendet, haben wir eine lila AppBar erhalten. Nun, und wie erwartet stimmt es überhaupt nicht mit dem dunklen Thema der Anwendung überein:
Versuchen wir es zu lösen.
Themen und Stile. Integration mit vorhandenen im Projekt.
Um vorhandene Stile in Composable zu verwenden, übergeben wir sie im MaterialTheme-Konstruktor:
@Composable fun Hello(name: String) = MaterialTheme(colors = MaterialColors( primary = resolveColor(context, R.attr.colorPrimary, MaterialColors().primary), secondary = resolveColor(context, R.attr.colorSecondary, MaterialColors().secondary), onBackground = resolveColor(context, R.attr.textColor, MaterialColors().onBackground) )){...}
MaterialTheme selbst besteht aus zwei Teilen: MaterialColors und MaterialTypography.
Um die Farben aufzulösen, habe ich einen Wrapper über die Stile verwendet:
private fun resolveColor(context: Context?, @AttrRes attrRes: Int, colorDefault: Color) = context?.let { Color(resolveThemeAttr(it, attrRes).data.toLong()) } ?: colorDefault private fun resolveThemeAttr(context: Context, @AttrRes attrRes: Int): TypedValue { val theme = context.theme val typedValue = TypedValue() theme.resolveAttribute(attrRes, typedValue, true) return typedValue }
In diesem Stadium wird die AppBar grün. Zum Neulackieren des Textes müssen Sie jedoch noch eine Aktion ausführen:
Text("Hello $name!", style = TextStyle(color = +themeColor { onBackground }))
Das Thema wird mit der unären Plus-Operation auf das Widget angewendet. Wir werden es immer noch sehen, wenn wir mit State arbeiten.
Jetzt sieht der neue Bildschirm in beiden Varianten des Themas mit dem Rest der Anwendung einheitlich aus:
Compose hat auch die Datei DarkTheme.kt in den Quellen gefunden, deren Funktionen verwendet werden können, um verschiedene Auslöser für das Einschalten eines dunklen Themas unter Android P und 10 zu bestimmen.
Zugänglichkeits- und UI-Tests.
Bis der Bildschirm mit neuen Elementen zu wachsen beginnt, sehen wir uns an, wie er im Layout-Inspektor und bei aktivierter Anzeige der Rahmen von Elementen im Entwicklungsmodus aussieht:

Hier sehen wir FrameLayout, in dem sich nur AndroidComposeView befindet. Bestehende Tools für Accebility- und UI-Tests sind nicht mehr anwendbar? Vielleicht gibt es stattdessen eine neue Bibliothek: androidx.ui:ui-test
.
Die Hauptkomponenten und Analoga der View-Erben.
Versuchen wir nun, den Bildschirm etwas informativer zu gestalten. Ändern Sie zunächst den Text, fügen Sie eine Schaltfläche zur Anwendungsseite bei Google Play und ein Bild mit einem Logo hinzu. Ich zeige Ihnen sofort den Code und was passiert ist:
@Composable fun AboutScreen() = MaterialTheme(...) { FlexColumn { inflexible { TopAppBar<MenuItem>(title = { Text(getString(R.string.about)) }) } expanded(1F) { VerticalScroller { Column { Image() Title() MyButton() } } } } } private fun Image() { Center { Padding(16.dp) { Container( constraints = DpConstraints( minWidth = 96.dp, minHeight = 96.dp ) ) { imageFromResource(resources, R.drawable.ic_launcher) } } } } private fun Title() { Center { Padding(16.dp) { Text(getString(R.string.app_name) + " " + BuildConfig.VERSION_NAME, style = TextStyle(color = +themeColor { onBackground })) } } } private fun MyButton() { Center { Padding(16.dp) { Button(getString(R.string.about_button), onClick = { openAppInPlayStore() }) } } }
Die Grundprinzipien der Widget-Komposition haben sich seit dem ersten Erscheinen von Compose-Quellen nicht geändert.
Aus dem Interessanten:
Lassen Sie uns nun die vorhandene Hauptansicht-Gruppe durchgehen und versuchen, Analoga in Compose zu finden.
Anstelle von FrameLayout können Sie Stack verwenden. Hier ist alles einfach: Die untergeordneten Widgets überlappen sich und werden abhängig von der für den Anhang verwendeten Funktion positioniert: ausgerichtet, positioniert oder erweitert.
LinearLayout wird sofort durch zwei Widgets ersetzt: Column und Row, anstatt den Parameter android: Orientierung zu verwenden. Sie enthalten wiederum FlexColumn und FlexRow mit einer Ebene unflexibler Funktionen über einem verschachtelten Teilbaum. Nun, FlexColumn und FlexRow selbst basieren auf Flex mit dem Parameter orientation = LayoutOrientation.Vertical
oder Horizontal
.
Eine ähnliche Hierarchie für FlowColumn-, FlowRow- und Flow-Widgets. Ihr Hauptunterschied: Wenn der Inhalt nicht in eine Spalte oder Zeile passt, wird der nächste als nächstes gezeichnet und die eingebetteten Widgets „fließen“ dorthin. Es fällt mir schwer, mir den eigentlichen Zweck dieser Widgets vorzustellen.
Der ScrollView-Effekt wird erzielt, indem eine Spalte oder Zeile in einem VerticalScroller oder HorizontalScroller platziert wird. Beide komponieren im Scroller und übergeben den Parameter isVertical = true
oder false
.
Auf der Suche nach einem Analogon für ConstraintLayout oder zumindest RelativeLayout stieß man auf ein neues Tabellen-Widget. Ich habe versucht, den Beispielcode in meiner Anwendung auszuführen: DataTableSamples.kt . Da ich jedoch nicht versucht habe, das Beispiel zu vereinfachen, hat es nicht funktioniert, damit es funktioniert.
Arbeite mit dem Staat
Eine der am meisten erwarteten Neuerungen des Frameworks ist seine sofort einsatzbereite Bereitschaft zur Verwendung in unidirektionalen Architekturen, die auf der Grundlage eines einzelnen Zustands erstellt wurden. Und dies sollte die Annotation @Model für Tag-Klassen einführen, die den Status für das Rendern der Benutzeroberfläche bereitstellen.
Betrachten Sie ein Beispiel:
data class DialogVisibleModel(val visible: Boolean, val dismissPushed: Boolean = false) ... @Composable fun SideBySideAlertDialogSample() { val openDialog = +state { DialogVisibleModel(true) } Button(text = "Ok", onClick = { openDialog.value = DialogVisibleModel(true) }) if (openDialog.value.visible) { AlertDialog( onCloseRequest = {
Dadurch wird eine Datenklasse für das Statusmodell erstellt, die nicht mit der Annotation @Model gekennzeichnet werden muss.
Der Anfangszustand selbst wird in der Funktion @Composable mit dem Zustand + erstellt.
Die Sichtbarkeit des Dialogfelds wird durch die sichtbare Eigenschaft des Modells bestimmt, die durch Aufrufen der value-Eigenschaft erhalten wird.
Diese Eigenschaft kann auch auf ein neues unveränderliches Objekt festgelegt werden, wie dies beim onClick beider Schaltflächen der Fall ist. Der erste versteckt sich, der zweite - schließt den Dialog. Der Dialog kann wieder geöffnet werden, indem Sie auf die Schaltfläche OK klicken, die in derselben @ Composable-Funktion definiert ist.
Beim Versuch, einen Status außerhalb dieser Funktion zu erstellen, tritt ein Fehler auf:
java.lang.IllegalStateException: Composition requires an active composition context.
Der Kontext kann durch Zuweisen des Werts der Funktion setContent {} in onCreateView abgerufen werden. Wie Sie ihn beispielsweise in Presenter oder einer anderen Klasse als Fragment oder Activity zum Ändern des Status verwenden können, ist jedoch noch unklar.

Damit ist unsere Überprüfung der neuen Jetpack Compose-Bibliothek abgeschlossen. Das Framework begründet seinen Namen architektonisch und ersetzt alle Vererbungen, die in der Ansichtshierarchie so unpraktisch waren, durch Komposition. Es gibt immer noch zu viele Fragen darüber, wie Analoga komplexerer ViewGroups implementiert werden, wie z. B. ConstraintLayout und RecyclerView. nicht genug Dokumentation und Vorschau.
Es ist absolut klar, dass Compose auch in kleinen Kampfanwendungen nicht einsatzbereit ist.
Dies ist jedoch nur die erste Version von Dev Preview. Es wird interessant sein, die Entwicklung des Konzepts der Zusammenarbeit mit Staat und Bibliotheken aus der auf Compose basierenden Community zu beobachten.
Wenn Sie erfolgreichere Beispiele für Code oder Dokumentation für Fälle finden, die ich nicht erhalten konnte, schreiben Sie bitte in die Kommentare.