Rheinwerk Computing < openbook >

 
Inhaltsverzeichnis
Vorwort
Teil I Grundlagen
1 Android – eine offene, mobile Plattform
2 Hallo Android!
3 Von der Idee zur Veröffentlichung
Teil II Elementare Anwendungsbausteine
4 Wichtige Grundbausteine von Apps
5 Benutzeroberflächen
6 Multitasking
Teil III Gerätefunktionen nutzen
7 Telefonieren und surfen
8 Sensoren, GPS und Bluetooth
Teil IV Dateien und Datenbanken
9 Dateien lesen, schreiben und drucken
10 Datenbanken
Teil V Multimedia und Produktivität
11 Multimedia
12 Kontakte und Organizer
A Einführung in Kotlin
B Jetpack Compose
C Häufig benötigte Codebausteine
D Literaturverzeichnis
E Die Begleitmaterialien
Stichwortverzeichnis

Ihre Meinung?
Spacer
<< zurück
Android 11 von Thomas Künneth
Das Praxisbuch für App-Entwickler
Buch: Android 11

Android 11
Pfeil 2 Hallo Android!
Pfeil 2.1 Android-Projekte
Pfeil 2.1.1 Projekte anlegen
Pfeil 2.1.2 Projektstruktur
Pfeil 2.1.3 Bibliotheken
Pfeil 2.2 Benutzeroberfläche
Pfeil 2.2.1 Grafiken
Pfeil 2.2.2 Texte
Pfeil 2.2.3 Views
Pfeil 2.2.4 Oberflächenbeschreibungen
Pfeil 2.3 Programmlogik und -ablauf
Pfeil 2.3.1 Activities
Pfeil 2.3.2 Benutzereingaben
Pfeil 2.3.3 Der letzte Schliff
Pfeil 2.4 Zusammenfassung
 
Zum Seitenanfang

2.3    Programmlogik und -ablauf Zur vorigen ÜberschriftZur nächsten Überschrift

Viele Desktop-Anwendungen sind datei- oder dokumentenzentriert. Egal, ob Textverarbeitung, Tabellenkalkulation oder Layoutprogramm – ihr Aufbau ist stets gleich. Den überwiegenden Teil des Bildschirms oder Fensters belegt ein Arbeitsbereich, der ein Dokument oder einen Teil davon darstellt. Um diesen Bereich herum gruppieren sich Symbolleisten und Paletten, mit deren Werkzeugen die Elemente des Dokuments bearbeitet werden. Das gleichzeitige Darstellen von Werkzeugen und Inhalt ist auf den im Vergleich zu PC- oder Laptop-Monitoren kleinen Bildschirmen mobiler Geräte nur bedingt sinnvoll. Der Benutzer würde kaum etwas erkennen. Als Entwickler sollten Sie Ihre App deshalb in Funktionsblöcke oder Bereiche unterteilen, die genau einen Aspekt Ihres Programms abbilden.

Ein anderes Beispiel: E-Mail-Clients zeigen die wichtigsten Informationen zu eingegangenen Nachrichten häufig in einer Liste an. Neben oder unter der Liste befindet sich ein Lesebereich, der das aktuell ausgewählte Element vollständig anzeigt. Auch dies lässt sich aufgrund des geringe(re)n Platzes auf Smartphones nicht sinnvoll realisieren. Stattdessen zeigen entsprechende Anwendungen dem Nutzer zunächst eine Übersicht, nämlich die Liste der eingegangenen Nachrichten, und verzweigen erst in eine Detailansicht, wenn eine Zeile der Liste angeklickt wird.

Tablet-Bildschirme bieten im Vergleich zu einem Smartphone deutlich mehr Platz für Informationen. Um Benutzeroberflächen für beide Welten entwickeln zu können, hat Google mit Android 3 sogenannte Fragmente eingeführt. Bevor ich Ihnen im nächsten Kapitel zeige, wie Sie damit Benutzeroberflächen für unterschiedliche Bildschirmgrößen anbieten, wollen wir uns den wahrscheinlich wichtigsten Anwendungsbaustein ansehen.

 
Zum Seitenanfang

2.3.1    Activities Zur vorigen ÜberschriftZur nächsten Überschrift

Unter Android ist das Zerlegen einer App in aufgabenorientierte Teile bzw. Funktionsblöcke ein grundlegendes Architekturmuster. Die gerade eben skizzierten Aufgaben bzw. »Aktivitäten« E-Mail auswählen und E-Mail anzeigen werden dann zu Bausteinen, die die Plattform Activities nennt. Eine Anwendung besteht aus mindestens einer solchen Activity, je nach Funktionsumfang können es aber auch viele mehr sein. Normalerweise ist jeder Activity eine Benutzeroberfläche, also ein Baum bestehend aus Views und ViewGroups, zugeordnet.

