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 3 Von der Idee zur Veröffentlichung
Pfeil 3.1 Konzept und Realisierung
Pfeil 3.1.1 Konzeption
Pfeil 3.1.2 Fachlogik
Pfeil 3.1.3 Benutzeroberfläche
Pfeil 3.2 Vom Programm zum Produkt
Pfeil 3.2.1 Protokollierung
Pfeil 3.2.2 Fehler suchen und finden
Pfeil 3.2.3 Debuggen auf echter Hardware
Pfeil 3.3 Anwendungen verteilen
Pfeil 3.3.1 Die App vorbereiten
Pfeil 3.3.2 Apps in Google Play einstellen
Pfeil 3.3.3 Alternative Märkte und Ad-hoc-Verteilung
Pfeil 3.4 Zusammenfassung
 
Zum Seitenanfang

3    Von der Idee zur Veröffentlichung Zur vorigen ÜberschriftZur nächsten Überschrift

Sie haben eine tolle Idee für eine App und würden am liebsten gleich loslegen? Ein bisschen Planung erleichtert nicht nur die Implementierung, sondern sorgt auch für zufriedene Nutzer. Warum das so ist, zeige ich Ihnen in diesem Kapitel.

Sie kennen das sicher: Sie haben eine Idee und möchten diese am liebsten sofort in die Tat umsetzen. Die Entwicklungsumgebung ist schnell gestartet. Erste Ergebnisse lassen sich unter Android in kurzer Zeit erzielen, wie das Beispiel im vorherigen Kapitel zeigt. Zunächst klappt das Erweitern eines so begonnenen Projekts noch recht gut. Irgendwann werden Sie aber feststellen, dass die Struktur der Anwendung nicht mehr so recht nachvollziehbar ist. Dann wird es auch zunehmend schwer, Änderungen durchzuführen und neue Funktionen einzubauen. In ein so aus dem Ruder gelaufenes Programm schleichen sich auch mehr und mehr Fehler ein. Und mancher Bug lässt sich nicht mehr entfernen, ohne die gesamte Konstruktion ins Wanken zu bringen.

Natürlich möchte ich Ihnen mit diesem Schreckensszenario nicht die Lust am Programmieren nehmen. Im Gegenteil, ganz gleich, ob Sie eine App nur für sich entwickeln oder in Google Play anbieten wollen – die Beschäftigung mit Googles mobiler Plattform soll Spaß machen. Deshalb ist es wichtig, die Stationen im Entstehungsprozess einer Anwendung zu kennen. Wenn Sie alle Schritte abgearbeitet und gedanklich mit einem Häkchen versehen haben, können Sie sicher sein, nichts Wesentliches vergessen zu haben. Vor allem aber lässt sich Ihr Programm so auch in Zukunft problemlos erweitern.

 
Zum Seitenanfang

3.1    Konzept und Realisierung Zur vorigen ÜberschriftZur nächsten Überschrift

Bevor Sie mit der Implementierung einer App beginnen, sollten Sie sich darüber im Klaren sein, was das Programm leisten soll und aus welchen Bausteinen oder Funktionsbereichen es bestehen wird. Widerstehen Sie der Versuchung, sich mit einer vagen Idee zufriedenzugeben. Sonst kann es leicht passieren, dass Sie Dinge implementieren, die schon im System vorhanden sind.

Wie Sie bereits wissen, bestehen Android-Anwendungen, zumindest aus der Sicht des Benutzers, aus Abfolgen von Aktivitäten, zum Beispiel SMS eingeben, Kontakt auswählen, Foto aufnehmen oder Wähltastatur zeigen. In der Konzeptionsphase legen Sie unter anderem fest, welche Aktivitäten Sie programmieren müssen und welche der bereits vorhandenen Sie gegebenenfalls einbinden können.

 
Zum Seitenanfang

3.1.1    Konzeption Zur vorigen ÜberschriftZur nächsten Überschrift

