Jetpack Compose basiert auf Kotlin. In einigen Fällen bietet Kotlin spezielle Redewendungen, die das Schreiben von gutem Compose-Code erleichtern. Wenn Sie in einem anderen und diese Sprache gedanklich in Kotlin übersetzen, verpassen Sie wahrscheinlich einige der Stärken von "Schreiben", ist es schwierig, idiomatisch geschriebenen Kotlin-Code zu verstehen. Mehr gewinnen Wenn Sie mit dem Kotlin-Stil vertraut sind, können Sie diese Schwierigkeiten vermeiden.
Standardargumente
Wenn Sie eine Kotlin-Funktion schreiben, können Sie Standardwerte dafür angeben Argumente, wenn der Aufrufer diese Werte nicht explizit übergibt. Diese Funktion reduziert die Notwendigkeit überlasteter Funktionen.
Angenommen, Sie möchten eine Funktion schreiben, die ein Quadrat zeichnet. Das Funktion einen einzelnen erforderlichen Parameter sideLength haben, mit dem die Länge angegeben wird auf jeder Seite. Es kann mehrere optionale Parameter wie Dicke, edgeColor und so weiter. Wenn der Aufrufer diese nicht angibt, für die Funktion Standardwerte verwendet. In anderen Sprachen verschiedene Funktionen:
// We don't need to do this in Kotlin! void drawSquare(int sideLength) { } void drawSquare(int sideLength, int thickness) { } void drawSquare(int sideLength, int thickness, Color edgeColor) { }
In Kotlin können Sie eine einzelne Funktion schreiben und die Standardwerte für die Argumente:
fun drawSquare( sideLength: Int, thickness: Int = 2, edgeColor: Color = Color.Black ) { }
Abgesehen davon, dass Sie nicht mehrere redundante Funktionen schreiben müssen,
wird Ihr Code viel klarer lesbar. Wenn der Aufrufer kein
-Wert für ein Argument, das angibt, dass sie zur Verwendung des Standardwerts
Wert. Außerdem können Sie anhand der benannten Parameter
aktiviert. Wenn Sie im Code einen Funktionsaufruf wie diesen sehen,
wissen, was die Parameter bedeuten, ohne den drawSquare()
-Code zu überprüfen:
drawSquare(30, 5, Color.Red);
Im Gegensatz dazu ist dieser Code selbstdokumentierend:
drawSquare(sideLength = 30, thickness = 5, edgeColor = Color.Red)
Die meisten Schreibbibliotheken verwenden Standardargumente. für die zusammensetzbaren Funktionen, die Sie schreiben. Durch diese Übung werden Ihre zusammensetzbare Funktionen anpassbar, macht das Standardverhalten aber dennoch einfach aufzurufen. Sie können beispielsweise ein einfaches Textelement wie folgt erstellen:
Text(text = "Hello, Android!")
Dieser Code hat den gleichen Effekt wie der folgende, viel ausführlichere Code, in dem
mehr von den
Text
werden explizit festgelegt:
Text( text = "Hello, Android!", color = Color.Unspecified, fontSize = TextUnit.Unspecified, letterSpacing = TextUnit.Unspecified, overflow = TextOverflow.Clip )
Das erste Code-Snippet ist nicht nur viel einfacher und leichter zu lesen, sondern auch
selbst dokumentieren. Wenn Sie nur den Parameter text
angeben, dokumentieren Sie das für
anderen Parametern verwenden, möchten Sie die Standardwerte verwenden. Im Gegensatz dazu
das zweite Snippet impliziert, dass Sie die Werte für diese
Parameter verwendet. Die von Ihnen festgelegten Werte sind
jedoch die Standardwerte für
der Funktion.
Funktionen höherer Ordnung und Lambda-Ausdrücke
Kotlin unterstützt höhere Reihenfolge
Funktionen, die
andere Funktionen als Parameter empfangen. Die Erstellung baut auf diesem Ansatz auf. Für
Beispiel: Der Parameter
Button
zusammensetzbare Funktion stellt einen Lambda-Parameter onClick
bereit. Der Wert
dieses Parameters ist eine Funktion, die von der Schaltfläche aufgerufen wird, wenn der Nutzer darauf klickt:
Button( // ... onClick = myClickFunction ) // ...
Funktionen höherer Ordnung koppeln natürlich mit Lambda-Ausdrücken, Ausdrücken,
die als Funktion ausgewertet werden. Wenn Sie die Funktion nur einmal benötigen,
um sie an anderer Stelle zu definieren und an die übergeordnete Funktion zu übergeben. Stattdessen können Sie
definiere einfach die Funktion direkt
mit einem Lambda-Ausdruck. Das vorherige Beispiel
setzt voraus, dass myClickFunction()
an anderer Stelle definiert ist. Aber wenn Sie nur diese
ist es einfacher, die Funktion inline mit einer Lambda-Funktion zu definieren.
Ausdruck:
Button( // ... onClick = { // do something // do something else } ) { /* ... */ }
Nachgestellte Lambdas
Kotlin bietet eine spezielle Syntax zum Aufrufen höherrangiger Funktionen, deren last ein Lambda-Parameter ist. Wenn Sie einen Lambda-Ausdruck übergeben möchten, kannst du nachgestellte Lambda- Syntax. Anstatt den Lambda-Ausdruck in die Klammern zu setzen, setzen Sie ihn . Das kommt häufig in der Funktion zum Schreiben von Text vor, Sie sollten sich also mit dem Aussehen des Codes.
Der letzte Parameter für alle Layouts, wie der
Column()
zusammensetzbare Funktion ist content
, eine Funktion, die die untergeordnete Benutzeroberfläche ausgibt.
Elemente. Angenommen, Sie möchten eine Spalte
mit drei Textelementen erstellen,
und müssen etwas formatieren. Dieser Code würde funktionieren,
umständlich:
Column( modifier = Modifier.padding(16.dp), content = { Text("Some text") Text("Some more text") Text("Last text") } )
Da der Parameter content
der letzte in der Funktionssignatur ist und
übergeben wir seinen Wert als Lambda-Ausdruck, wir können ihn aus der
Klammern:
Column(modifier = Modifier.padding(16.dp)) { Text("Some text") Text("Some more text") Text("Last text") }
Die beiden Beispiele haben dieselbe Bedeutung. Die geschweiften Klammern definieren die Lambda-Funktion.
Ausdruck, der an den Parameter content
übergeben wird.
Wenn der einzige Parameter, den Sie übergeben, ein nachgestelltes Lambda ist, also
wenn der letzte Parameter ein Lambda ist und Sie keine anderen
-Parameter zu setzen – Sie können die Klammern ganz weglassen. Angenommen, Sie
musste keinen Modifizierer an Column
übergeben. Sie könnten den Code so schreiben,
dies:
Column { Text("Some text") Text("Some more text") Text("Last text") }
Diese Syntax kommt in Compose recht häufig vor, insbesondere bei Layoutelementen wie
Column
Der letzte Parameter ist ein Lambda-Ausdruck, der die
untergeordneten Elementen. Diese untergeordneten Elemente werden nach dem Funktionsaufruf in geschweiften Klammern angegeben.
Sucher und Empfänger
Einige Methoden und Attribute sind nur in einem bestimmten Bereich verfügbar. Die eingeschränkte können Sie Funktionen dort anbieten, wo sie benötigt werden, und vermeiden, wenn dies nicht angemessen ist.
Sehen wir uns ein Beispiel aus dem Tool „Compose“ an. Wenn Sie das Layout Row
aufrufen
zusammensetzbar ist, wird das Lambda für Inhalte automatisch in einem RowScope
aufgerufen.
Dadurch kann Row
Funktionen bereitstellen, die nur innerhalb eines Row
-Objekts gültig sind.
Das folgende Beispiel zeigt, wie Row
einen zeilenspezifischen Wert für
den align
-Modifikator:
Row { Text( text = "Hello world", // This Text is inside a RowScope so it has access to // Alignment.CenterVertically but not to // Alignment.CenterHorizontally, which would be available // in a ColumnScope. modifier = Modifier.align(Alignment.CenterVertically) ) }
Einige APIs akzeptieren Lambdas, die im Empfängerbereich aufgerufen werden. Diese Lambdas Zugriff auf Eigenschaften und Funktionen haben, die an anderer Stelle definiert sind, basierend auf dem Parameterdeklaration:
Box( modifier = Modifier.drawBehind { // This method accepts a lambda of type DrawScope.() -> Unit // therefore in this lambda we can access properties and functions // available from DrawScope, such as the `drawRectangle` function. drawRect( /*...*/ /* ... ) } )
Weitere Informationen finden Sie unter Funktionsliterale mit Empfänger in der Kotlin-Dokumentation.
Delegierte Attribute
Kotlin unterstützt delegierte
Eigenschaften.
Diese Eigenschaften werden wie Felder aufgerufen, ihr Wert ist jedoch
dynamisch durch Auswerten eines Ausdrucks ermittelt. Sie erkennen diese
Eigenschaften durch Verwendung der by
-Syntax:
class DelegatingClass { var name: String by nameGetterFunction() // ... }
Anderer Code kann mit folgendem Code auf die Property zugreifen:
val myDC = DelegatingClass() println("The name property is: " + myDC.name)
Wenn println()
ausgeführt wird, wird nameGetterFunction()
aufgerufen, um den Wert zurückzugeben.
der Zeichenfolge ein.
Diese delegierten Attribute sind besonders nützlich, staatlich unterstützten Properties:
var showDialog by remember { mutableStateOf(false) } // Updating the var automatically triggers a state change showDialog = true
Datenklassen löschen
Wenn Sie ein Datenelement
können Sie ganz einfach
auf die Daten mit einer destruktiven
Erklärung. Für
Angenommen, Sie definieren eine Person
-Klasse:
data class Person(val name: String, val age: Int)
Wenn Sie ein Objekt dieses Typs haben, können Sie mit folgendem Code auf seine Werte zugreifen: dies:
val mary = Person(name = "Mary", age = 35) // ... val (name, age) = mary
Diese Art von Code wird in Compose-Funktionen häufig angezeigt:
Row { val (image, title, subtitle) = createRefs() // The `createRefs` function returns a data object; // the first three components are extracted into the // image, title, and subtitle variables. // ... }
Datenklassen bieten viele weitere nützliche Funktionen. Wenn Sie zum Beispiel
definiert, definiert der Compiler automatisch nützliche Funktionen wie
equals()
und copy()
. Weitere Informationen finden Sie in den Daten
Klassen.
Singleton-Objekte
Mit Kotlin ist es einfach, Singleton-Klassen zu deklarieren, d. h. Klassen, die immer ein und
nur eine Instanz. Diese Singleton-Werte werden mit dem Keyword object
deklariert.
Compose verwendet häufig solche Objekte. Beispiel:
MaterialTheme
ist
definiert als Singleton-Objekt; die MaterialTheme.colors
, shapes
und
typography
-Eigenschaften enthalten alle die Werte für das aktuelle Design.
Typsichere Builder und DSLs
Mit Kotlin können domainspezifische Sprachen (DSLs) erstellt werden. mit typsicheren Buildern. DSLs ermöglichen den Aufbau komplexer hierarchischer Daten besser wartbar und lesbarer gestalten.
Jetpack Compose nutzt DSLs für einige APIs wie
LazyRow
und LazyColumn
.
@Composable fun MessageList(messages: List<Message>) { LazyColumn { // Add a single item as a header item { Text("Message List") } // Add list of messages items(messages) { message -> Message(message) } } }
Kotlin garantiert typsichere Builder mit
Funktionsliterale mit Empfänger.
Wenn wir die Canvas
nehmen,
zusammensetzbar ist, verwendet sie als Parameter eine Funktion mit
DrawScope
als Empfänger, onDraw: DrawScope.() -> Unit
, sodass der Codeblock
Mitgliederfunktionen aufrufen, die in DrawScope
definiert sind.
Canvas(Modifier.size(120.dp)) { // Draw grey background, drawRect function is provided by the receiver drawRect(color = Color.Gray) // Inset content by 10 pixels on the left/right sides // and 12 by the top/bottom inset(10.0f, 12.0f) { val quadrantSize = size / 2.0f // Draw a rectangle within the inset bounds drawRect( size = quadrantSize, color = Color.Red ) rotate(45.0f) { drawRect(size = quadrantSize, color = Color.Blue) } } }
Weitere Informationen zu typsicheren Buildern und DSLs finden Sie unter Kotlin-Dokumentation
Kotlin-Koroutinen
Koroutinen bieten Unterstützung für asynchrone Programmierung auf Sprachebene in Kotlin Koroutinen können die Ausführung sperren, ohne Threads zu blockieren. A Die responsive Benutzeroberfläche ist asynchron. Jetpack Compose löst dieses Problem, Koroutinen auf API-Ebene anstelle von Callbacks verwenden.
Jetpack Compose bietet APIs, die die Verwendung von Koroutinen auf der UI-Ebene sicher machen.
Die rememberCoroutineScope
gibt ein CoroutineScope
-Objekt zurück, mit dem Sie Koroutinen in Event-Handlern erstellen und
Aussetzen von APIs erstellen Sehen Sie sich dazu das Beispiel unten mit der Methode
ScrollState
s
animateScrollTo
API verwenden.
// Create a CoroutineScope that follows this composable's lifecycle val composableScope = rememberCoroutineScope() Button( // ... onClick = { // Create a new coroutine that scrolls to the top of the list // and call the ViewModel to load data composableScope.launch { scrollState.animateScrollTo(0) // This is a suspend function viewModel.loadData() } } ) { /* ... */ }
Koroutinen führen den Codeblock standardmäßig sequentiell aus. Ein Lauftraining
Koroutine, die eine Beendigungsfunktion aufruft, ihre Ausführung angehalten, bis die
aussetzen. Dies gilt auch dann, wenn die Aussetztfunktion das
in ein anderes CoroutineDispatcher
-Objekt. Im vorherigen Beispiel
loadData
wird erst ausgeführt, wenn die Unterbrechungsfunktion animateScrollTo
angewendet wird
Rücksendungen.
Um Code gleichzeitig auszuführen, müssen neue Koroutinen erstellt werden. Im Beispiel
um das Scrollen zum oberen Bildschirmrand und das Laden von Daten aus
viewModel
, zwei Koroutinen sind erforderlich.
// Create a CoroutineScope that follows this composable's lifecycle val composableScope = rememberCoroutineScope() Button( // ... onClick = { // Scroll to the top and load data in parallel by creating a new // coroutine per independent work to do composableScope.launch { scrollState.animateScrollTo(0) } composableScope.launch { viewModel.loadData() } } ) { /* ... */ }
Koroutinen erleichtern das Kombinieren asynchroner APIs. Im Folgenden
Wir kombinieren beispielsweise den pointerInput
-Modifikator mit den Animations-APIs,
die Position eines Elements animieren, wenn Nutzende auf den Bildschirm tippen.
@Composable fun MoveBoxWhereTapped() { // Creates an `Animatable` to animate Offset and `remember` it. val animatedOffset = remember { Animatable(Offset(0f, 0f), Offset.VectorConverter) } Box( // The pointerInput modifier takes a suspend block of code Modifier .fillMaxSize() .pointerInput(Unit) { // Create a new CoroutineScope to be able to create new // coroutines inside a suspend function coroutineScope { while (true) { // Wait for the user to tap on the screen val offset = awaitPointerEventScope { awaitFirstDown().position } // Launch a new coroutine to asynchronously animate to // where the user tapped on the screen launch { // Animate to the pressed position animatedOffset.animateTo(offset) } } } } ) { Text("Tap anywhere", Modifier.align(Alignment.Center)) Box( Modifier .offset { // Use the animated offset as the offset of this Box IntOffset( animatedOffset.value.x.roundToInt(), animatedOffset.value.y.roundToInt() ) } .size(40.dp) .background(Color(0xff3c1361), CircleShape) ) }
Weitere Informationen zu Koroutinen findest du in der Kotlin-Koroutinen für Android
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Materialkomponenten und Layouts
- Nebeneffekte in der Funktion „Schreiben“
- Grundlagen des E-Mail-Layouts