Activities bilden demnach die vom Anwender wahrgenommenen Bereiche einer App. Sie können sich gegenseitig aufrufen. Die Vorwärtsnavigation innerhalb einer Anwendung wird auf diese Weise realisiert. Da das System Activities auf einem Stapel ablegt, müssen Sie sich als Entwickler nicht darum kümmern, von wem Ihre Activity aufgerufen wird. Drückt der Benutzer die reale oder eine virtuelle Zurück-Schaltfläche (oder führt die korrespondierende Wischgeste aus), wird automatisch die zuvor angezeigte Activity reaktiviert. Vielleicht fragen Sie sich, aus wie vielen Activities Hallo Android besteht. Theoretisch könnten Sie die App in drei Activities unterteilen, die Sie unabhängig voneinander anlegen müssten:

  1. Begrüßung anzeigen

  2. Namen eingeben

  3. personalisierten Gruß anzeigen

Das wäre sinnvoll, wenn die entsprechenden Aufgaben umfangreiche Benutzereingaben oder aufwendige Netzwerkkommunikation erforderten. Dies ist hier nicht der Fall. Da die gesamte Anwendung aus sehr wenigen Bedienelementen besteht, ist es hier zielführender, alle Funktionen in einer Activity abzubilden. Bitte übernehmen Sie die Klasse MainActivity aus der im Folgenden dargestellten ersten Version.

In der Methode onCreate() wird mit setContentView() die Benutzeroberfläche geladen und angezeigt. Danach werden durch den Aufruf der Methode findViewById() zwei Referenzen auf Bedienelemente ermittelt und den Variablen message und nextFinish zugewiesen. setText() setzt die Beschriftung der Schaltfläche sowie des Textfeldes. Hierzu erfahren Sie gleich mehr. Bitte achten Sie darauf, in Ihren Apps findViewById() erst nach setContentView() aufzurufen. Andernfalls drohen Abstürze.

[»]  Hinweis

Etwas später zeige ich Ihnen, wie Sie auf Views zugreifen können, ohne Referenzen auf diese in Instanzvariablen zu halten. Allerdings ist dazu Magie nötig. Da ich glaube, dass für ein solides Verständnis der Plattform die Kenntnis möglichst vieler Zusammenhänge wichtig ist, sehen Sie zunächst den klassischen Weg, wie er seit der ersten Android-Version verwendet wird.

package com.thomaskuenneth.androidbuch.halloandroid

import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

private lateinit var message: TextView
private lateinit var nextFinish: Button

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

message = findViewById(R.id.message)
nextFinish = findViewById(R.id.next_finish)

message.setText(R.string.welcome)
nextFinish.setText(R.string.next)
}
}

Listing 2.6    Erste Version der Klasse »MainActivity«

Toolbar-Symbole zum Starten von Apps

Abbildung 2.10    Toolbar-Symbole zum Starten von Apps

Um die Anwendung zu starten, wählen Sie wie in Abbildung 2.10 dargestellt das gewünschte echte oder virtuelle Gerät aus und klicken danach auf den grünen Play-Button.

Die erste Version von »Hallo Android«

Abbildung 2.11    Die erste Version von »Hallo Android«

Nach der Installation sollte das Emulator-Fenster bzw. der Bildschirm des echten Geräts in etwa Abbildung 2.11 entsprechen. »In etwa«, weil es ein paar Faktoren gibt, die die Darstellung einer App beeinflussen, zum Beispiel:

  • die Plattformversion des Emulators bzw. echten Geräts

  • der API-Level in der Datei build.gradle

  • Bildschirmeinstellungen und Schriftgröße im (simulierten) Gerät

Lassen Sie uns zunächst weiter auf Ihre erste eigene App konzentrieren. Das Textfeld nimmt zwar Eingaben entgegen, das Anklicken der Schaltfläche Weiter löst aber selbstverständlich noch keine Aktion aus. Diese Aktion werden wir im nächsten Abschnitt implementieren. Zuvor möchte ich Sie aber mit einigen Schlüsselstellen des Quelltextes vertraut machen. Ganz wichtig: Jede Activity erbt von der Klasse android.app.Activity oder von spezialisierten Kindklassen. Mein Beispiel verwendet androidx.appcompat.app.AppCompatActivity. Sie stellt eine Reihe von Funktionen zur Verfügung, die dem Original fehlen. Wir werden im weiteren Verlauf des Buches noch ausführlich darauf zu sprechen kommen.