Für eine App ein Konzept zu schreiben, bedeutet keineswegs zwangsläufig, umfangreiche Dokumente zu erstellen. Beginnen Sie damit, den Zweck des Programms in wenigen Sätzen zu beschreiben. Wählen Sie die Formulierung so, dass jemand, der in Google Play zufällig über die Anwendung stolpert, Lust bekommt, sie herunterzuladen und auszuprobieren. Das könnte folgendermaßen aussehen:

Zeigt eine nach Kalendermonaten sortierte Liste der zwölf Sternzeichen an. Das Antippen eines Tierkreiszeichens führt zu dem entsprechenden Eintrag in der Wikipedia.

Diese beiden Sätze, ergänzt durch zwei Screenshots, genügen dem Google-Play-Besucher, um zu entscheiden, ob er die App herunterladen möchte oder nicht. Wer kein Interesse an Astrologie hat, wird dies wahrscheinlich nicht tun. Auch für Sie als Entwickler sind die zwei Sätze ausreichend, denn sie beschreiben den vollständigen Funktionsumfang des Programms. Als Nächstes leiten Sie aus dieser Beschreibung seine groben Funktionsblöcke ab. Diese sind:

  1. Leere Liste bereitstellen

  2. Liste mit Inhalt füllen

  3. Webbrowser aufrufen

Da Listenansichten eine Kernfunktionalität der Android-Plattform sind, müssen Sie sich während der Konzeptphase nicht weiter um den ersten Punkt kümmern. Er wird erst im Verlauf der Implementierung interessant. Das Gleiche gilt für das Öffnen des Browsers. Der Inhalt der Liste hingegen repräsentiert die »Fachlichkeit« der App. Sie müssen deshalb die folgenden zwei Fragen beantworten:

  1. Was soll dargestellt werden?

  2. Wie kommt es zustande?

An dieser Stelle ist es verführerisch, in Benutzeroberflächen zu denken, diese sind aber erst später an der Reihe. Dass ein Listenelement also vielleicht das Symbol eines Sternzeichens enthält und dessen Namen ausgibt, spielt zunächst noch keine Rolle. Die Liste enthält alle existierenden Tierkreiszeichen, also genau zwölf. Diese sind nach Kalendermonaten in aufsteigender Reihenfolge sortiert. Tierkreiszeichen decken einen Datumsbereich ab, sie haben demnach ein Start- und ein Enddatum. Das aktuelle Sternzeichen lässt sich ermitteln, indem man prüft, in welchem Bereich das aktuelle Datum liegt.

 
Zum Seitenanfang

3.1.2    Fachlogik Zur vorigen ÜberschriftZur nächsten Überschrift

Bitte öffnen Sie mein Beispielprojekt Tierkreiszeichen (die App ist in Abbildung 3.1 zu sehen) in Android Studio, und klicken Sie in der Menüleiste auf BuildMake Project. Wie Sie bereits wissen, können Sie mit R.string auf die Einträge der Datei strings.xml zugreifen.

Die App »Tierkreiszeichen«

Abbildung 3.1    Die App »Tierkreiszeichen«

Auf ganz ähnliche Weise referenzieren Sie Grafiken. So entspricht z. B. R.drawable.leo der Datei res\drawable\leo.png. Wir werden uns beides zunutze machen, um den Tierkreis zusammenzusetzen.

Bitte sehen Sie sich hierzu die folgende Klasse an:

package com.thomaskuenneth.androidbuch.tierkreiszeichen

import android.content.Context

class Tierkreiszeichen(
val tag: Int, val monat: Int, private val tierkreiszeichen: Int
) {

fun getName(context: Context): String {
return context.getString(tierkreiszeichen)
}

val idForDrawable: Int
get() = when (tierkreiszeichen) {
R.string.aquarius -> R.drawable.aquarius
R.string.aries -> R.drawable.aries
R.string.cancer -> R.drawable.cancer
R.string.capricornus -> R.drawable.capricornus
R.string.gemini -> R.drawable.gemini
R.string.leo -> R.drawable.leo
R.string.libra -> R.drawable.libra
R.string.pisces -> R.drawable.pisces
R.string.sagittarius -> R.drawable.sagittarius
R.string.scorpius -> R.drawable.scorpius
R.string.taurus -> R.drawable.taurus
R.string.virgo -> R.drawable.virgo
else -> R.mipmap.ic_launcher
}
}

