‏ ‏ ‎ ‏ ‏ ‎

1. Android Views mit Jetpack Compose

android 2 0

1.1. Einleitung

Das wichtigste Element bei Android-Apps ist natürlich die GUI. Früher hat man die GUI mit Java-Code entwickelt und mit XML-Dateien konfiguriert (imperativ).

Seit ein paar Jahren ist man allerdings dazu übergegangen, diese mit Kotlin-Code unter Verwendung der deklarativen Library Jetpack Compose zu entwickeln (deklarativ).

1.2. Programm

Geht nach Kapitel 1 vor, um ein neues Projekt zu erstellen. Das Projekt sollte "Compose Basics" heißen und wir werden die Vorschau-Funktion verwenden, um die UI-Elemente zu betrachten.

  1. In unserem Projekt, mach ein neues Package und nenne es components. Hier werden wir alle Komponenten hinzufügen, die wir erstellen.

  2. Erstelle ein Kotlin File und nenne es UiComponents.kt. Innerhalb von UIComponent, erstelle eine composable Funktion, nenne sie EditTextExample() und rufe die OutlinedTextField() Funktion auf. Dabei wirst Du aufgefordert, den erforderlichen Import zu importieren, der androidx.Compose.material.OutlinedTextField ist:

    @Composable
    fun EditTextExample() {
    OutlinedTextField()
    }
  3. Wenn man sich die Signatur von OutlineTextField() genauer ansieht, bemerkt man neben der @Composable-Annotation, dass sie eine Menge Parameter hat, die alle optional sind und mit einem Default-Wert versehen sind. Auf diese Weise können Sie die Felder parametrisieren und an Ihre Bedürfnisse anpassen.

    android 2 1

  4. Für dieses Beispiel werden wir nicht viel mit DU machen, die wir erstellen. Wir wollen nur zeigen wie man sie grundsätzlich erstellt.

  5. Jetzt, damit wir unsere Methode für unsere Zwecke anpassen können, können wir die Parameter, die wir nicht benötigen, weglassen und nur die verwenden, die wir benötigen. Wir werden die folgenden Parameter verwenden: Text, Color und Modifier, um es zu dekorieren. Dem Modifier können wir eine Liste von Modifier-Objekten übergeben, die wir verwenden möchten. So setzen wir zum Beispiel fillMaxWidth(), um die Breite des Textfelds auf die maximale Breite zu setzen. Wenn wir fill() aufrufen, wird das Textfeld voll gefüllt. Wir setzen padding(top) auf 16.dp, was zusätzlichen Platz entlang jeder Kante des Inhalts in dp anwendet. Es hat auch einen Wert, der der Wert ist, der im OutlinedTextField eingegeben werden soll, und ein onValueChange-Lambda, das auf die Eingabeänderung hört.

    Hausübung: Installieren und auf die neueste Version updaten.
  6. Wir weisen unserem OutlinedText auch Randfarben zu, wenn er fokussiert und nicht fokussiert ist, um verschiedene Zustände darzustellen. Wenn Sie also mit der Eingabe beginnen, ändert sich die Boxfarbe zu Blau, wie im Code angegeben:

    @Composable
    fun EditTextExample() {
        OutlinedTextField(
            value = "",
            onValueChange = {},
            label = { Text(stringResource(id = R.string.sample)) },
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 16.dp),
            colors = OutlinedTextFieldDefaults.colors(
                focusedBorderColor = Color.Blue,
                unfocusedBorderColor = Color.Black,
            )
        )
    }

    Importiere alle notwendigen Libraries und definiere R.string.sample mit einem beliebigen Wert.

  7. Wir haben auch einen anderen Typ von TextField, der nicht umrandet ist. Wenn Sie die Eingabeparameter von OutlinedTextField vergleichen, werden Sie feststellen, dass sie ziemlich ähnlich sind:

    @Composable
    fun NotOutlinedEditTextExample() {
        TextField(
            value = "",
            onValueChange = {},
            label = { Text(stringResource(id = R.string.sample)) },
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 16.dp),
            colors = OutlinedTextFieldDefaults.colors(
                focusedBorderColor = Color.Blue,
                unfocusedBorderColor = Color.Black,
            )
        )
    }
  8. Sie können die Anwendung ausführen, indem Sie die Compose-Funktionen innerhalb der @Preview-Compose-Funktion hinzufügen. In unserem Beispiel können wir UIElementPreview() in der gleichen Klasse erstellen, was eine Vorschau-Funktion ist, um unsere Benutzeroberfläche anzuzeigen. In der nächsten Abbildung ist die obere Ansicht ein OutlinedTextField, während die zweite ein normales TextField ist.

    @Preview(showBackground = true)
    @Composable
    fun UiElementPreview() {
        MyApplicationTheme {
            Column(Modifier.padding(start = 5.dp, end = 5.dp)) {
                EditTextExample()
                NotOutlinedEditTextExample()
                ButtonWithIcon()
            }
        }
    }

    Die Column sagt Compose, dass die Elemente in der Spalte angeordnet werden sollen. Wenn Sie die Anwendung ausführen, sollte das Ergebnis etwas Ähnliches wie in der nächsten Abbildung sein.

    android 2 2

  9. Jetzt schauen wir uns Beispiele für Schaltflächen an. Wir werden verschiedene Möglichkeiten betrachten, Schaltflächen mit unterschiedlichen Formen zu erstellen. Wenn Sie mit der Maus über der Button()-Compose-Funktion schweben, sehen Sie, welche Eingabe sie akzeptiert, wie in der nächsten Abbildung dargestellt.

    android 2 3

    In unserem zweiten Beispiel werden wir versuchen, eine Schaltfläche mit einem Symbol darauf zu erstellen. Darüber hinaus werden wir Text hinzufügen, was entscheidend ist, wenn Schaltflächen erstellt werden, da wir den Benutzern angeben müssen, welche Aktion die Schaltfläche ausführt oder was passieren wird, wenn sie darauf geklickt wird.

  10. Gehen Sie also voran und erstellen Sie eine Compose-Funktion in der gleichen Kotlin-Datei und nennen Sie sie ButtonWithIcon(). Importieren Sie dann die Button()-Compose-Funktion.

  11. Innerhalb dieser Funktion müssen Sie ein Icon() mit painterResource-Eingabe, einer contentDescription, einem Modifier und tint importieren. Wir benötigen auch Text(), der unserer Schaltfläche einen Namen gibt. In unserem Beispiel werden wir tint nicht verwenden:

    @Composable
    fun ButtonWithIcon() {
      Button(onClick = {}) {
        Icon(
          painterResource(id = R.drawable.shopping_cart_24px),
          contentDescription = stringResource(id = R.string.shop),
          modifier = Modifier.size(20.dp)
        )
        Text(text = stringResource(id = R.string.buy), Modifier.padding(start = 10.dp))
      }
    }

    Damit dieser Code einwandfrei funktioniert müssen sie das Icon zuerst herunterladen (https://fonts.google.com/icons?icon.platform=android, ein Icon suchen, auf den Tab 'Android' klicken und dann auf 'Download' klicken) und dann über den Resource-Manager im Android-Studio als drawable importieren (+ und Import drawables). Dort bekommt es auch automatisch einen Namen und einen Eintrag in den Resourcen, über den wir das Bild ansprechen können. In meinem Beispiel ist dies shopping_cart_24px, bei Ihnen kann der Name aber durchaus anders sein (kommt auf das gewählte Icon an). Alternativ können Sie sich so ein File hier herunterladen https://github.com/UnterrainerInformatik/lectures/blob/main/android/chapter1/shopping_cart-24px.xml. Danach noch das importierte XML-File anklicken und checken, ob alle Attribute richtig gesetzt sind (in meinem Fall war tint auf einen Farbwert gesetzt, den ich nicht im Resource-File hatte). Weiters müssen Sie noch die Strings R.string.shop und R.string.buy in den Resourcen anlegen (für Quick-Action mit Maus über den Fehler fahren, Create string value resource).

  12. Erstellen Sie eine neue Compose-Funktion und nennen Sie sie CornerCutShapeButton(). In diesem Beispiel werden wir versuchen, eine Schaltfläche mit abgeschnittenen Ecken zu erstellen:

    @Composable
    fun CornerCutShapeButton() {
      Button(onClick = {}, shape = CutCornerShape(10)) {
        Text(text = stringResource(id = R.string.cornerButton))
      }
    }
  13. Erstellen Sie eine neue Compose-Funktion und nennen Sie sie RoundCornerShapeButton(). In diesem Beispiel werden wir versuchen, eine Schaltfläche mit abgerundeten Ecken zu erstellen:

    @Composable
    fun RoundCornerShapeButton() {
      Button(onClick = {}, shape = RoundedCornerShape(10.dp)) {
        Text(text = stringResource(id = R.string.rounded))
      }
    }
  14. Erstellen Sie eine neue Compose-Funktion und nennen Sie sie ElevatedButtonExample(). In diesem Beispiel werden wir versuchen, eine Schaltfläche mit Erhebung zu erstellen:

    @Composable
    fun ElevatedButtonExample() {
      Button(
        onClick = {},
        elevation = ButtonDefaults.buttonElevation(
          defaultElevation = 8.dp,
          pressedElevation = 10.dp,
          disabledElevation = 0.dp
        )
      ) {
        Text(text = stringResource(id = R.string.elevated))
      }
    }
  15. Nachdem Sie die Anwendung gestartet haben, sollte ein Bild ähnlich wie in der nächsten Abbildung erscheinen. Die erste Schaltfläche nach dem TextField ist ButtonWithIcon(), die zweite ist CornerCutShapeButton(), die dritte ist RoundCornerShapeButton(), und schließlich haben wir ElevatedButtonExample().

    android 2 4

  16. Schauen wir uns nun ein letztes Beispiel an, da wir im Laufe des Buchs verschiedene Ansichten und Stile verwenden und dabei mehr lernen werden. Lassen Sie uns jetzt eine Bildansicht betrachten; die Image()-Compose-Funktion akzeptiert mehrere Eingaben, wie in der nächsten Abbildung dargestellt.

    android 2 5

  17. In unserem Beispiel wird die Image() nur einen painter haben, der nicht null sein kann, was bedeutet, dass Sie ein Bild für diese Compose-Funktion bereitstellen müssen, eine Inhaltsbeschreibung für die Barrierefreiheit und einen Modifier:

    @Composable
    fun ImageViewExample() {
      Image(
        painterResource(id = R.drawable.android_logo),
        contentDescription = stringResource(id = R.string.image),
        modifier = Modifier.size(200.dp)
      )
    }

    Das Bild gehört wieder über den Resource-Manager importiert (Download hier, falls Sie kein eigenes finden: https://github.com/UnterrainerInformatik/lectures/blob/main/android/chapter1/android-logo.png) und die contentDescription wieder als String gesetzt.

  18. Sie können auch versuchen, mit anderen Dingen zu experimentieren, wie zum Beispiel das Hinzufügen von RadioButton()- und CheckBox()-Elementen und deren Anpassung. Wenn Sie Ihre Anwendung ausführen, sollte das Ergebnis etwas Ähnliches wie in der nächsten Abbildung sein.

    android 2 6

    Ihre @Preview-Funktion sollte jetzt so aussehen:

    @Preview(showBackground = true)
    @Composable
    fun UiElementPreview() {
        MyApplicationTheme {
            Column(Modifier.padding(start = 5.dp, end = 5.dp)) {
                EditTextExample()
                NotOutlinedEditTextExample()
    
                ButtonWithIcon()
                CornerCutShapeButton()
                RoundCornerShapeButton()
                ElevatedButtonExample()
    
                ImageViewExample()
            }
        }
    }

1.3. Funktionsweise

Jede Compose-Funktion ist mit der @Composable-Annotation versehen. Diese Annotation teilt dem Compose-Compiler mit, dass der bereitgestellte Compiler dazu bestimmt ist, die bereitgestellten Daten in eine Benutzeroberfläche umzuwandeln. Es ist auch wichtig zu beachten, dass der Name jeder Compose-Funktion ein Nomen sein muss und kein Verb oder Adjektiv sein darf. Google stellt diese Richtlinien bereit. Jede von Ihnen erstellte Compose-Funktion kann Parameter akzeptieren, die es der App-Logik ermöglichen, Ihre Benutzeroberfläche zu beschreiben oder zu ändern.

Wir erwähnen den Compose-Compiler, was bedeutet, dass dieser Compiler irgendein spezielles Programm ist, das den von uns geschriebenen Code analysiert und ihn in etwas übersetzt, das der Computer verstehen kann – oder Maschinensprache.

In Icon() gibt painterResouce das Symbol an, das wir der Schaltfläche hinzufügen werden. contentDescription hilft bei der Barrierefreiheit, und der modifier wird verwendet, um unser Symbol zu dekorieren.

Wir können die erstellten UI-Elemente vorab anzeigen, indem wir die @Preview-Annotation hinzufügen und showBackground = true setzen:

@Preview(showBackground = true)

+ @Preview ist sehr mächtig und wir werden uns die richtige Verwendung in späteren Kapiteln genauer ansehen.

2. Scrollable List mit Jetpack Compose

android 2 7

2.1. Einleitung

Beim Erstellen von Android-Anwendungen sind wir uns alle einig, dass Sie wissen müssen, wie Sie eine RecyclerView erstellen, um Ihre Daten anzuzeigen. Mit unserer neuen, modernen Art, Android-Anwendungen zu erstellen, können wir LazyColumn verwenden, was sich ähnlich verhält.

In diesem Rezept werden wir uns Zeilen, Spalten und LazyColumn ansehen und eine scrollbare Liste mit unseren Dummy-Daten erstellen. Zusätzlich werden wir dabei auch etwas Kotlin lernen.

Wir werden das Projekt Compose Basics weiterhin verwenden, um eine scrollbare Liste zu erstellen. Daher müssen Sie die vorherige Anleitung abgeschlossen haben, um zu beginnen.

2.2. Programm

  1. Lassen Sie uns jetzt unsere erste scrollbare Liste erstellen. Zuerst brauchen wir jedoch Dummy-Daten, die in unserer Liste angezeigt werden sollen. Erstellen Sie daher ein Paket namens favoritecity, in dem unser scrollbares Beispiel leben wird.

  2. Innerhalb des Pakets favoritecity erstellen Sie eine neue Datenklasse und nennen Sie sie City; dies wird unsere Dummy-Datenquelle sein - data class City().

  3. Modellieren wir unsere City-Datenklasse. Stellen Sie sicher, dass Sie die erforderlichen Imports hinzufügen, sobald Sie die annotierten Werte hinzugefügt haben:

    data class City(
     val id: Int,
     @StringRes val nameResourceId: Int,
     @DrawableRes val imageResourceId: Int
    )
  4. Jetzt müssen wir in unseren Dummy-Daten eine Kotlin-Klasse erstellen und diese Klasse CityDataSource nennen. In dieser Klasse werden wir eine Funktion namens loadCities() erstellen, die unsere Liste von List<City> zurückgibt, die wir in unserer scrollbaren Liste anzeigen werden.

    class CityDataSource {
     fun loadCities(): List<City> {
     return listOf(
     City(1, R.string.spain, R.drawable.spain),
     City(2, R.string.new_york, R.drawable.newyork),
     City(3, R.string.tokyo, R.drawable.tokyo),
     City(4, R.string.switzerland, R.drawable.switzerland),
     City(5, R.string.singapore, R.drawable.singapore),
     City(6, R.string.paris, R.drawable.paris),
     )
     }
    }
  5. Jetzt haben wir unsere Dummy-Daten, und es ist Zeit, diese in unserer scrollbaren Liste anzuzeigen. Erstellen Sie eine neue Kotlin-Datei in unserem components-Paket und nennen Sie sie CityComponents. In CityComponents erstellen wir unsere @Preview-Funktion:

    @Preview(showBackground = true)
    @Composable
    private fun CityCardPreview() {
     CityApp()
    }
  6. Innerhalb unserer @Preview-Funktion haben wir eine weitere Compose-Funktion, CityApp(); innerhalb dieser Funktion rufen wir unsere CityList-Compose-Funktion auf, die die Liste als Parameter hat. In dieser Compose-Funktion rufen wir außerdem LazyColumn auf, und items wird CityCard(cities) sein. Weitere Erläuterungen zu LazyColumn und items finden Sie im Abschnitt "Funktionsweise":

    @Composable
    fun CityList(cityList: List<City>) {
     LazyColumn {
     items(cityList) { cities ->
     CityCard(cities)
     }
     }
    }
  7. Schließlich erstellen wir unsere CityCard(city)-Compose-Funktion:

    @Composable
    fun CityCard(city: City) {
     Card(modifier = Modifier.padding(10.dp),
     elevation = 4.dp) {
     Column {
     Image(
     painter = painterResource(city.imageResourceId),
     contentDescription = stringResource(city.nameResourceId),
     modifier = Modifier
     .fillMaxWidth()
     .height(154.dp),
     contentScale = ContentScale.Crop
     )
     Text(
     text = LocalContext.current.getString(city.nameResourceId),
     modifier = Modifier.padding(16.dp),
     style = MaterialTheme.typography.h5
     )
     }
     }
    }

    Wenn Sie die CityCardPreview-Komponierfunktion ausführen, sollte eine scrollbare Liste erstellt werden, wie in der nächsten Abbildung zu sehen ist. android 2 7

2.3. Funktionsweise

In Kotlin gibt es zwei Arten von Listen: unveränderliche (immutable) und veränderliche (mutable). Unveränderliche Listen sind Listen mit Elementen, die nicht geändert werden können (es werden Kopien zurückgegeben und nicht die eigentliche Liste), während veränderliche Listen Elemente enthalten, die in der Liste modifiziert werden können. Um eine Liste zu definieren, können wir sagen, dass eine Liste eine generische, geordnete Sammlung von Elementen ist, und diese Elemente können in Form von Ganzzahlen, Zeichenketten, Bildern usw. vorliegen, was größtenteils vom Typ der Daten abhängt, die unsere Listen enthalten sollen.

Zum Beispiel haben wir in unserem Beispiel eine Zeichenkette und ein Bild, die unsere Lieblingsstädte anhand ihres Namens und Bildes identifizieren.

In unserer City-Datenklasse verwenden wir @StringRes und @DrawableRes, um dies direkt aus den res-Ordnern für Drawable und String einfach abzurufen, und sie repräsentieren auch die ID für die Bilder und Zeichenketten.

Wir haben CityList erstellt und sie mit der composable-Funktion annotiert und die Liste der Stadtobjekte als Parameter in der Funktion deklariert. Eine scrollbare Liste in Jetpack Compose wird mit LazyColumn erstellt. Der Hauptunterschied zwischen LazyColumn und Column besteht darin, dass bei Verwendung von Column nur kleine Elemente angezeigt werden können, da Compose alle Elemente auf einmal lädt.

Zusätzlich kann eine Spalte nur feste komponierbare Funktionen enthalten, während LazyColumn, wie der Name schon sagt, den Inhalt bei Bedarf und auf Abruf lädt, was es gut für das Laden weiterer Elemente bei Bedarf macht. Außerdem verfügt LazyColumn über eine eingebaute Scrollfähigkeit, was die Arbeit für Entwickler erleichtert.

Wir haben auch eine komposable Funktion, CityCard, erstellt, in der wir das Card()-Element aus Compose importieren. Eine Karte enthält Inhalte und Aktionen zu einem einzelnen Objekt. In unserem Beispiel hat unsere Karte zum Beispiel ein Bild und den Namen der Stadt. Das Card()-Element in Compose hat die folgenden Eingaben in seinen Parametern:

@Composable
fun Card(
 modifier: Modifier = Modifier,
 shape: Shape = MaterialTheme.shapes.medium,
 backgroundColor: Color = MaterialTheme.colors.surface,
 contentColor: Color = contentColorFor(backgroundColor),
 border: BorderStroke? = null,
 elevation: Dp = 1.dp,
 content: @Composable () -> Unit
)

Das bedeutet, dass Sie Ihre Karte leicht nach Ihren Wünschen modellieren können. Unsere Karte hat zum Beispiel Padding und Elevation, und der Bereich enthält eine Spalte. In dieser Spalte haben wir ein Bild und Text, der dazu dient, das Bild näher zu beschreiben und mehr Kontext zu bieten.

3. Tab-Layout mit View Pager

3.1. Einleitung

In Android ist es Usus zwischen verschiedenen Inhalten zu 'sliden'. Dies trifft zum Beispiel bei Carousels oder bei Tabs zu. Hier werden wir einen Pager bauen, der zwischen einzelnen Seiten horizontal hin- und herwechseln kann. Er hat einen State, der die Seite bestimmt, die dann angezeigt wird. Die Seite wird mittels einer Farbe symbolisiert. Sie können im vorherigen Programm weiter machen.

3.2. Programm

  1. Fügen sie folgende Zeilen zur build.gradle(Module:app)

implementation("com.google.accompanist:accompanist-pager:0.32.0")
    implementation("com.google.accompanist:accompanist-pager-indicators:0.32.0")

Accompanist ist von JetPack und bietet einige nützliche Komponenten, die wir verwenden können.

  1. Im gleichen Projekt

3.3. Funktionsweise

4. Animationen

4.1. Einleitung

4.2. Programm

4.3. Funktionsweise

5. Accessibility

5.1. Einleitung

5.2. Programm

5.3. Funktionsweise

6. Declarative Graphics

6.1. Einleitung

6.2. Programm

6.3. Funktionsweise

7. Grundlagen Android-Entwicklung

7.1. Einleitung

7.2. Programm

7.3. Funktionsweise