Haben Sie bemerkt, dass die gesamte Programmlogik in der Methode onCreate() liegt? Activities haben einen ausgeklügelten Lebenszyklus, den ich Ihnen in Kapitel 4, »Wichtige Grundbausteine von Apps«, ausführlicher vorstelle. Seine einzelnen Stationen werden durch bestimmte Methoden der Klasse Activity realisiert, die Sie bei Bedarf überschreiben können. Beispielsweise informiert die Plattform eine Activity, kurz bevor sie beendet, unterbrochen oder zerstört wird. Die Methode onCreate() wird immer überschrieben. Sie ist der ideale Ort, um die Benutzeroberfläche aufzubauen und Variablen zu initialisieren. Ganz wichtig ist, mit super.onCreate() die Implementierung der Elternklasse aufzurufen. Sonst wird zur Laufzeit die Ausnahme SuperNotCalledException ausgelöst. Das Laden und Anzeigen der Bedienelemente reduziert sich auf eine Zeile Quelltext:

setContentView(R.layout.activity_main)

Sie sorgt dafür, dass alle Views und ViewGroups, die in der Datei activity_main.xml definiert wurden, zu einem Objektbaum entfaltet werden und dieser als Inhaltsbereich der Activity gesetzt wird. Warum ich den Begriff »entfalten« verwende, erkläre ich Ihnen in Kapitel 5, »Benutzeroberflächen«.

Möglicherweise fragen Sie sich, woher die Klasse R stammt. Sie wird von den Build Tools automatisch generiert und auf dem aktuellen Stand gehalten. Ihr Zweck ist es, Elemente aus Layout- und anderen XML-Dateien im Java- oder Kotlin-Quelltext verfügbar zu machen. R.layout.activity_main referenziert also die XML-Datei mit Namen activity_main.

Der Inhalt des Textfeldes message und die Beschriftung der Schaltfläche nextFinish werden auf sehr ähnliche Weise festgelegt: Zunächst ermitteln wir durch Aufruf der Methode findViewById() eine Referenz auf das gewünschte Objekt. R.id.message und R.id.next_finish verweisen hierbei auf Elemente, die wir ebenfalls in activity_main.xml definiert haben. Sehen Sie sich zur Verdeutlichung folgendes Dateifragment an:

<TextView
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
...
<Button
android:id="@+id/next_finish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end" />

Listing 2.7    Auszug aus der Datei »activity_main.xml«

Durch den Ausdruck android:id="@+id/xyz" entsteht ein Bezeichner, auf den Sie mit R.id.xyz zugreifen können. xyz ist der Name des Bezeichners. @+id/ definiert ihn. Dies funktioniert nicht nur in Layoutdateien, sondern auch für die Definition von Texten, die in der Datei strings.xml abgelegt werden. Auch hierzu ein kurzer Auszug:

<!-- Beschriftungen für Schaltflächen -->
<string name="next">Weiter</string>
<string name="finish">Fertig</string>

Listing 2.8    Auszug aus der Datei »strings.xml«

Die Anweisung nextFinish.setText(R.string.next) legt den Text der einzigen Schaltfläche unserer App fest.

 
Zum Seitenanfang

2.3.2    Benutzereingaben Zur vorigen ÜberschriftZur nächsten Überschrift

Um Hallo Android zu komplettieren, müssen wir auf das Anklicken der Schaltfläche nextFinish reagieren. Beim ersten Mal wird das Textfeld input ausgelesen und als persönlicher Gruß in message eingetragen. Anschließend wird das Textfeld ausgeblendet und die Beschriftung der Schaltfläche geändert. Wird diese ein zweites Mal angeklickt, beendet sich die App. Im Folgenden sehen Sie die entsprechend erweiterte Fassung der Klasse MainActivity:

package com.thomaskuenneth.androidbuch.halloandroid

import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

private lateinit var message: TextView
private lateinit var nextFinish: Button
private lateinit var input: EditText

private var firstClick = true

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

message = findViewById(R.id.message)
nextFinish = findViewById(R.id.next_finish)
input = findViewById(R.id.input)

message.setText(R.string.welcome)
nextFinish.setText(R.string.next)
nextFinish.setOnClickListener(fun(_: View) {
if (firstClick) {
message.text = getString(
R.string.hello,
input.text
)
input.visibility = View.INVISIBLE
nextFinish.setText(R.string.finish)
firstClick = false
} else {
finish()
}
})
}
}

Listing 2.9    Zweite Fassung der Klasse »MainActivity«