Listing 3.1    Die Klasse »Tierkreiszeichen«

Zur Laufzeit der App existiert für jedes Tierkreiszeichen eine Instanz dieser Klasse. Die Instanzen werden in der Datei Zodiak.kt, die ich Ihnen gleich vorstelle, erzeugt. Jedes Tierkreiszeichen speichert den Tag, an dem es beginnt, sowie eine eindeutige Zahl, die das Sternzeichen repräsentiert. Hierfür verwende ich Konstanten aus R.string. Bitte sehen Sie sich die Datei strings.xml an, sie weist jedem Tierkreiszeichen einen eindeutigen Namen zu:

<string name="aries">Widder</string>
<string name="taurus">Stier</string>
<string name="gemini">Zwillinge</string>
<string name="cancer">Krebs</string>
<string name="leo">Löwe</string>
<string name="virgo">Jungfrau</string>
<string name="libra">Waage</string>
<string name="scorpius">Skorpion</string>
<string name="sagittarius">Schütze</string>
<string name="capricornus">Steinbock</string>
<string name="aquarius">Wassermann</string>
<string name="pisces">Fische</string>

Listing 3.2    Auszug aus »strings.xml«

Die Methode getName() der Klasse Tierkreiszeichen liefert den Namen eines Sternzeichens im Klartext. Aus dem eindeutigen numerischen Wert, der durch Auslesen der Variablen tierkreiszeichen ermittelt wird, lässt sich mit der Methode getString() der in strings.xml eingetragene zugehörige Text auslesen. Auf sehr ähnliche Weise funktioniert die Eigenschaft idForDrawable: Ihr Getter liefert für jedes Sternzeichen eine Referenz auf eine .png-Datei, indem in einem when-Block der jeweils passende R.drawable-Wert ermittelt und zurückgeliefert wird. Ein Tierkreiszeichen speichert nur den Tag und den Monat, an dem es beginnt. Vorgänger- und Nachfolgerbeziehungen werden in der Datei Zodiak.kt abgebildet. Sie ist in Listing 3.3 zu sehen:

package com.thomaskuenneth.androidbuch.tierkreiszeichen

import android.util.SparseArray
import java.util.*

private val tierkreis = createSparseArray()

private fun createSparseArray(): SparseArray<Tierkreiszeichen> {
val a = SparseArray<Tierkreiszeichen>()
a.put(Calendar.JANUARY, Tierkreiszeichen(
21, Calendar.JANUARY, R.string.aquarius)
)
a.put(Calendar.FEBRUARY, Tierkreiszeichen(
20, Calendar.FEBRUARY, R.string.pisces)
)
a.put(Calendar.MARCH, Tierkreiszeichen(
21, Calendar.MARCH, R.string.aries)
)
a.put(Calendar.APRIL, Tierkreiszeichen(
21, Calendar.APRIL, R.string.taurus)
)
a.put(Calendar.MAY, Tierkreiszeichen(
22, Calendar.MAY, R.string.gemini)
)
a.put(Calendar.JUNE, Tierkreiszeichen(
22, Calendar.JUNE, R.string.cancer)
)
a.put(Calendar.JULY, Tierkreiszeichen(
24, Calendar.JULY, R.string.leo)
)
a.put(Calendar.AUGUST, Tierkreiszeichen(
24, Calendar.AUGUST, R.string.virgo)
)
a.put(Calendar.SEPTEMBER, Tierkreiszeichen(
24, Calendar.SEPTEMBER, R.string.libra)
)
a.put(Calendar.OCTOBER, Tierkreiszeichen(
24, Calendar.OCTOBER, R.string.scorpius)
)
a.put(Calendar.NOVEMBER, Tierkreiszeichen(
23, Calendar.NOVEMBER, R.string.sagittarius)
)
a.put(Calendar.DECEMBER, Tierkreiszeichen(
22, Calendar.DECEMBER, R.string.capricornus)
)
return a
}