Um auf das Anklicken der Schaltfläche reagieren zu können, wird ein sogenannter OnClickListener registriert. Dieses Interface besteht aus der Methode onClick(). Ihr wird nur ein Wert übergeben, nämlich die angeklickte View. Wenn Sie in Ihrem Code nicht weiter damit arbeiten, bietet es sich an, den Unterstrich _ anstelle eines Variablennamens zu verwenden. Die hier vorgestellte Implementierung unter Verwendung einer anonymen Funktion nutzt die Boolean-Variable firstClick, um die durchzuführenden Aktionen zu bestimmen. input.visibility = View.INVISIBLE blendet das Eingabefeld aus. getString(R.string.hello, input.getText()) liefert den in strings.xml definierten persönlichen Gruß und fügt an der Stelle %1$s den vom Benutzer eingetippten Namen ein. Um die App zu beenden, wird die Methode finish() der Klasse Activity aufgerufen.

[»]  Hinweis

Kotlin bietet vielfach eine schlankere Syntax als Java. Beispielsweise ist es oft möglich, Klammern wegzulassen und Lambda-Ausdrücke unmittelbar anzuschließen. Im weiteren Verlauf des Buches werden Sie mehr und mehr diese kompakten Formen sehen. Für den Einstieg halte ich die konservative Darstellung aber für zielführender.

 
Zum Seitenanfang

2.3.3    Der letzte Schliff Zur vorigen ÜberschriftZur nächsten Überschrift

In diesem Abschnitt möchte ich Ihnen zeigen, wie Sie Hallo Android den letzten Schliff geben. Zum Beispiel kann das System in leeren Eingabefeldern einen Hinweis anzeigen, was der Benutzer eingeben soll. Hierzu fügen Sie in der Datei strings.xml die folgende Zeile ein:

<string name="firstname_surname">Vorname Nachname</string>

Anschließend erweitern Sie in activity_main.xml das Element <EditText> um das Attribut android:hint="@string/firstname_surname". Damit verschwindet übrigens auch die Warnung, die Sie etwas weiter vorne gesehen haben. Starten Sie die App, um sich das Ergebnis anzusehen. Abbildung 2.12 zeigt das entsprechend abgeänderte Programm.

Schon besser, aber noch nicht perfekt. Drücken Sie während der Eingabe eines Namens nämlich auf (¢), wandert der Cursor in die nächste Zeile, und auch die Höhe des Eingabefeldes nimmt zu. Dieses Verhalten lässt sich zum Glück leicht korrigieren. Erweitern Sie hierzu <EditText> um die folgenden vier Attribute:

android:lines="1"
android:inputType="textCapWords"
android:autofillHints="personName"
android:imeOptions="actionNext"
Leeres Eingabefeld mit Hinweis

Abbildung 2.12    Leeres Eingabefeld mit Hinweis

Damit begrenzen wir die Eingabe auf eine Zeile, und der erste Buchstabe eines Wortes wird automatisch in einen Großbuchstaben umgewandelt. Ferner teilen wir dem Autofill framework von Android mit, dass hier Personennamen (Vorname und Nachname) eingegeben werden. Schließlich löst das Drücken von (¢) bzw. das Anklicken des korrespondierenden Symbols auf der virtuellen Gerätetastatur eine Aktion aus. Um auf diese reagieren zu können, müssen wir in der Activity ebenfalls eine Kleinigkeit hinzufügen. Unter die Zeile input = findViewById(R.id.input) gehören die folgenden Zeilen:

input.setOnEditorActionListener(fun(_, _, _): Boolean {
if (nextFinish.isEnabled) {
nextFinish.performClick()
}
return true
})

Listing 2.10    Einen Button im Code auslösen

Das Interface TextView.OnEditorActionListener definiert eine Methode, onEditorAction(). Sie erhält als Argumente die betroffene TextView, eine ID, die die ausgelöste Aktion repräsentiert, sowie ein KeyEvent oder null. Der Aufruf der Methode performClick() simuliert das Antippen der Schaltfläche Weiter. Dadurch wird der Code ausgeführt, den wir in der Methode onClick() der Klasse OnClickListener implementiert haben. Alternativ hätten wir diesen Code auch in eine eigene Methode auslagern und diese an beiden Stellen aufrufen können. Aber Sie wissen nun, wie Sie das Antippen einer Komponente simulieren können. Übrigens prüft isEnabled, ob die Schaltfläche aktiv oder inaktiv ist. Das werden wir gleich noch brauchen.