fun getTierkreiszeichenFuerMonat(
monat: Int
): Tierkreiszeichen = tierkreis.get(monat)

Listing 3.3    Die Datei »Zodiak.kt«

Die private Funktion createSparseArray() legt die zwölf Tierkreiszeichen in einem SparseArray ab. Diese Klasse mappt Zahlen (Int) auf Objekte, im konkreten Fall Tierkreiszeichen. Es können also Schlüssel-Wert-Paare gespeichert werden. SparseArray wurde mit der Intention entwickelt, speichersparender zu sein als die von Java bekannte HashMap. Der Schlüssel ist der Monat, in dem ein Sternzeichen beginnt. Damit kann durch Aufruf von getTierkreiszeichenFuerMonat() das gewünschte Tierkreiszeichen einfach ermittelt werden. Im nächsten Abschnitt zeige ich Ihnen, wie Sie diese Funktion in eine Benutzeroberfläche integrieren.

 
Zum Seitenanfang

3.1.3    Benutzeroberfläche Zur vorigen ÜberschriftZur nächsten Überschrift

Bevor Sie mit der Programmierung der Benutzeroberfläche beginnen, sollten Sie das Konzeptdokument um eine entsprechende Beschreibung erweitern. Hierbei geht es nicht um eine möglichst genaue Vorwegnahme des späteren Bildschirminhalts. Überlegen Sie sich stattdessen, welche Informationen Sie anzeigen möchten, in welcher Beziehung sie zueinanderstehen und wie sie logisch angeordnet werden.

Der Benutzer der App Tierkreiszeichen möchte sicher wissen, wann ein Sternzeichen beginnt und wann es endet. Außer dem Namen erwartet er noch ein Bild oder Symbol desselben. Mit diesen Informationen können Sie eine logische Darstellung der GUI entwerfen. Hierfür haben Sie zahlreiche Möglichkeiten. Neben speziellen Wireframe-Editoren gibt es Bibliotheken für gängige Visualisierungsprogramme, wie etwa Microsofts Visio oder das auf dem Mac verbreitete OmniGraffle. Auch einfache Strichzeichnungen, sogenannte Scribbles, sind ein geeignetes Mittel. Welche Variante Sie wählen, ist letztlich eine Frage des persönlichen Geschmacks. Abbildung 3.2 zeigt einen Wireframe der Hauptansicht. Systembestandteile, beispielsweise die Statuszeile sowie der Anwendungsname, wurden bewusst weggelassen.

Wireframe der Listenansicht

Abbildung 3.2    Wireframe der Listenansicht

Android stellt mit ListView eine Komponente zur Verfügung, die auch mehrzeilige Elemente und Grafiken problemlos darstellen kann. Wie sie zusammengesetzt und in der App verwendet wird, zeige ich im Folgenden. Bitte sehen Sie sich zunächst die Datei icon_text_text.xml im Verzeichnis res\layout an. In ihr wird der Aufbau eines Listenelements definiert. Jede Zeile der ListView unserer Tierkreiszeichen-App wird aus den Elementen dieser Layoutdatei zusammengesetzt. Der etwas sperrige Name beschreibt übrigens ihren Inhalt: ein Icon sowie zwei Textzeilen.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">

<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/hint"
app:tint="?attr/colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
app:layout_constraintBottom_toTopOf="@id/text2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintTop_toBottomOf="@id/text2" />

<TextView
android:id="@+id/text2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:textAppearance="?android:attr/textAppearanceSmall"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintTop_toBottomOf="@id/text1" />

</androidx.constraintlayout.widget.ConstraintLayout>

Listing 3.4    Die Datei »icon_text_text.xml«

Layoutdateien bilden stets baumartige Strukturen. Ein Wurzelelement enthält ein oder mehrere Kinder. Diese können entweder für sich stehen (Schaltflächen, Eingabefelder etc.) oder wiederum die Wurzel von Teilbäumen darstellen. Der Dateiname icon_text_text.xml beschreibt in gewisser Weise den Aufbau des Layouts, nämlich ein Bild (ImageView) und zwei Textzeilen (TextView). Die Grafiken der Tierkreiszeichen sind einfarbig, ihr Hintergrund ist transparent. Um die Farbe zu setzen, verwende ich den folgenden Ausdruck:

app:tint="?attr/colorAccent"

Er besagt, dass sich Android die Farbe aus einem sogenannten Theme holt. Sie können sich colorAccent als eine Art Variable vorstellen. Sie wird in der Datei res/values/styles.xml gesetzt.

ConstraintLayouts beschreiben die Lage und die Größe von Views in Form von Abhängigkeiten oder Beschränkungen zu anderen Bedienelementen. app:layout_constraintBottom_toBottomOf bedeutet, dass sich der untere Rand einer View an dem einer anderen orientiert. Das Attribut app:layout_constraintStart_toEndOf legt fest, dass eine Komponente sich an das Ende einer anderen anschließt. Die Grafik ist deshalb durch den oberen, unteren und linken Rand des ConstraintLayouts (parent) beschränkt. Neben dem Bild erscheinen zwei Textzeilen, deren Schrift unterschiedlich groß ist. Die Schriftgröße setzen Sie folgendermaßen:

android:textAppearance="?android:attr/textAppearanceMedium"

oder

android:textAppearance="?android:attr/textAppearanceSmall"

Vielleicht ist Ihnen aufgefallen, dass die beiden TextViews verkettet sind. Die erste verweist mit app:layout_constraintBottom_toTopOf und app:layout_constraintTop_toBottomOf auf die zweite. Diese wiederum referenziert mit app:layout_constraintTop_toBottomOf auf die erste und mit app:layout_constraintEnd_toEndOf="parent" auf das ConstraintLayout. Ketten werden verwendet, um mehrere Elemente als Gruppe zu zentrieren, hier vertikal. Solche Oberflächenbeschreibungen sind zugegebenermaßen nicht ganz leicht zu lesen. Allerdings steht in Android Studio ein mächtiger visueller Editor zur Verfügung, mit dem sich Bedienelemente sehr einfach erstellen lassen. Wir werden etwas später darauf zurückkommen.

[»]  Hinweis

Ist Ihnen aufgefallen, dass ich in meiner Beschreibung von links und rechts gesprochen habe, im Code aber Elemente wie ...Start... und ...End... verwendet werden? Im europäischen und amerikanischen Kulturraum sind wir gewohnt, von links nach rechts zu lesen. Andere Sprachen »denken« aber in umgekehrter Richtung. In solchen Sprachräumen lebende Nutzer orientieren sich deshalb von rechts kommend nach links. Bei der Beschreibung von Oberflächen trägt Android dem Rechnung, indem von »echten« Positionen (links oder rechts) abstrahiert und stattdessen Abhängigkeiten (Start und Ende) verwendet werden können. Ich rate Ihnen, wann immer möglich diese allgemeineren Positionsangaben in Ihren Layoutdateien zu verwenden.

Wie aus icon_text_text.xml zur Laufzeit der App ein Objektbaum wird, zeige ich Ihnen gleich. Zunächst aber möchte ich kurz demonstrieren, mit wie wenig Aufwand sich unter Android mithilfe der Klasse ListView eine Listenanzeige bauen lässt. Sehen Sie sich hierzu bitte das folgende Listing an:

package com.thomaskuenneth.androidbuch.tierkreiszeichen

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.AdapterView
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

private lateinit var adapter: TierkreiszeichenAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
adapter = TierkreiszeichenAdapter(this)
val liste = findViewById<ListView>(R.id.liste)
liste.adapter = adapter
liste.onItemClickListener
= AdapterView.OnItemClickListener { _, _, position, _ ->
val zeichen = adapter.getItem(position) as Tierkreiszeichen
val url = getString(R.string.wikipedia_url, zeichen.getName(this))
// eine Webseite anzeigen
val viewIntent = Intent(
"android.intent.action.VIEW",
Uri.parse(url)
)
startActivity(viewIntent)
}
}
}

Listing 3.5    Die Klasse »MainActivity«

Welche Daten eine Liste anzeigt, wird durch sogenannte Adapter gesteuert. Android stellt hierfür einige fertige Klassen bereit. Sie können beispielsweise mit android.widget.ArrayAdapter<String> String-Arrays oder mit android.widget.SimpleCursorAdapter Tabellenzeilen einer Datenbank so umwandeln, dass eine ListView sie darstellen kann. Die ListView greift über die Methoden des Interface android.widget.ListAdapter auf einen Adapter und die durch ihn bereitgestellten Daten zu. Um zu verstehen, wie das Zusammenspiel funktioniert, werden wir in diesem Beispiel keine fertige Klasse verwenden, sondern einen eigenen Adapter schreiben. Die App erzeugt hierzu ein Objekt des Typs TierkreiszeichenAdapter und setzt es mit

val liste = findViewById<ListView>(R.id.liste)
liste.adapter = adapter

Damit das klappt, muss in der Layoutdatei activity_main.xml mit <ListView android:id="@+id/liste" ... /> eine ListView definiert werden. Sie ist das einzige Kindelement dieses ConstraintLayouts. Um auf das Antippen eines Eintrags zu reagieren, wird ein sogenannter OnItemClickListener registriert (liste.onItemClickListener = ...). Das Interface definiert die Methode onItemClick(). Ihr werden drei Argumente übergeben: die View, die das angeklickte Element enthält, das angeklickte Element selbst sowie eine ID der korrespondierenden Zeile. Die Beispielimplementierung nutzt dies, um den eingebauten Webbrowser mit einer bestimmten URL zu starten. Ausführliche Informationen zu sogenannten Intents finden Sie in Kapitel 4, »Wichtige Grundbausteine von Apps«.

Lassen Sie uns nun einen Blick auf die Adapterimplementierung werfen:

package com.thomaskuenneth.androidbuch.tierkreiszeichen

import android.content.Context
import android.view.*
import android.widget.*
import java.text.*
import java.util.*
import kotlin.collections.ArrayList

class TierkreiszeichenAdapter(context: Context) : BaseAdapter() {

private val inflater = LayoutInflater.from(context)
private val zodiak = ArrayList<Tierkreiszeichen>()
private val cal = Calendar.getInstance()

// Legt fest, in welchem Format das Datum ausgegeben wird
private val df = SimpleDateFormat(
context.getString(R.string.format_string),
Locale.US
)

init {
// Tierkreiszeichen für alle Monate ermitteln
for (monat in Calendar.JANUARY..Calendar.DECEMBER) {
val zeichen = getTierkreiszeichenFuerMonat(monat)
zodiak.add(zeichen)
}
}

override fun getCount(): Int {
return zodiak.size
}

override fun getItem(position: Int): Any {
return zodiak[position]
}

override fun getItemId(position: Int): Long {
return position.toLong()
}

override fun getView(
position: Int,
convertView: View?, parent: ViewGroup
): View {
var currentPosition = position
val view: View
val holder: ViewHolder
// falls nötig, convertView bauen
if (convertView == null) {
// Layoutdatei entfalten
view = inflater.inflate(
R.layout.icon_text_text,
parent, false
)
// Holder erzeugen
holder = ViewHolder()
holder.name = view.findViewById(R.id.text1)
holder.datumsbereich = view.findViewById(R.id.text2)
holder.icon = view.findViewById(R.id.icon)
view.tag = holder
} else {
// Holder bereits vorhanden
holder = convertView.tag as ViewHolder
view = convertView
}
var zeichen = getItem(currentPosition) as Tierkreiszeichen
// Name und Symbol
holder.name.text = zeichen.getName(parent.context)
holder.icon.setImageResource(zeichen.idForDrawable)
// Erster Tag des Tierkreiszeichens
cal.set(Calendar.DAY_OF_MONTH, zeichen.tag)
cal.set(Calendar.MONTH, zeichen.monat)
val datum1 = df.format(cal.time)
// Den letzten Tag des Tierkreiszeichens ermitteln
if (++currentPosition >= count) {
currentPosition = 0
}
zeichen = getItem(currentPosition) as Tierkreiszeichen
cal.set(Calendar.DAY_OF_MONTH, zeichen.tag - 1)
cal.set(Calendar.MONTH, zeichen.monat)
val datum2 = df.format(cal.time)
holder.datumsbereich.text = parent.context.getString(
R.string.interval, datum1, datum2
)
return view
}
}

private class ViewHolder {
lateinit var name: TextView
lateinit var datumsbereich: TextView
lateinit var icon: ImageView
}

Listing 3.6    Die Datei »TierkreiszeichenAdapter.kt«

TierkreiszeichenAdapter leitet von android.widget.BaseAdapter ab. Sie könnten prinzipiell das ListAdapter-Interface auch direkt implementieren, allerdings müssten Sie sich dann um Benachrichtigungen bei Änderungen selbst kümmern. In einem Initialisierungsblock werden die Tierkreiszeichen in einer ArrayList abgelegt und aufsteigend nach Monaten sortiert. Die überschriebene Methode getCount() liefert die Länge der Liste und damit die Zahl der insgesamt anzeigbaren Zeilen. getItem() liefert das Element an einer bestimmten Position. Beide gehören übrigens zu android.widget.Adapter, von dem widerum ListAdapter ableitet.

Die Methode getView() baut aus den Daten des Modells (also einem Tierkreiszeichen) einen Eintrag zusammen und stellt ihn in Gestalt einer View-Instanz zur Verfügung. Aus Effizienzgründen puffert Android eine gewisse Menge solcher Objekte. Im Fall der erstmaligen Verwendung ist der Parameter convertView gleich null. Dann wird mithilfe eines LayoutInflator aus einer XML-Datei (icon_text_text.xml) ein entsprechender Komponentenbaum erzeugt. Diesem wird mit view.tag = holder ein sogenannter (mit holder = ViewHolder() erzeugter) ViewHolder übergeben. Er fungiert als eine Art Platzhalter, um später einfach und effizient auf die Elemente des Baumes zugreifen zu können. Dies ist nötig, um die beiden Textzeilen sowie die Grafik mit den Werten für eine Listenzeile füllen zu können. Der Aufruf von findViewById() für jedes der drei Objekte wäre wesentlich kostspieliger, zumal dies ja für jedes angezeigte Listenelement geschieht. War die an getView() übergebene convertView nicht null, kann mit holder = convertView.tag as ViewHolder der früher bereits erzeugte ViewHolder ermittelt und dann weiter verwendet werden.

Haben Sie sich über die Zeilen nach if (++currentPosition >= count) { gewundert? Für jedes Tierkreiszeichen speichere ich, wann es beginnt, nicht jedoch, wann es endet. Da alle Tierkreiszeichen in der Abfolge der Kalendermonate angezeigt werden, muss ich nur auf das nächste Element in der Liste zugreifen und dessen Starttag ermitteln. Natürlich könnte man den letzten Tag und den Folgemonat auch in der Klasse Tierkreiszeichen selbst ablegen.

Lassen Sie mich kurz zusammenfassen. Ein Adapter besteht aus zwei logischen Blöcken, nämlich einem Modell, das die anzuzeigenden Daten enthält, sowie einer Umwandlungslogik, die aus diesen Informationen einen Mini-Komponentenbaum bastelt. Im folgenden Abschnitt zeige ich Ihnen, wie Sie Ihr Programm mit Debug-Ausgaben versehen und wie Sie es auf die Veröffentlichung in Google Play vorbereiten.

 


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