Schließlich wollen wir noch dafür sorgen, dass die Bedienelemente nicht mehr an den Rändern der Anzeige kleben. Eine kurze Anweisung schiebt zwischen ihnen und dem Rand einen kleinen leeren Bereich ein. Fügen Sie dem XML-Tag <LinearLayout> einfach das Attribut android:padding="10dp" hinzu. Padding wirkt nach innen. Das LinearLayout ist eine Komponente, die weitere Elemente enthält. Diese werden in horizontaler oder vertikaler Richtung angeordnet. Mit android:padding legen Sie fest, wie nahe die Schaltfläche, das Textfeld und die Eingabezeile der oberen, unteren, linken und rechten Begrenzung kommen können.

Im Gegensatz dazu wirkt Margin nach außen. Hiermit können Sie einen Bereich um die Begrenzung einer Komponente herum definieren. Auch hierzu ein Beispiel: Fügen Sie dem XML-Tag <Button> das Attribut android:layout_marginTop="16dp" hinzu, wird die Schaltfläche deutlich nach unten abgesetzt. Sie haben einen oberen Rand definiert, der gegen die untere Begrenzung der Eingabezeile wirkt. Werte, die auf dp enden, geben übrigens geräteunabhängige Pixelgrößen an. Sie beziehen die Auflösung der Anzeige eines Geräts mit ein.

[+]  Tipp

Wenn nach dem Einfügen von Kotlin-Codeschnipseln Teile des Quelltextes mit roten Schlangenlinien unterkringelt werden, liegt das sehr häufig an fehlenden import-Anweisungen. Um diese einzufügen, klicken Sie die angemeckerte Klasse an und drücken danach (Alt)+(¢). Wiederholen Sie dies für alle nicht erkannten Klassen.

Fällt Ihnen noch ein Defizit der gegenwärtigen Version auf? Solange der Benutzer keinen Namen eingetippt hat, sollte die Schaltfläche Weiter nicht anwählbar sein. Das lässt sich mithilfe eines sogenannten TextWatchers leicht realisieren. Dazu fügen Sie in der Methode onCreate() vor dem Ende des Methodenrumpfes, also vor ihrer schließenden geschweiften Klammer, das folgende Quelltextfragment ein:

input.doAfterTextChanged {
nextFinish.isEnabled = it?.isNotEmpty() ?: false
}
nextFinish.isEnabled = false

Listing 2.11    Auf Texteingaben reagieren

Jedes Mal, wenn ein Zeichen eingegeben oder gelöscht wird, ruft Android unsere Implementierung von doAfterTextChanged auf. Diese ist sehr einfach gehalten: Nur wenn der Name mindestens ein Zeichen lang ist, kann die Schaltfläche Weiter angeklickt werden. doAfterTextChanged ist eine Erweiterungsfunktion für android.widget.TextView. Sie gehört zu der Jetpack-Komponente Android KTX. Sie wird der modulspezifischen build.gradle-Datei in dependencies { ... } hinzugefügt:

implementation 'androidx.core:core-ktx:1.3.1'

Ferner müssen Sie in android { ... } die Zeile

kotlinOptions { jvmTarget = "1.8" }

eintragen. Als kleine Übung können Sie versuchen, die Prüfroutine so zu erweitern, dass Vor- und Nachname vorhanden sein müssen. Prüfen Sie der Einfachheit halber, ob der eingegebene Text ein Leerzeichen enthält, das nicht am Anfang und nicht am Ende steht.

Die fertige App »Hallo Android«

Abbildung 2.13    Die fertige App »Hallo Android«

Damit haben Sie Ihre erste eigene Anwendung fast fertiggestellt. Es gibt nur noch eine kleine Unvollkommenheit: Die Schaltfläche Fertig befindet sich gegenüber der Schaltfläche Weiter etwas näher am oberen Bildschirmrand. Der Grund dafür ist, dass die Grußfloskel meistens in eine Zeile passt, der Begrüßungstext aber zwei Zeilen benötigt. Beheben Sie dieses Malheur, indem Sie in der Layoutdatei innerhalb des <TextView />-Tags, zum Beispiel unterhalb von android:id="@+id/message", die Zeile android:lines="2" einfügen. Abbildung 2.13 zeigt die fertige App Hallo Android.

 


Ihre Meinung?

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de

<< zurück
Zur Rheinwerk-Konferenz für Kotlin
 Buchempfehlungen
Zum Rheinwerk-Shop: Kotlin

Kotlin


Zum Rheinwerk-Shop: Praxisbuch Usability und UX

Praxisbuch Usability und UX


Zum Rheinwerk-Shop: Flutter und Dart

Flutter und Dart


Zum Rheinwerk-Shop: App-Design

App-Design


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und in die Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2023
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 
[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern