Android App Entwicklung für Dummies
Author: Donn Felker; Michael Burton
Date: 2015-12-01T21:23:29+00:00
Keywords: General; Programming Languages; Java; Programming; Computers
Description: Die ganze Welt der Android App Entwicklung in einem Buch Haben Sie eine kreative Idee für eine eigene App und Vorkenntnisse in Java und XML? Dann erfahren Sie in diesem Buch, wie Sie Ihre Idee umsetzen und eine App mit allen Schikanen für Android-Smartphones programmieren können. Der Autor erklärt, wie Sie die kostenlosen Programme Android Studio und Java Development Kit herunterladen, wie Sie den Emulator nutzen und wie Sie Ihre App im Google Play Store veröffentlichen. Schritt für Schritt erläutert er anhand von zwei Beispielen das Know-how der App Entwicklung. Außerdem erklärt er Ihnen, wie
Publisher: John Wiley & Sons

Android App Entwicklung für Dummies – Schummelseite

Bibliografische Information der Deutschen Nationalbibliothek

Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

2. Auflage 2013

© 2013 WILEY-VCH Verlag GmbH & Co. KGaA, Weinheim

Original English language edition Android App Development For Dummies © 2012 by Wiley Publishing, Inc.

All rights reserved including the right of reproduction in whole or in part in any form. This EBook published under license with the original publisher John Wiley and Sons, Inc.

Copyright der englischsprachigen Originalausgabe Android App Development For Dummies © 2012 by John Wiley and Sons, Inc.

Alle Rechte vorbehalten inklusive des Rechtes auf Reproduktion im Ganzen oder in Teilen und in jeglicher Form. Dieses E-Book wird mit Genehmigung des Original-Verlages John Wiley and Sons, Inc. publiziert.

Wiley, the Wiley logo, Für Dummies, the Dummies Man logo, and related trademarks and trade dress are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its affiliates, in the United States and other countries. Used by permission.

Wiley, die Bezeichnung »Für Dummies«, das Dummies-Mann-Logo und darauf bezogene Gestaltungen sind Marken oder eingetragene Marken von John Wiley & Sons, Inc., USA, Deutschland und in anderen Ländern.

Das vorliegende Werk wurde sorgfältig erarbeitet. Dennoch übernehmen Autoren und Verlag für die Richtigkeit von Angaben, Hinweisen und Ratschlägen sowie eventuelle Druckfehler keine Haftung.

Coverfoto: © sellingpix/fotolia

Korrektur:Petra Heubach-Erdmann und Jürgen Erdmann, Düsseldorf

Satz: inmedialo Digital- und Printmedien UG, Plankstadt

Print ISBN: 978-3-527-70916-8
ePub ISBN: 978-3-527-67683-5
mobi ISBN: 978-3-527-67684-2

Über die Autoren

Michael Burton ist der leitende Android-Entwickler bei Groupon. Er hat beispielsweise die Android-Apps Groupon, Digg, TripIt und OpenTable erstellt. Eines seiner Projekte ist mit dem Space Shuttle in den Weltraum gereist. Er hat bei Konferenzen in London, Boston, Silicon Valley, Rio de Janeiro und anderen Orten Reden über die Android-Anwendungsentwicklung gehalten. Und er ist der Autor von RoboGuice, dem Open-Source-Dependency-Injection-Framework, das beispielsweise von Google und Facebook verwendet wird.

Donn Felker ist ein bekannter, führender Entwickler und Berater auf dem Gebiet innovativer, topaktueller Software für mobile Endgeräte und das Internet. Als unabhängiger Berater mit langjähriger Berufserfahrung ist er in verschiedenen Bereichen wie Unterhaltung, Gesundheitswesen, Einzelhandel, Vermögensberatung und Immobilien tätig. Als Mehrfach-Firmengründer und kreativer Erfinder im Mobilbereich und dem Internet ist er von den entsprechenden Möglichkeiten abhängig und begeistert. Er ist Gründer von Agilevent, einer innovativen, kreativen Entwicklungsfirma, die Auftragsarbeiten für kleine Jung- und Fortune-500-Unternehmen erledigt hat. Er ist ein Microsoft-ASP-Insider, ein MCTS für Webanwendungen mit .NET-Framework 2.0 und 3.5 und ein zertifizierter Scrum-Master. Landesweit hält er Vorträge über Themen wie Android, .NET und Softwarearchitektur. Er ist der Autor der TekPub-Videoreihe zur Einführung in Android. In verschiedenen Themengebieten von der Architektur bis hin zur Entwicklung allgemeiner, agiler Verfahren, Muster und Vorgehensweisen ist er als Autor, Veranstalter und Berater tätig.

Danksagung

Großer Dank gebührt Donn Felker, der die ursprüngliche Version dieses Buches verfasst und mir das Projekt dann überlassen hat. Hoffentlich werden wir zukünftig noch bei vielen erfolgreichen Projekten zusammenarbeiten können!

Ein großes Dankeschön geht auch an die erweiterte Open-Source-Gemeinde rund um Android und insbesondere Carlos Sessa, Manfred Moser, Donn und Jake Wharton, die mit ihrem Code, ihren Kenntnissen und ihrer Kritik zu diesem Buch beigetragen haben.

Dank gilt auch meinem Team bei Groupon und damit Chris, Alex, Robyn, Eric, Aubrey und David, die mir dabei geholfen haben, die Android-Plattform besser zu verstehen, als es mir allein möglich gewesen wäre.

Und schließlich will ich auch meinen Freunden und meiner Familie danken, die mich während der Abende meiner Arbeit an diesem Projekt unterstützt haben. Mehr als die Gesellschaft unseres Welpen und ein paar belohnende Leckereien für fertige Kapitel haben mir wirklich über diese langen Wochenenden hinweggeholfen!

Widmung

Für BugDroid.

Über den Übersetzer

Gerhard Franken, der Übersetzer und Bearbeiter der deutschen Auflage dieses Buches, ist nach seiner Zeit als Systemadministrator und Fachlektor in einem Verlag seit Jahren als Übersetzer, Autor und Bearbeiter von Computerbüchern und anderen Publikationen tätig und hat mittlerweile eine Unmenge an Computerbüchern selbst verfasst, übersetzt und/oder bearbeitet. Darunter befinden sich auch eine ganze Reihe von … für Dummies-Bücher, wie zum Beispiel Linux für Dummies, Netzwerke für Dummies, Applikationen mit PHP und MySQL für Dummies und HTML5 Schnelleinstieg für Dummies.

Inhaltsverzeichnis

Über dieses Buch

Wie Sie dieses Buch benutzen sollten

Törichte Annahmen über den Leser

Wie dieses Buch aufgebaut ist

Teil I: Android-Grundlagen

Teil II: Ihre erste Android-App erstellen und veröffentlichen

Teil III: Eine umfassende App erstellen

Teil IV: Tablets

Teil V: Der Top-Ten-Teil

Symbole, die in diesem Buch verwendet werden

Wie es weitergeht

Teil I
Android-Grundlagen

1 Entwickeln Sie spektakuläre Android-Apps

Warum für Android entwickeln?

Marktanteil

Zeit für die Vermarktung

Offene Plattform

Übergreifende Kompatibilität

Kombinierbarkeit (Mashups)

Grundlagen der Android-Programmierung

Java: Ihre Android-Programmiersprache

Activities

Intents

Cursorlose Steuerelemente

Views

Asynchrone Aufrufe

Hintergrunddienste

Funktionen von Honeycomb, Ice Cream Sandwich und Jelly Bean

Fragmente

Loader

Android Support Library

Aktionsleiste

Holo

Widgets, Benachrichtigungen, Leistung und mehr

Hardwarefunktionen

Touchscreen

GPS

Beschleunigungssensor

SD-Speicherkarte

Softwarewerkzeuge

Internet

Audio- und Videounterstützung

Kontakte

Sicherheit

Google-APIs

2 Vorbereitung Ihrer Entwicklungszentrale

Entdecken Sie den Android-Entwickler in sich

Zusammenstellung Ihres Werkzeugkastens

Linux-Kernel 2.6

Android-Framework

Anwendungsframework

OHA-Bibliotheken

Java-Kenntnisse

Optimierung der Hardware

Betriebssystem

Computerhardware

Hilfsprogramme installieren und konfigurieren

Das JDK herunterladen und installieren

So erhalten Sie das Android-SDK

Das Android-SDK herunterladen

Setzen Sie den Pfad zu Ihren Tools

So erhalten Sie Eclipse

Eclipse installieren

Eclipse konfigurieren

Machen Sie sich mit den Android-SDK vertraut

Android-Zielplattformen

SDK-Tools zur Programmentwicklung nutzen

Lernen Sie den Emulator kennen

Physische Erfahrungen mit echten Android-Geräten

Beseitigung von Programmfehlern

Probieren Sie die API- und SDK-Beispiele aus

Sehen Sie sich die API-Demos im Testlauf an

Teil II
Ihre erste Android-App erstellen und veröffentlichen

3 Ihr erstes Android-Projekt

Ein neues Projekt in Eclipse anlegen

Analyse Ihres Projekts

Auf Fehlermeldungen reagieren

Die Bedeutung der Einstellungen Target SDK und Min Required SDK

Einen Emulator einrichten

Launch-Konfigurationen erzeugen

Die App Hallo Android starten

Die App im Emulator ausführen

Status der Bereitstellung prüfen

Die Projektstruktur

Durch die Ordner der App navigieren

Die Manifest-Datei Ihrer App

Die Datei project.properties

Ihr Projekt schließen

Eclipse-Macken

Fehlende import-Anweisungen

Projekte bereinigen

4 Design der Benutzeroberfläche

Erstellen der App »Lautlosmodus-Umschalter«

Layout der Anwendung

Die XML-Layoutdatei nutzen

Die Layoutwerkzeuge des Android-SDKs nutzen

Den visuellen Designer nutzen

Die Bedienschnittstelle entwickeln

XML-Layoutattribute betrachten

Mit Views arbeiten

Bilder zu Ihrer App hinzufügen

Ein Bild auf dem Bildschirm anzeigen

Die Bilder zum Layout hinzufügen

Ein Startsymbol für die App erstellen

Ein eigenes Startsymbol entwerfen

Ein eigenes Startsymbol hinzufügen

Ein View-Objekt für den Umschalter hinzufügen

Vorschau der App im visuellen Designer

Die Hintergrundfarbe ändern

Kosmetik und Kommentare

5 Code für Ihre App

Grundlagen von Aktivitäten

Mit Aktivitäten, Stapeln und Zuständen arbeiten

Den Lebenszyklus einer Aktivität verfolgen

Ihre erste Aktivität erstellen

Wir beginnen mit onCreate

Android die Benutzeroberfläche anzeigen lassen

Umgang mit Benutzereingaben

Ihre erste Ereignisbehandlungsroutine

Mit den Klassen des Android-Frameworks arbeiten

Dienste nutzen

Den Lautlosmodus mit AudioManager umschalten

Ihre Anwendung installieren

Zurück zum Emulator

Apps auf physischen Android-Geräten installieren

Oh je! Auf Fehler reagieren

Den DDMS (Dalvik Debug Monitor Server) benutzen

Den Eclipse-Debugger nutzen

Über Anwendungsgrenzen hinweg denken

Mit Ihrer Anwendung interagieren

Prüfen Sie, ob Ihre App funktioniert

6 Grundlagen von Android-Ressourcen

Ressourcen verstehen

Abmessungen

Styles (Formatvorlagen)

Themes (Schemas)

Werte

Menüs

Farben

Arbeiten mit Ressourcen

Strings in Ressourcen verschieben

Der Kampf mit den Bildern

Globalisierung von Apps mit Ressourcen

7 Umwandlung Ihrer App in ein Widget für den Startbildschirm

In Android mit App-Widgets arbeiten

Arbeiten mit RemoteView

AppWidgetProvider nutzen

Mit PendingIntent arbeiten

Das Intent-System von Android verstehen

Intent-Daten verstehen

Intents auswerten

PendingIntent nutzen

Das Widget für den Startbildschirm erstellen

AppWidgetProvider implementieren

Kommunikation mit dem App-Widget

Das Layout des App-Widgets erstellen

Arbeit in AppWidgetProvider verrichten

Arbeiten mit den Metadaten des App-Widgets

Die neuen Komponenten mit dem Manifest registrieren

Noch fehlende Import-Anweisungen ergänzen

Ihr App-Widget zum Startbildschirm hinzufügen

8 Verteilung Ihrer App über Google Play Store

Eine verteilbare Datei erstellen

Noch einmal zurück zur Manifest-Datei

Auswahl der Werkzeuge

Anwendungen digital signieren

Erstellen der APK-Datei

Ein Google-Play-Entwicklerprofil erstellen

Preisgestaltung für Ihre Anwendungen

Argumente für das kostenpflichtige Modell

Argumente für das kostenlose Modell

Bildschirmfotos Ihrer Anwendung

Ihre Anwendung auf Google Play Store hochladen

Installationshäufigkeit überwachen

Teil III
Eine umfassendere App erstellen

9 Entwurf einer App zur Terminplanung

Überblick über die Basisanforderungen

Erinnerungstermine erstellen

Daten speichern

Den Benutzer (höflich) aufmerksam machen

Das Layout der App-Bildschirme entwerfen

Das neue Projekt erstellen

ReminderListActivity erstellen

ReminderListFragment erstellen

Termine mit einer Activity erstellen und bearbeiten

Ein Fragment zur Activity hinzufügen

Das Layout für das Hinzufügen/Bearbeiten von Terminen erstellen

Ihr erstes ListFragment fertigstellen

Ein Rumpfgerüst mit falschen Daten

Verarbeitung von Klick-Ereignissen

Intents identifizieren

Neue Aktivitäten mit Intents starten

Einen Chooser erstellen

10 Menüs gestalten

Grundlagen von Auswahl- und Kontextmenüs

Ihr erstes Menü erstellen

Die XML-Datei erstellen

Umgang mit Benutzeraktionen

Einen Erinnerungstermin erstellen

Ein Kontextmenü erstellen

Die XML-Datei für das Menü erstellen

Das Menü laden

Verarbeitung der Benutzerauswahl

11 Verarbeitung von Benutzereingaben

Schnittstellen für Benutzereingaben erstellen

Ein EditText-Widget erstellen

Anzeige einer Bildschirmtastatur

Auswahl von Datum und Uhrzeit

Auswahl-Schaltflächen erstellen

DatePicker einbinden

TimePicker einbinden

TimePickerDialogFragment erzeugen

Das Fragment für die Verarbeitung von DatePicker- und TimePickerCallbacks erstellen

Ein Alert-Dialogfeld erstellen

Warum Sie Dialogfelder nutzen sollten

Zu den Aufgaben passende Dialogfelder auswählen

Ein eigenes Alert-Dialogfeld erstellen

Eingaben prüfen

Toast-Meldungen

Andere Prüfungsverfahren nutzen

12 Eingegebene Daten dauerhaft speichern

Orte zum Speichern von Daten

Die verschiedenen Speicheroptionen

Auswahl einer Speicheroption

Die SQLite-Datenbank für Ihre App erstellen

Die Arbeitsweise der SQLite-Datenbank

Einen ContentProvider für den Datenbankcode erstellen

Die Schlüsselelemente einer Datenbank definieren

Die SQL-Tabelle visualisieren

Die Datenbanktabelle erstellen

ContentProvider-URLs auflösen

Termine mit SQLite erstellen und bearbeiten

Einen Termineintrag einfügen

Loader

Alle Termine über einen Cursor zurückgeben

Grundlagen von SimpleCursorAdapter

Einen Termin löschen

13 Terminerinnerungen mit AlarmManager

Warum AlarmManager benötigt wird

Den Benutzer um Erlaubnis bitten

Welchen Einfluss Berechtigungen auf die Benutzerfreundlichkeit haben

Berechtigungen über die Datei AndroidManifest.xml anfordern

Einen Prozess mit AlarmManager aufwecken

Die Klasse ReminderManager erstellen

Die Klasse OnAlarmReceiver erstellen

Die Klasse WakeReminderIntentService erstellen

Die Klasse ReminderService erzeugen

Geräte neu starten

Einen BootReceiver erzeugen

Funktion von BootReceiver prüfen

14 Die Android-Statusleiste aktualisieren

Elemente der Statusleiste

NotificationManager benutzen

Eine Benachrichtigung erstellen

Der Arbeitsablauf

String-Ressourcen hinzufügen

Benachrichtigungen aktualisieren

Benachrichtigungen entfernen

15 Arbeiten mit dem Android-Preference-Framework

Das Android-Preference-Framework verstehen

Grundlagen der Klasse PreferenceActivity

Einstellungen dauerhaft speichern

Das Layout von Einstellungsbildschirmen

Einen eigenen Einstellungsbildschirm erstellen

Eine Datei für die Einstellungen erstellen

String-Ressourcen hinzufügen

Die Klasse PreferenceActivity nutzen

Anlegen der Klasse PreferenceActivity

Verarbeitung der Menüauswahl

Während der Programmausführung mit Einstellungen in Aktivitäten arbeiten

Werte von Einstellungen ermitteln

Werte von Einstellungen programmgesteuert setzen

Teil IV
Tablets

16 Apps für Tablets entwickeln

Berücksichtigung der Unterschiede zwischen Telefon und Tablet

Optimierung des Terminplaners für Tablets

Variable Layouts für unterschiedlich große Bildschirme

Weitere Fragmente hinzufügen

Verschiedene Layouts für unterschiedliche Geräte erstellen

Nutzung der Aktionsleiste

Die Support-Library und ActionBarSherlock nutzen

17 Apps auf Android-Tablets portieren

Einen Tablet-Emulator konfigurieren

Die Datei AndroidManifest aktualisieren

Activities für Tablets programmieren

ReminderListAndEditorActivity erstellen

Die richtige Activity auswählen

Das Layout der Activity erstellen

Bei Tablet-Apps mit Fragmenten arbeiten

Kommunikation zwischen Fragmenten

Fragment-Transaktionen hinzufügen

18 Jenseits von Google

Google-Funktionen umgehen

Ihr Kindle Fire oder einen passenden Emulator einrichten

Einen Kindle-ähnlichen Emulator erstellen

ADB konfigurieren (Mac)

ADB konfigurieren (Windows)

Android-Apps bei Amazon veröffentlichen

Teil V
Der Top-Ten-Teil

19 Zehn tolle kostenlose Beispielanwendungen und SDKs (mit Code!)

Google I/O 2012

LOLCat Builder

Amazed

API-Demos

HoneycombGallery

K-9 Mail

Agit

Facebook-SDK für Android

Replica Island

Notepad Tutorial

20 Zehn Tools zur Erleichterung Ihres Entwicklerlebens

Droid-Fu und ignition

RoboGuice

Translator Toolkit

Draw 9-patch

Hierarchy Viewer

UI/Application Exerciser Monkey
zipalign
layoutopt

Git

Paint.NET und GIMP

Stichwortverzeichnis

Einführung

Willkommen bei Android App Entwicklung für Dummies.

Als Android 2005 von Google übernommen wurde (ja, Android war einmal ein junges, aufstrebendes Unternehmen), stieß das zunächst einmal auf kein verbreitetes Interesse, da Google im Mobilbereich noch gar nicht aktiv geworden war. Nur wenige Jahre später war es dann aber so weit, als Google mit dem G1 sein erstes Android-Smartphone vorstellte. Und es bedeutete den Startschuss einer wahrlich enormen Entwicklung.

Das G1 war das erste frei verfügbare Android-Gerät. Es konnte seinerzeit zwar nicht mit der großen Funktionsvielfalt des iPhones mithalten, die Plattform genoss aber Vertrauen. Und mit der Veröffentlichung von Donut (Android 1.6) wurde klar, dass Google erhebliche Anstrengungen in das Produkt investierte. Und tatsächlich erschien bereits direkt nach der Version 1.6 die Version 2.0 am Horizont.

Aktuell sind wir bei der Version 4.1 der Android-Plattform angekommen. Die Plattform ist erst etwa zwei Jahre alt und noch gibt es keine Anzeichen, dass sich das Entwicklungstempo verlangsamen würde. Zweifellos handelt es sich immer noch um aufregende Zeiten im Bereich der Android-Entwicklung.

Über dieses Buch

Android App Entwicklung für Dummies soll Sie beim Einstieg in die Entwicklung von Android-Anwendungen unterstützen. Sie benötigen dazu keine Vorkenntnisse in der Android-Anwendungsentwicklung. Sie können die Materialien auch ohne vorherige Erfahrungen mit der Android-Platt form nutzen, zumal diese ohnehin verschiedene Verfahren nutzt und auf Paradigmen zurückgreift, die bei den meisten Programmierern nicht zum alltäglichen Handwerkszeug gehören.

Android ist eine geräteunabhängige Plattform. Sie können daher Anwendungen für unterschiedliche Geräte entwickeln. Dabei handelt es sich nicht nur um Telefone, E-Book-Reader, Netbooks, Fernseher und GPS-Geräte.

Wenn Sie wissen, wie Sie Anwendungen für die Android-Plattform entwickeln können, erschließen Sie sich damit eine Vielzahl von Optionen. In diesem Buch werden Hunderte, wenn nicht Tausende Seiten an Android-Dokumentation, Tipps, Tricks und Tutorials in einem kurzen, besser verdaulichen Format zusammengefasst, damit es Ihnen als Sprungbrett in eine Zukunft als Android-Entwickler dienen kann. In diesem Buch finden Sie keine fertigen Rezepte, es versieht Sie aber mit den grundlegenden Kenntnissen, die Sie benötigen, um mit den verschiedenen Android-Bausteinen interaktive und überzeugende Anwendungen erstellen zu können.

Wie Sie dieses Buch benutzen sollten

In diesem Buch nutzen Sie die Klassen des Android-Frameworks und werden Java-Klassen und XML-Dateien erstellen.

Quelltextbeispiele werden in diesem Buch in einer nichtproportionalen Schriftart dargestellt, damit sie sich von dem übrigen Text abheben. Codebeispiele sehen daher so aus:

public class MainActivity

Java ist eine Hochsprache, die zwischen Groß- und Kleinschreibung unterscheidet. Daher müssen Sie den Text genau so in den Editor eingeben, wie er im Buch abgedruckt ist. Ich werde mich im Buch zudem an die üblichen Java-Konventionen halten. Das erleichtert die Austauschbarkeit zwischen meinen Beispielen und denen aus dem Android-SDK (Android Software Development Kit). Für Klassennamen wird zum Beispiel immer ein Format mit Binnenversalien (PascalCase) verwendet und alle klassenweit gültigen Variablen beginnen mit m.

Auch alle im Buch angegebenen Internetadressen (URLs) werden in der für Quellcode verwendeten Schriftart dargestellt:

http://d.android.com

Sie können sich die vollständigen Quelltexte aus dem Internet herunterladen. Sie finden sie auf den Dummies-Webseiten unter der Adresse: www.downloads.fuer-dummies.de

Törichte Annahmen über den Leser

Für den Einstieg in die Android-Programmierung benötigen Sie einen Computer, auf dem eines der folgenden Betriebssysteme läuft:

Windows XP (32 Bit), Vista (32 oder 64 Bit), Windows 7 oder 8 (32 oder 64 Bit)

Mac OS X (Intel) ab Version 10.5.8 (nur x86)

Linux (i386)

Sofern sich diese Programme nicht bereits auf Ihrem Rechner befinden, müssen Sie sich darüber hinaus das Android-SDK (kostenlos) und das Java Development Kit (das JDK ist ebenfalls kostenlos) herunterladen. In Kapitel 2 werde ich die Installation der Tools und Frameworks ausführlich darstellen.

Da Android-Apps in der Programmiersprache Java entwickelt werden, müssen Sie diese – wie bereits erwähnt – verstehen. Zudem macht Android bei der Definition verschiedener Ressourcen innerhalb der Anwendungen recht intensiv von XML Gebrauch, weshalb Sie auch mit XML vertraut sein sollten. Sie müssen aber kein Experte in diesen Sprachen sein.

Sie benötigen auch kein physisches Android-Gerät, da alle in diesem Buch erstellten Applikationen im Emulator funktionieren.

Wie dieses Buch aufgebaut ist

Android App Entwicklung für Dummies besteht aus fünf Teilen, die ich in den folgenden Abschnitten beschreiben werde.

Teil I: Android-Grundlagen

In Teil I werden die von Ihnen bei der Entwicklung von Android-Anwendungen verwendeten Tools und Frameworks vorgestellt. Hier werden auch die verschiedenen SDK-Komponenten und deren Einsatz im Android-Entwicklungssystem vorgestellt.

Teil II: Ihre erste Android-App erstellen und veröffentlichen

In Teil II werden Sie Ihre erste Android-Applikation erstellen, den Umschalter in den stillen Modus. Nach Fertigstellung der Rohanwendung werden Sie dafür ein Steuerelement erstellen, das auf dem Startbildschirm eines Android-Geräts abgelegt werden kann. Anschließend werde ich Ihnen zeigen, wie Sie Ihre Anwendung auf dem Google Play genannten Marktplatz für Android-Apps veröffentlichen können.

Teil III: Eine umfassende App erstellen

Teil III stellt dann etwas höhere Anforderungen an Ihre Fähigkeiten und zeigt Ihnen schrittweise die Erstellung einer Anwendung, mit der Sie Termine für Aufgaben festlegen und sich daran erinnern lassen können. Im Rahmen dieser aus mehreren Bildschirmen bestehenden Anwendung werden Sie auch eine SQLite-Datenbank implementieren. Zudem werden Sie erfahren, wie Sie in der Android-Status- und Benachrichtigungsleiste Meldungen anzeigen lassen und die Benutzerfreundlichkeit Ihrer Anwendungen damit steigern können.

Teil IV: Tablets

In Teil IV werden wir die in Teil III erstellte App noch ein wenig optimieren, um sie auch auf Android-Tablets sinnvoll nutzen zu können. Sie werden dabei auch erfahren, wie Sie Ihre Apps auf Geräte wie das Amazon Kindle Fire übertragen können, die nicht mit dem Googles Android arbeiten.

Teil V: Der Top-Ten-Teil

In Teil IV erhalten Sie einen Überblick über Beispielanwendungen, die sich als phänomenale Einstiegshilfen für Ihre eigenen Android-Apps erweisen können, und ich werde Ihnen nützliche Android-Bibliotheken vorstellen, mit denen Sie sich Ihr Leben als Android-Entwickler erheblich erleichtern können.

Symbole, die in diesem Buch verwendet werden

Dieses Symbol verweist auf Informationen, die Sie keinesfalls überspringen sollten.

Dieses Symbol soll Sie höflich auf wichtige Dinge aufmerksam machen, die Sie beim Lesen des jeweiligen Abschnitts nicht vergessen sollten.

Dieses Symbol weist darauf hin, dass die entsprechenden Erläuterungen zwar informativ sind, aber für das Verständnis der Android-Anwendungsentwicklung nicht zwingend benötigt werden. Wenn Sie wollen, können Sie diese Absätze überspringen.

Dieses Symbol weist auf potenziell auftretende Probleme hin. Lesen und merken Sie sich diese weisen Hinweise, um möglichen Ärger zu vermeiden.

Wie es weitergeht

Nun ist es an der Zeit, die Android-Plattform zu erforschen! Sie können Ihre Scheu ablegen und loslegen. Ein wenig Nervosität ist angesichts des aufregenden neuen Themas aber sicherlich verständlich.

Teil I

Android-Grundlagen

In diesem Teil …

In Teil I stelle ich Ihnen die Android-Plattform vor und werde erläutern, was eine spektakuläre Android-Applikation ausmacht. Dabei werde ich kurz verschiedene Teile des Android-SDKs (Software Development Kits) untersuchen und Ihnen erläutern, wie Sie sie in Ihren Apps nutzen können. Im Verlauf des Kapitels werden Sie diese zur Entwicklung von Android-Apps erforderlichen Tools und Frameworks installieren.

1

Entwickeln Sie spektakuläre Android-Apps

In diesem Kapitel

Gründe für die Entwicklung von Android-Apps

Erste Schritte in der Android-Programmierung

Arbeiten mit der Hardware

Sich mit der Software vertraut machen

Google ist Spitze! Google übernahm 2005 das Android-Projekt (siehe Kasten »Die Wurzeln von Android«), um ein Betriebssystem für Mobilgeräte zu entwickeln und es als offene Plattform zur Verfügung zu stellen. Google investiert weiterhin Zeit und Ressourcen in das Android-Projekt, das seine Lukrativität bereits bewiesen hat. Obwohl Android erst im Oktober 2008 bereitgestellt wurde, werden heute täglich etwa eine Million Android-Geräte aktiviert. Innerhalb weniger Jahre konnte Android riesigen Einfluss gewinnen!

Nie war es für Entwickler einfacher, Geld mit der App-Entwicklung zu verdienen. Android-Anwender vertrauen Google. Und da die Apps über den von Google kontrollierten Play Store angeboten werden, nehmen sie an, dass auch diese Anwendungen in Ordnung sind.

Die Wurzeln von Android

Es ist nur wenig bekannt, dass Google das Android-Projekt nicht ins Leben gerufen hat. Anfangs wurde das Betriebssystem von einem kleinen Start-up-Unternehmen in Silicon Valley namens Android entwickelt, das dann im Juli 2005 von Google übernommen wurde. Die Android-Pioniere kamen aus verschiedenen Technologieunternehmen des Internet-Bereichs, zum Beispiel Danger, Wildfire Communications T-Mobile und WebTV. Google übernahm sie mit in das Google-Team, um dort das heute umfassende Android-Betriebssystem für Mobilgeräte zu entwickeln.

Warum für Android entwickeln?

Die Frage sollte eigentlich besser »Warum nicht?« lauten. Wollen Sie nicht, dass Ihre App weltweit für Millionen Anwender verfügbar ist? Wollen Sie Apps sofort nach Abschluss der Entwicklung und Tests veröffentlichen? Gefällt es Ihnen, für offene Plattformen zu entwickeln? Wenn Sie eine dieser Fragen bejaht haben, dürften Sie die Antwort kennen. Sollten Sie aber noch unentschlossen sein, lesen Sie einfach weiter.

Marktanteil

Als Entwickler können Sie Apps für einen noch recht jungen, aber boomenden Markt entwickeln. Die meisten Marktanalysen kommen mittlerweile zu dem Ergebnis, dass die Anzahl der genutzten Android-Geräte heute größer als die aller anderen Betriebssysteme für Mobilgeräte zusammengenommen ist. Über Google Play Store gelangt Ihre App direkt zum Anwender (eigentlich dessen Gerät)! Anwender müssen nicht das Internet nach zu installierenden Apps durchsuchen. Sie müssen nur den auf ihren Geräten vorinstallierten Google Play Store nutzen, um an Ihre Apps kommen zu können. Da Google Play Store auf den meisten Android-Geräten vorinstalliert ist (auf ein paar Ausnahmen werde ich später noch eingehen), suchen Anwender üblicherweise erst einmal im Play Store nach von ihnen benötigten Apps. Es ist daher gar nicht so ungewöhnlich, wenn die Anzahl der Downloads einer App binnen weniger Tage förmlich explodiert.

Zeit für die Vermarktung

Angesichts all der APIs (Anwendungsprogrammierschnittstellen – Application Programming Interfaces), die sich mit im Lieferumfang von Android befinden, lassen sich innerhalb relativ kurzer Zeit umfassende Anwendungen entwickeln. Nach der Registrierung beim Google Play Store müssen Sie Ihre Apps nur noch hochladen und veröffentlichen. Im Unterschied zu den Marktplätzen anderer Mobilgeräte gibt es bei Google Play Store keine zeitraubenden Genehmigungsverfahren für Apps. Sie müssen Ihre Apps nur entwickeln, schreiben und veröffentlichen.

Aus technischer Sicht kann zwar jeder Beliebige Apps veröffentlichen, es dürfte aber besser sein, wenn Sie sich an die Google-Bedingungen halten und Ihre Apps familienfreundlich gestalten. Und vergessen Sie auch nicht, dass Android-Anwender aus den verschiedensten Regionen der Welt und allen Alterskategorien kommen.

Offene Plattform

Das Android-Betriebssystem ist eine offene Plattform und damit an keinen Hardwarehersteller und/oder Anbieter gebunden. Wie sich leicht vorstellen lässt, konnte Android durch seine freie Verfügbarkeit schnell Marktanteile gewinnen. Nichts hält Sie davon ab, den Android-Quellcode zu studieren, wenn Sie herausfinden wollen, wie bestimmte Aufgaben darin gelöst werden. Sie können ihn sich über http://source.android.com herunterladen. Durch den quelloffenen Code können Hersteller eigens angepasste Benutzeroberflächen oder auch Bedienoberflächen (UI – User Interface) erstellen und Android um die integrierten Funktionen bestimmter Komponenten erweitern.

Übergreifende Kompatibilität

Android läuft auf vielen Geräten mit unterschiedlichen Bildschirmabmessungen und Auflösungen. Zudem enthält es auch die Werkzeuge zur Entwicklung entsprechender Apps. Google sorgt jedoch dafür, dass Ihre Apps nur auf kompatiblen Geräten ausgeführt werden. Wenn eine App nur mit einer Kameralinse an der Gerätefront funktioniert, wird sie im Play Store nur bei Benutzung derart ausgestatteter Geräte angezeigt. Diese Art der Hardwareerkennung wird bei Android Feature Detection genannt. (Mehr zur Veröffentlichung Ihrer Apps auf dem Android-Marktplatz erfahren Sie in Kapitel 8.)

Kombinierbarkeit (Mashups)

Wenn Sie zwei oder mehr Dienste kombinieren, um eine App zu erstellen, wird dies Mashup genannt. Sie können beispielsweise ein Mashup erstellen, wenn Sie die Kamera und die Ortungsdienste von Android nutzen, um ein Foto aufzunehmen, in dem die genaue Positionsangabe angezeigt wird. Oder Sie könnten beispielsweise das Maps-API mit der Kontaktliste kombinieren, um all Ihre Kontakte in einer Landkarte anzeigen zu lassen. Die folgenden Mashup-Beispiele sollen Ihre Fantasie noch ein wenig stärker anregen:

Geolokation und soziale Netze: Nehmen wir an, Sie wollen eine App schreiben, die Ihren aktuellen Standort mittels geologischer Ortsbestimmung (Geolokation) über den gesamten Tag hinweg alle zehn Minuten auf Twitter meldet. Kein Problem. Dazu müssen Sie nur die Lokalisierungsdienste von Android und das Twitter-API eines Drittanbieters (zum Beispiel iTwitter) miteinander kombinieren.

Geolokation und Spiele: Ortsabhängige Spiele werden immer beliebter und bieten tolle Möglichkeiten, Anwender in Spiele einzubinden. Ein Spiel könnte als Hintergrunddienst laufen, Ihren aktuellen Standort feststellen und diesen dann mit den Positionen anderer Anwender in derselben Gegend vergleichen. Befindet sich dann beispielsweise ein anderer Anwender in weniger als einem Kilometer Entfernung von Ihnen, könnten Sie benachrichtigt werden und ihn oder sie zum Kampf herausfordern. Ermöglicht wird dies durch die leistungsfähigen Technologien von GPS und Android.

Kontakte und Internet: Angesichts der Vielzahl der verfügbaren nützlichen APIs lassen sich über die Kombination von Funktionen mehrerer APIs leicht umfassende Apps erstellen. Sie können beispielsweise die Namen aus Kontaktlisten und das Internet gemeinsam dazu benutzen, um Grußkarten-Apps zu erstellen. Sie könnten Anwendern über Ihre App auch die Kontaktaufnahme mit Ihnen erleichtern oder ihnen die Weiterleitung der App an Freunde ermöglichen. All dies ist mit den integrierten APIs machbar. (Mehr über APIs erfahren Sie im Abschnitt »Google-APIs« weiter hinten in diesem Kapitel.)

Entwicklern bietet Android nahezu grenzenlose Möglichkeiten, mit denen Sie daher vorsichtig umgehen sollten. Bilden Sie sich selbst ein möglichst objektives Urteil, bevor Sie Ihre Apps veröffentlichen und der Masse zugänglich machen. Dass Ihnen als Hintergrundmotiv dieses Filmchen gefällt, in dem Sie auf Ihrer Geburtstagsparty den Hula tanzen, bedeutet nicht zwangsläufig, dass andere das auch sehen wollen oder sehen sollten.

Denken Sie auch an die Gesetze zum Schutz des Persönlichkeitsrechts, bevor Sie Kontaktdaten von Anwendern sammeln und für eigene Vermarktungszwecke nutzen.

Grundlagen der Android-Programmierung

Sie müssen nicht Mitglied des Mensa-Clubs sein, um Android-Apps programmieren zu können. Dass Java als Standardsprache dient, erleichtert die Android-Programmierung. Aber auch wenn sich Android-Programme relativ leicht schreiben lassen, kann deren Entwicklung allein doch bereits eine schwierig zu bewältigende Aufgabe sein.

Wenn Sie bisher noch nie Apps entwickelt haben, bildet dieses Buch vielleicht nicht gerade den besten Einstieg. Zum Erlernen der Grundlagen empfehle ich Ihnen das Buch Java für Dummies von Barry Burd (Wiley-VCH). Wenn Sie erst einmal die Java-Grundlagen beherrschen, sollten Sie auf das vorliegende Buch besser vorbereitet sein.

Auch wenn Java bei Android im Vordergrund steht, trifft dies auf einige kleinere Teile des Frameworks nicht zu. Android nutzt auch XML (eXtensible Markup Language) und die einfache Skriptsprache Apache Ant beim Erstellen von Programmen. Daher sollten Ihnen für das vorliegende Buch auch die XML-Grundlagen halbwegs vertraut sein.

Wenn Sie mit Java und XML bereits vertraut sind, Glückwunsch, denn dann sind Sie bereits auf bestem Wege!

Java: Ihre Android-Programmiersprache

Android-Apps werden in Java geschrieben. Dabei handelt es sich zwar nicht um das ausgewachsene Java, an das die Entwickler für die Java-Unternehmensplattform (J2EE – Java Platform, Enterprise Edition) gewöhnt sind, aber doch um eine für Android spezifische JavaUntermenge, die gewisse Klassen ausspart, die für Mobilgeräte sinnlos sind. Wenn Sie bereits Erfahrungen in Java gesammelt haben, sollten Sie sich im Bereich der App-Entwicklung für Android direkt heimisch fühlen.

Selbst wenn Sie ein Java-Referenzhandbuch griffbereit halten, können Sie, wenn Sie bestimmte Befehle nicht ganz verstehen, immer noch danach googeln. Da Java keineswegs neu ist, finden Sie für nahezu alle Einsatzzwecke eine Menge Beispiele im Web.

Nicht alle Klassen, die Java-Programmierer nutzen können, sind auch unter Android verfügbar. Prüfen Sie vor deren Nutzung, ob sie bereitstehen. Falls nicht, gibt es aber wahrscheinlich für Ihre Zwecke passende Alternativen in den Android-APIs.

Activities

Android-Apps bestehen aus einer oder mehreren Activities. Ihre Android-App muss mindestens eine Activity enthalten, kann aber auch mehrere umfassen. Eine Activity ist eine Art Container für Teile der Benutzeroberfläche und den für deren Ausführung benötigten Code. Sie können sich Aktivitäten als einzelne Seiten Ihrer App vorstellen – eine Seite entspricht einer Activity. Activities werden in den Kapiteln 3 und 5 ausführlicher behandelt.

Intents

Intents bilden den Kern des Nachrichtensystems von Android. Intents bestehen aus zwei Elementen:

Aktion. Die allgemeine Aufgabe (Anzeigen, Bearbeiten, Wählen und so weiter), die nach dem Empfang eines Intents ausgeführt werden soll

Daten. Die Daten, die von einer Aktion verarbeitet werden sollen, wie beispielsweise der Name eines Kontakts

Intents werden zum Starten von Aktivitäten und zur Kommunikation zwischen den verschiedenen Teilen des Android-Systems genutzt. Applikationen können Intents entweder senden oder empfangen.

Messages mit Intents versenden

Wenn Sie einen Intent senden, handelt es sich dabei um eine Message (Systemnachricht), die Android mitteilt, dass es etwas geschehen lassen soll. Ein Intent könnte Android aus Ihrer App heraus zum Starten einer neuen Activity oder einer anderen App bewegen.

Intent-Receiver registrieren

Nur weil Sie eine Message versenden, muss aber noch lange nicht automatisch etwas passieren. Sie müssen einen Intent-Receiver (Empfänger) registrieren, der auf die Intents wartet und Android dann mitteilt, was es tun soll und ob der Task eine neue Activity oder eine andere App starten will. Wenn mehrere Empfänger (Receiver) mit einem bestimmten Intent etwas anzufangen wissen, kann ein Chooser (Auswahlmenü) erstellt werden, über den der Anwender auswählen kann, mit welcher App eine Activity weiter verarbeitet werden soll. Ein klassisches Beispiel ist ein Lang-Druck auf ein Bild in einer Fotogalerie. Dabei halten Sie Ihren Finger längere Zeit auf ein Objekt auf dem Bildschirm gedrückt, um dessen Kontextmenü anzeigen zu lassen.

Standardmäßig wissen verschiedene registrierte Receiver etwas mit Intents zur Weiterverarbeitung von Bildern anzufangen. E-Mail- und Messaging-Apps sind hier nur zwei Beispiele unter vielen. Wenn es mehrere mögliche Intent-Receiver gibt, wird dem Anwender ein Auswahlmenü angezeigt, in dem er gefragt wird, wie weiter vorgegangen werden soll (siehe Abbildung 1.1).

Wenn das Android-System keinen passenden Empfänger für einen gesendeten Intent findet und manuell kein Chooser erzeugt wurde, stürzt die App zur Laufzeit mit einem Ausnahmefehler (run-time exception) ab und es kommt zu einem nicht behebbaren Fehler in der App. Android erwartet von Entwicklern, dass sie wissen, was sie tun. Wenn Sie einen Intent versenden, mit dem das Android-Gerät eines Anwenders nichts anzufangen weiß, stürzt es ab. Deshalb sollten Sie für Intents, die nicht für andere Aktivitäten innerhalb Ihrer App bestimmt sind, möglichst immer Chooser erstellen.

Abbildung 1.1: Ein Auswahlmenü (Chooser)

Cursorlose Steuerelemente

Anders als beim Personal Computer, bei dem Sie einen Zeiger mit der Maus über den Bildschirm schubsen, ersetzen Ihre Finger bei Android-Geräten nahezu alle Funktionen einer Maus. Und was ist mit dem Rechtsklick? Bei Android wird der Rechtsklick durch den Lang-Druck ersetzt. Wenn Sie Ihren Finger über längere Zeit hinweg auf eine Schaltfläche (auch Knopf für Linux-Anwender), ein Symbol oder den Bildschirm drücken, wird ein Kontextmenü angezeigt.

Als Entwickler können Sie Kontextmenüs erstellen und ändern. Zudem können Sie bei Android-Geräten beispielsweise zwei Finger anstelle nur eines Mauszeigers verwenden. Denken Sie aber daran, dass Finger unterschiedlich groß sind, und entwerfen Sie Ihre Benutzeroberflächen entsprechend. Die Schaltflächen sollten groß genug sein und ausreichend Abstand untereinander haben, damit selbst Anwender mit riesigen Pranken Ihre Apps auf kleinen Telefonen und Tablets leicht bedienen können.

Views

Was ist denn das nun schon wieder? Bei einer View handelt es sich um ein Grundelement der Benutzeroberfläche, einen rechteckigen Bereich auf dem Bildschirm, der Objekte anzeigt und Ereignisse verarbeitet. Views sind also einfache Steuerelemente, wie zum Beispiel ein Label in HTML. Einige Beispiele für Views, die in Android-Apps verwendet werden können, sind TextView, ImageView, Layout und Button.

Es gibt aber Dutzende weitere Views, mit denen Sie sich eingehender befassen und die Sie nutzen können. Einzelheiten dazu finden Sie bei den Paketen android.widget und android.view in der Android-Dokumentation unter http://developer.android.com/reference/android/widget/packagesummary.html.

Asynchrone Aufrufe

Mit der Klasse AsyncTask können Sie in Android mehrere Operationen gleichzeitig ausführen lassen, ohne selbst Threads verwalten zu müssen. Mit der Klasse AsyncTask können Sie nicht nur einen neuen Prozess starten, ohne sich selbst nach dessen Ausführung um die Aufräumarbeiten kümmern zu müssen, sondern auch der Activity, die ihn gestartet hat, dessen Ergebnisse übergeben. Damit steht Ihnen ein sauberes Programmiermodell für die asynchrone Verarbeitung zur Verfügung. Wir werden in diesem Buch zwar generell Loader und nicht AsyncTask verwenden, aber es ist dennoch nützlich, wenn Sie die Klasse AsyncTask kennen und wissen, dass Sie sie nutzen können, wenn Loader nicht zum gewünschten Ergebnis führt.

Ein Thread ist ein zwar separat von, aber simultan mit allen anderen Vorgängen ablaufender Prozess.

Die asynchrone Verarbeitung sollten Sie für Tasks nutzen, deren Ausführung länger als nur ein paar Sekundenbruchteile dauert, also beispielsweise für die Netzwerkkommunikation (Internet) oder das Lesen/Schreiben von Daten auf Speichermedien und insbesondere AV-Daten (Audio/Video). Sie sollten also immer auf die asynchrone Verarbeitung zurückgreifen, wenn Anwender möglicherweise warten müssen. Neben den asynchronen Aufrufen sollten Sie dann aber auch mit irgendwelchen Bedienelementen arbeiten, die den Anwender über die aktuellen Vorgänge informieren.

Wenn Sie kein asynchrones Programmiermodell nutzen, könnten Anwender Ihre App für fehlerhaft halten. Es dauert beispielsweise ein wenig, die letzten Twitter-Mitteilungen aus dem Internet herunterzuladen. Wenn das Netzwerk stark ausgelastet ist und Ihre App nicht asynchron arbeitet, hängt sie scheinbar, reagiert nicht mehr auf Eingaben, woraufhin Anwender meinen könnten, dass ein Fehler aufgetreten ist. Wenn eine App nicht innerhalb eines vom Android-Betriebssystem definierten angemessenen Zeitrahmens reagiert, zeigt es ein Dialogfeld an und informiert den Benutzer darüber, dass die App nicht mehr reagiert (siehe Abbildung 1.2). Dann kann der Anwender im dargestellten Beispiel einer (möglicherweise) beschädigten PDF-Datei entscheiden, ob er warten, die App zwangsweise schließen oder gegebenenfalls einen Bericht an den Programmanbieter senden will.

Abbildung 1.2: Hier wird gemeldet, dass eine App nicht mehr reagiert.

Am besten betten Sie CPU-lastigen oder länger laufenden Code in einen anderen Thread ein. Die Vorgehensweise wird auf der Seite Designing for Responsiveness auf der Website für Android-Entwickler unter http://developer.android.com/guide/practices/design/responsiveness.html beschrieben.

Hintergrunddienste

Als Windows-Benutzer werden Sie möglicherweise bereits wissen, was ein Dienst (Service) ist, nämlich eine im Hintergrund laufende Anwendung, die nicht notwendigerweise eine Benutzeroberfläche besitzen muss. Klassische Beispiele sind die üblicherweise als Hintergrunddienst laufenden Antivirenprogramme. Auch wenn sie auf dem Bildschirm unsichtbar bleiben, wissen Sie doch, dass sie laufen.

Auch die meisten erhältlichen Audioplayer können Sie als Hintergrunddienst laufen lassen. Auf diese Weise können Sie beim Abruf Ihrer E-Mails oder der Erledigung anderer Aufgaben, für die der Bildschirm benötigt wird, weiter Musik hören.

Funktionen von Honeycomb, Ice Cream Sandwich und Jelly Bean

Bei Android 3.0 (Codename Honeycomb) handelte es sich um die erste Version des Betriebssystems, die spezielle Funktionen für Tablets enthielt. Honeycomb und seine Nachfolgeversionen 3.1 und 3.2 brachten eine Reihe von Änderungen zur Unterstützung dieser neuen Geräteklasse mit sich. Mit Android 4.0 (Ice Cream Sandwich) und 4.1 (Jelly Bean) wurden die mit Honeycomb vorgestellten Änderungen für Tablets verfeinert und auch auf Telefone übertragen. Damit können Entwickler denselben Code verwenden und Telefone und Tablets über eine einzige Codebasis unterstützen.

Im Oktober 2012 wurde die Vorstellung der Android-Version 4.2, die ein wenig überraschend weiterhin unter dem Codenamen »Jelly Bean« firmiert, mit dem »Frankenstorm Sandy« ein wenig vom Winde verweht.

In den folgenden Abschnitten werde ich Ihnen einige der Funktionen dieser Versionen kurz vorstellen, auf die ich im weiteren Verlauf des Buches überwiegend noch näher eingehen werde.

Fragmente

Bei den einzelnen »Seiten« einer Android-App handelt es sich um eine eigenständige Activity. In älteren Android-Versionen würden Sie beliebige, auf dem Bildschirm darzustellende Elemente direkt der Activity-Klasse zuordnen. Diese Vorgehensweise funktionierte zufriedenstellend bei kleinen Telefonbildschirmen, auf denen typischerweise nicht sonderlich viele Informationen gleichzeitig dargestellt werden können. Darauf lässt sich vielleicht eine Liste von Aufgaben oder ein zu bearbeitender Text anzeigen, beide Elemente gleichzeitig auf diesen kleinen Bildschirmen darzustellen, ist aber unmöglich.

Auf den Bildschirmen von Tablets haben Sie hingegen massig Platz. Hier ist es nicht nur sinnvoll, Benutzern eine Aufgabenliste auf dem Bildschirm anzuzeigen und sie auf derselben Seite zu bearbeiten, es würde vielmehr sogar recht umständlich wirken, wenn man anders vorgehen würde. Die Bildschirme der meisten Tablets bieten einfach zu viel Platz, um ihn mit einzelnen langen Listen oder einer Menge Leerraum zu füllen.

Unter Android lassen sich zwei Aktivitäten nicht so einfach gleichzeitig auf den Bildschirm bringen. Was soll man also machen? Fragmente bieten die Lösung.

Wenn Sie Fragmente benutzen, kann eine Liste die eine Hälfte des Bildschirms belegen, während der Bearbeitungsbereich die andere Hälfte benutzt. Wie Sie Fragmente in Ihrer Telefonanwendung nutzen können, werden Sie in Kapitel 9 erfahren. Wie Sie bei der Skalierung Ihrer App für Tablets vorgehen können, erfahren Sie dann in Kapitel 17.

Fragmente können Sie sich als Miniaktivitäten vorstellen: Da alle Fragmente einen eigenen Lebenszyklus besitzen, wissen Sie unter anderem, wann sie erzeugt und zerstört werden. Fragmente werden in Aktivitäten eingebettet.

Loader

Fragmente werden häufig für die Anzeige von Daten für den Benutzer verwendet. Sie können beispielsweise einige Aufgaben als Liste darstellen, die aus einer Datenbank geladen wird. Dabei ist es jedoch wichtig, dass Sie Ein-/Ausgabeoperationen nie im Hauptthread der Benutzerschnittstelle durchführen. Wenn Sie diesen Thread für Datenbankoperationen nutzen würden, bekäme der Anwender das in Abbildung 1.2 dargestellte (gefürchtete) ANR-Dialogfeld (Application Not Responding – Anwendung reagiert nicht) präsentiert, das nur stört und irritiert und von vielen Anwendern mit einem Systemabsturz gleichgesetzt wird.

Loader bieten Ihnen einfache Möglichkeiten, um Daten in einem Hintergrund-Thread laden zu können, die den Thread für die Benutzeroberfläche (UI – User Interface) nicht beeinträchtigen und mit denen sich (scheinbare) Hänger vermeiden lassen. Mehr über Loader erfahren Sie in Kapitel 10.

Android Support Library

Fragmente und Loader bieten effektive Lösungsansätze für Apps unter Android 3.x und 4.x mit gesteigertem Nutzwert. Wenn Sie aber auch Geräte unterstützen wollen, die noch unter Android 2.x oder gar 1.x laufen, stehen Sie vor dem Problem, dass diese neuen Funktionen hier nicht unterstützt werden.

Glücklicherweise bietet Android aber auch hier eine Lösung. Sie können nämlich die Android Support Library benutzen, um Fragmente und Loader mit allen Geräten bis zurück in die Android-Steinzeit (etwa 2009 nach Christus) kompatibel zu machen.

Diese Hilfsbibliotheken unterstützen nicht nur Fragmente und Loader, sondern erweitern die alten Geräte auch um einige weitere hervorragende Funktionen wie:

ViewPager: Seiten nach links und rechts wischen

GridLayout: Eine neue Möglichkeit für das Layout von Views

ShareCompat: Für gemeinsame Aktivitäten mit Freunden

Eine vollständige Liste der Funktionen der Android Support Library finden Sie unter http://developer.android.com/tools/extras/supportlibrary.html.

Aktionsleiste

Der Menü-Schalter ist ein wichtiger Bestandteil nahezu aller Anwendungen unter Android 1.x oder 2.x. Android-Telefone besitzen (im Unterschied zu gewissen anderen beliebten Smartphones) einen physischen Menü-Schalter, über den über den Bildschirm ansonsten nicht zugängliche Funktionen erreichbar werden.

Nun ja, früher gab es diesen Schalter jedenfalls einmal bei allen Android-Telefonen. Mit Android 3.0 hat man sich von diesem Konzept verabschiedet. Zwar gibt es diesen Schalter noch bei einigen Geräten wie dem Samsung Galaxy S III, ansonsten handelt es sich dabei aber um ein Relikt der Vergangenheit. Allgemein ausgedrückt, waren Elemente im Android nicht gerade leicht zu finden und viele Benutzer neigen sogar dazu, deren Existenz völlig zu übersehen.

Anstelle des Menüs wird bei Geräten, die Android ab Version 3.x verwenden, am oberen Bildschirmrand beinahe ständig die Aktionsleiste (action bar) angezeigt, die sich nur schwer übersehen lässt. Abbildung 1.3 zeigt ein Beispiel für die Aktionsleiste der YouTube-App.

Abbildung 1.3: Die YouTube-Aktionsleiste für ein Android-Demovideo

Beachten Sie diese Elemente in der Aktionsleiste:

Aufwärts-Schaltfläche, App-Logo: Tippen Sie das App-Logo in der Aktionsleiste an, um eine Ebene aufwärtszugehen.

Beachten Sie den feinen Unterschied zwischen der Aufwärts- und der Zurück-Schaltfläche. Über die Zurück-Schaltfläche kehrt der Benutzer zur vorherigen Aktivität zurück, wobei die benutzte App keine Rolle spielt. Die Aufwärts-Schaltfläche bringt den Benutzer hingegen zurück zur letzten Aktivität in der aktuellen Anwendung, selbst wenn es sich bei dieser nicht um die Aktivität handelt, die er zuletzt genutzt hat.

Angenommen, Sie würden eine Webseite im Chrome-Browser betrachten und einen Link antippen, um die YouTube-App zu starten. Wenn Sie dann die Zurück-Schaltfläche antippen, kehren Sie damit zu Chrome zurück. Über die Aufwärts-Schaltfläche gelangen Sie hingegen zur Startseite der YouTube-App.

Seitentitel: Neben dem Symbol der Anwendung wird der Titel der aktuellen Seite in der Aktionsleiste angezeigt. Wenn sich auf der aktuellen Seite Ihrer App die Daten irgendwie filtern lassen, können Sie dort ein Dropdown-Menü einfügen, über das sich der Filter wechseln lässt (siehe Abbildung 1.4).

Registerkarten: Sie können (anstelle des Seitentitels) Registerkarten in die Aktionsleiste aufnehmen, zwischen denen Benutzer in der aktuellen Aktivität umschalten können.

Aktion: Am rechten Ende der Aktionsleiste können Sie verschiedene Aktionen sehen, die Benutzer ausführen können. In der YouTube-App in Abbildung 1.3 können Benutzer Videos zu einer Liste hinzufügen, sie weiterempfehlen oder nach weiteren Videos suchen. Für die Aktionsschaltflächen können Texte und/oder Symbole angezeigt werden. Sie können beliebig viele Aktionen hinzufügen. Aktionen, für die der Platz auf dem Bildschirm nicht mehr ausreicht, werden ganz rechts in ein Untermenü ausgelagert (siehe Abbildung 1.3).

Kontext-Aktionsleiste: Das Aussehen der Aktionsleiste kann geändert und an die jeweiligen Aktivitäten der Benutzer angepasst werden. Werden beispielsweise mehrere Elemente in einer Liste ausgewählt, können Sie die Standard-Aktionsleiste durch eine kontextbezogene Aktionsleiste ersetzen, um Benutzern die Auswahl auf der Grundlage dieser Elemente zu ermöglichen. Wenn beispielsweise mehrere Dateien gleichzeitig gelöscht werden können sollen, können Sie eine Löschen-Schaltfläche in die Kontext-Aktionsleiste aufnehmen.

Unter http://developer.android.com/guide/topics/ui/actionbar.html finden Sie weitere Informationen über die vielseitigen Möglichkeiten, die dieses Element der Benutzerschnittstelle in Ihren Apps bieten kann.

Abbildung 1.4: Audio-App mit Menü in der Aktionsleiste

Unter Android 2.x und in noch älteren Versionen existiert die Aktionsleiste schlichtweg nicht! Und sie wird auch nicht von der Support Library bereitgestellt. In diesen Android-Versionen werden zu Ihren Apps hinzugefügte Aktionsleisten nicht angezeigt. So schlimm ist das aber auch wieder nicht, da die in der Aktionsleiste untergebrachten Aktionen bei diesen Geräten immer noch unter der Menü-Schaltfläche angezeigt werden und dort für die Benutzer erreichbar bleiben.

Wenn Sie auch in Apps unter älteren Android-Versionen (einem älteren Telefon oder dem Kindle Fire beispielsweise) eine Aktionsleiste unterbringen wollen, sollten Sie sich einmal ActionBarSherlock ansehen (http://actionbarsher lock.com).

Holo

Mit Android 3.0 kommen drei holografische Themen hinzu, mit denen sich ansehnliche Android-Apps gestalten lassen:

Holo dunkel

Holo hell

Holo hell mit dunklen Aktionsleisten

Vielleicht dauert es eine Weile, bis man sich an diese etwas dunkleren Farbschemas gewöhnt hat, dann erweisen sie sich aber als sehr viel sauberere und konsistentere Lösungen als die von Android 2.x her bekannten Farbschemas. Die Holo-Themen wirken auch weniger überladen und lassen Ihren Apps mehr Platz für die Anzeige der wirklich wichtigen Informationen.

Der größte Vorteil von Holo ist aber zu guter Letzt dessen Konsistenz über die vielen verschiedenen Android-Geräte und deren Hersteller hinweg. Hersteller können das Holo-Thema nicht modifizieren, damit es unter ihren Android-Varianten anders aussieht.

Widgets, Benachrichtigungen, Leistung und mehr

Die Liste der neuen Funktionen von Android 3.x und 4.x ist scheinbar endlos. Einige weitere hervorragende Neuerungen sollen hier aber noch kurz erwähnt werden:

Widgets: Widgets wurden in den jüngeren Android-Versionen stark verbessert. Da sie nun in die Anwendungsliste verschoben wurden, lassen sie sich leichter finden. Sie können Widgets sogar um Listenansichten erweitern, um so Wischen und Scrollen in beschränktem Umfang zu ermöglichen, und deren Größe ändern und sie mehr oder weniger Platz belegen lassen. Tatsächlich ändern diese benutzerfreundlichen Widgets automatisch ihre Größe, wenn sie auf dem Bildschirm herumgezogen werden. Durch diese Änderungen wirken sie sehr viel variabler und ansprechender als in den älteren Android-Versionen.

Benachrichtigungen: Android 4.1 verleiht dem bisher eher biederen Android-Benachrichtigungssystem den gewissen Pep. Da sich Benachrichtigungen nun erweitern und verkleinern lassen, können Benutzern mehr Informationen darin angezeigt werden. Wenn Ihnen Ihre Mutter beispielsweise ein Foto ihres neuen Welpen in einer Textnachricht zuschickt, lässt es sich direkt in der Benachrichtigung anzeigen, ohne dass dazu erst die App geöffnet werden müsste. In einer Benachrichtigung über eine neue E-Mail-Nachricht kann eine Vorschau des Nachrichtentextes angezeigt werden, so dass er direkt gelesen werden kann.

Zudem können Benutzer nun unabhängig von der benutzten App direkt auf Benachrichtigungen reagieren. Um beispielsweise auf eine Geburtstags-E-Mail Ihrer Oma zu antworten, müssen Sie nur noch »Antworten« in der Benachrichtigung antippen, um Gmail samt Editor zu starten und sich bei ihr bedanken zu können.

Leistung: Mit Android 4.1 gehen deutliche Leistungssteigerungen der Plattform einher. Sie müssen weiter nichts tun, um von einer schneller und reibungsloser reagierenden Schnittstelle in Ihren Apps zu profitieren. Diese laufen auf Geräten mit Android ab Version 4.1 einfach besser.

Mehrbenutzerverwaltung: Ab der Version 4.2 von Android können sich mehrere Benutzer besser ein Gerät teilen. Nun lässt sich zwischen verschiedenen Nutzerkonten umschalten, wobei die einzelnen Benutzer jeweils nur auf ihre eigenen Daten zugreifen können.

Hardwarefunktionen

Google hat eine Unmenge an Funktionen in Android integriert und stellt (selbst unabhängigen) Entwicklern alle benötigten Werkzeuge zur Verfügung, um hervorragende und umfassende Mobilgeräte-Apps entwickeln zu können. Google macht es Ihnen leicht, die Funktionen der verfügbaren Hardwarekomponenten zu nutzen.

Um eine spektakuläre Android-App zu entwickeln, sollten Sie die Vorteile der verfügbaren Hardwarekomponenten nutzen. Aber verstehen Sie mich hier nicht falsch: Wenn Sie eine Idee für eine App haben, die ohne Hardwareunterstützung auskommt, dann ist auch das völlig in Ordnung.

Android-Geräte sind mit verschiedenen Hardwarekomponenten ausgestattet, die Sie im Rahmen Ihrer Apps nutzen können und die in Tabelle 1.1 im Überblick dargestellt werden.

Hardwarekomponente | Funktion
Beschleunigungssensor | Stellt Bewegungen des Telefons fest
Bluetooth-Funk | Ermöglicht beispielsweise den Anschluss eines Bluetooth-Kopfhörers oder einer externen Tastatur
Integrierter Kompass | Stellt fest, in welche Richtung das Gerät zeigt
Kamera | Aufnahme von Fotos oder Videoclips
GPS-Funk | Ermittelt, wo sich das Gerät befindet
Lagesensor | Stellt die Lage des Geräts im Raum fest (zeigt der Bildschirm nach oben?)

Tabelle 1.1: Android-Gerätehardware

Die meisten Android-Geräte sind zwar mit den in den folgenden Abschnitten beschriebenen Komponenten ausgestattet, deren Leistungsdaten sich aber zum Teil doch recht erheblich unterscheiden. Da Android von Hardwareherstellern kostenlos vertrieben werden darf, wird es für eine breite Gerätepalette genutzt. Bei kleineren Herstellern kann daher bei den Geräten schon einmal die eine oder andere Komponente fehlen oder auch zusätzlich hinzukommen.

Android-Geräte sind in den verschiedensten Formen und Abmessungen erhältlich, wie zum Beispiel als Mobiltelefon, Tablet-PC und E-Book-Reader. Es gibt aber noch viele weitere mögliche Android-Implementierungen, wie zum Beispiel Google TV (für Fernseher mit Internetanschluss) oder für Kraftfahrzeuge mit eingebauten Touchscreen-Geräten. Die Android-Entwickler stellen Werkzeuge bereit, mit denen Sie leicht Apps für unterschiedlich dimensionierte und auflösende Bildschirme erstellen können. Und da Ihnen die Android-Entwickler bereits die wirklich schweren Aufgaben abnehmen, brauchen Sie sich auch in dieser Hinsicht keine Sorgen zu machen. Die Grundlagen der Unterstützung verschiedener Bildschirmabmessungen und Auflösungen finden Sie in Kapitel 4.

Touchscreen

Android-Mobiltelefone werden über Touchscreens bedient, die weitreichende Möglichkeiten zur Verbesserung der Bedienung der Apps bieten. Anwender können beispielsweise durch Antippen, Drehen, Ziehen, Wischen und Drücken mit einem oder mehreren Fingern ein Bild auswählen und vergrößern. Entwickler können in Apps sogar angepasste Gesten definieren, um sich noch mehr Möglichkeiten zu erschließen.

Android unterstützt auch Multitouch-Gesten, bei denen der gesamte Bildschirm gleichzeitig mit mehreren Fingern berührt wird.

Echte Schalter an Geräten sind nichts Neues. An vielen Android-Geräten (insbesondere Tablets) finden Sie aber neben einem Standby-Schalter nur noch einen Lautstärkeregler. Wenn Sie eine geeignete Benutzeroberfläche für Ihre App erstellen, können Sie beliebig aussehende Schaltflächen irgendwo auf dem Bildschirm positionieren.

GPS

Wenn das Android-Betriebssystem mit dem GPS-Funk eines Geräts zusammenarbeitet, können entsprechend entwickelte Apps jederzeit den aktuellen Standort eines Anwenders feststellen. Sie können auch dessen Bewegungen und Ortswechsel aufzeichnen.

Ein weiteres nützliches Beispiel stellen Landkarten, Routenplaner oder ausgewachsene Navigationslösungen dar, die die aktuelle Position nebst Wegbeschreibung zu einem angegebenen Ziel anzeigen. Mit Android und der GPS-Hardware können Sie auf einige Meter genau die jeweilige Geräteposition feststellen. Viele Apps nutzen diese Funktion, um angeben zu können, wo sich Tankstellen, Restaurants, Parkhäuser, öffentliche Toiletten oder andere interessante Einrichtungen befinden (Abbildung 1.5).

Beschleunigungssensor

Android unterstützt einen Beschleunigungssensor (Accelerometer), mit dem ermittelt werden kann, ob das Gerät bewegt oder geschüttelt und in welche Richtung es gedreht wird. Diese Informationen können Sie zur Steuerung Ihrer Apps nutzen.

Damit lassen sich beispielsweise Fragen beantworten, wie die, ob der Bildschirm gerade auf dem Kopf steht und Ihre App darauf reagieren sollte. Oder vielleicht wollen Sie ein Würfelspiel entwickeln, bei dem Ihre Anwender ihr Telefon zum Wurf schütteln müssen. Durch derartige Funktionen heben sich Mobilgeräte von typischen Desktop-PCs ab.

Abbildung 1.5: Android-Navigationslösung mit Anzeigeoptionen

SD-Speicherkarte

Android sorgt für alle für den Zugriff (Speichern und Laden von Dateien) auf die SD-Speicherkarte erforderlichen Funktionen. Bei vielen Mobiltelefonen und Computern lassen sich SD-Speicherkarten einsetzen und zur Speicherung von Daten nutzen. Wenn ein Gerät mit einer SD-Speicherkarte ausgerüstet ist, können Sie darauf alle von Ihrer App benötigten Dateien speichern und darauf zugreifen. Seit Android 2.2 (Froyo) können Sie auch die von Apps benötigten Daten auf SD-Karten speichern. Um den internen Gerätespeicher nicht unnötig für benötigte Zusatzressourcen zu verschwenden, können Sie diese oft auch ganz oder teilweise von einem Webhost herunterladen und auf SD-Karte speichern. Benutzer finden dies vielleicht besser und werden Ihre App dann nicht so schnell deinstallieren, um Speicherplatz für andere Apps freizumachen.

SD-Speicherkarten sind zwar bei vielen, aber längst nicht allen Geräten auch wirklich eingelegt. Bevor Sie in Ihren Apps Daten auf SD-Karte zu speichern versuchen, sollten Sie daher zunächst immer erst einmal prüfen, ob eine SD-Karte installiert ist und ob darauf noch genug Platz für die zu speichernden Dateien vorhanden ist.

Softwarewerkzeuge

Beim Schreiben von Android-Apps stehen Ihnen verschiedene Werkzeuge zur Verfügung. In den folgenden Abschnitten werde ich Ihnen einige der beliebtesten Tools kurz vorstellen, die Sie im Rahmen Ihrer alltäglichen Android-Entwicklungsarbeit nutzen werden.

Internet

Dank der Internetfähigkeit von Android-Geräten lassen sich aktuelle Daten leicht abrufen. Als Anwender können Sie das Internet nutzen, um sich über die Anfangszeit des nächsten Films oder die Ankunftszeit der nächsten Straßenbahn zu informieren. Als Entwickler können Sie das Internet in Ihren Apps für den Zugriff auf aktuelle Wetterdaten, Nachrichten und Sportergebnisse nutzen. Sie können das Web aber (wie Pandora und YouTube) auch dazu benutzen, um dort die Symbole und Abbildungen Ihrer Apps zu speichern.

Lassen Sie es nicht damit bewenden. Sofern dies sinnvoll ist, können Sie auch Teile Ihrer App auf einen Webserver auslagern! Dadurch lässt sich manchmal sogar viel Verarbeitungszeit einsparen und dafür sorgen, dass Ihre Android-App schlank bleibt. Diese Vorgehensweise wird Client-Server-Computing genannt. Bei dieser verbreiteten Softwarearchitektur richtet der Client Anforderungen an einen Server, der nur darauf wartet, etwas zu tun zu bekommen. Die integrierte Maps-App ist ein gutes Beispiel für eine Anwendung, bei der ein Client auf die auf einem Webserver abgelegten Karten und GPS-Daten zurückgreift.

Audio- und Videounterstützung

Mit dem Android-Betriebssystem lässt sich in Ihren Apps auch Audio und Video spielend leicht nutzen. Dabei werden viele Standardformate unterstützt. Das Einbinden multimedialer Inhalte in Ihre Apps könnte kaum einfacher sein. Toneffekte, Anleitungsvideos, Hintergrundmusik, Videostreams und Audio aus dem Internet lassen sich problemlos zu Ihren Apps hinzufügen. Lassen Sie Ihrer Kreativität freien Lauf. Ihren Möglichkeiten sind kaum Grenzen gesetzt.

Kontakte

Ihre App kann auf die auf dem Gerät gespeicherten Kontaktdaten zugreifen. Diese Funktion können Sie nutzen, um Kontakte auf neue Weise oder einfach anders anzuzeigen. Vielleicht gefällt Ihnen die bereits vorhandene App zur Verwaltung der Kontakte nicht. Da Sie auf die auf dem Gerät gespeicherten Kontakte zurückgreifen können, hält Sie nichts davon ab, sich Ihre eigene App zu schreiben. Vielleicht kombiniert diese ja auch Ihre Kontakte mit dem GPS-System und benachrichtigt den Anwender, wenn er sich in der Nähe einer der Kontaktadressen befindet.

Nutzen Sie Ihre Fantasie, gehen Sie dabei aber verantwortungsvoll vor. Keinesfalls sollten Sie Kontaktdaten missbrauchen (siehe nächster Abschnitt).

Sicherheit

Stellen Sie sich nur einmal vor, dass jemand eine App veröffentlicht, die Kontaktlisten durchsucht und sie komplett irgendwo auf einen Server überträgt, um die Daten dann für eigene Zwecke zu missbrauchen. Nun sollte Ihnen klar sein, warum den meisten Funktionen, die die Gerätekonfiguration ändern oder auf geschützte Inhalte zugreifen, speziell entsprechende Berechtigungen eingeräumt werden müssen. Angenommen, Sie wollen ein Bild aus dem Web herunterladen und auf der SD-Karte speichern. Dazu benötigen Sie eine Berechtigung für den Zugriff auf das Internet. Sie benötigen auch entsprechende Berechtigungen, um Dateien auf der SD-Karte speichern zu können. Zu Beginn der Installation einer App wird deren Benutzer darüber informiert, welche Berechtigungen sie erfordert. Dann kann er entscheiden, ob die Installation fortgesetzt werden soll. Um eine Berechtigung anzufordern, müssen Sie nur eine Zeile Code in die Manifest-Datei Ihrer App einfügen. (Manifest-Dateien werden in Kapitel 3 beschrieben.)

Google-APIs

Das Android-Betriebssystem lässt Sie nicht nur telefonieren, Kontakte verwalten oder Apps installieren. Es bietet weit mehr. Als Entwickler bieten sich Ihnen weitreichende Möglichkeiten. Sie können beispielsweise sogar Landkarten in Ihre Apps integrieren. Dazu müssen Sie nur das Maps-API nutzen, das die entsprechenden Widgets bereitstellt.

Positionen in Landkarten einzeichnen

Vielleicht wollen Sie eine App schreiben, die Ihren Freunden Ihren aktuellen Aufenthaltsort mitteilt. Mit der Entwicklung eines Kartensystems könnten Sie entweder Hunderte Stunden Entwicklungszeit verbringen – oder einfach das Maps-API von Android nutzen. Google stellt das Android Maps-API bereit, das Sie wie die anderen Android-Funktionen in Ihren Apps nutzen können. Sie können das API einbinden und sich so Hunderte Stunden an Entwicklungszeit sparen, ohne dass es Sie auch nur einen einzigen Cent kosten würde. Mit dem Maps-API können Sie fast alles nur über eine Adresse finden. Die Möglichkeiten sind schier grenzenlos. Zeigen Sie den Aufenthaltsort Ihrer Freundin, die nächste Bäckerei, die nächste Tankstelle oder andere Dinge über deren Adresse an.

Es ist zwar nett, Ihre Freunde über Ihren aktuellen Aufenthaltsort zu informieren, aber das ist noch längst nicht alles! Das Maps-API von Android kann auch auf Googles Navigation-API zugreifen. Und schon können Sie Ihren Anwendern nicht nur Ihre aktuelle Position, sondern auch den Weg dorthin anzeigen.

Cloud-Messaging

Noch so ein obskurer Begriff! Nun ja, beim Cloud-Computing befinden sich die Rechner, die irgendwelche Dienste bereitstellen, irgendwo in den »Wolken« (clouds), ohne dass sich ihr entfernter Standort (im Internet) genau ausmachen ließe. Sie könnten die Daten Ihrer App beispielsweise irgendwo in der Wolke (dem Internet) speichern und die benötigten Daten beim ersten Starten Ihrer App herunterladen. Was aber, wenn diese sich später ändern? Damit die App aktualisiert werden kann, muss sie darüber benachrichtigt werden. Dazu können Sie Ihrer App beziehungsweise dem Gerät vom Server aus im Web eine Mitteilung zukommen lassen (Cloud-to-Device-Message), die dafür sorgt, dass es die aktualisierten Daten herunterlädt. Das funktioniert sogar, wenn Ihre App nicht läuft. Sobald das Gerät die Benachrichtigung erhält, sorgt es dafür, dass Ihre App gestartet wird, und leitet die entsprechenden Maßnahmen ein.

Das KISS-Prinzip

Bei der Entwicklung von Anwendungen kann man es leicht übertreiben und übermäßig komplizierte Lösungen entwickeln. Denken Sie daher an das KISS-Prinzip, das für »Keep It Simple, Stupid« steht und sinngemäß bedeutet, dass man seine Lösungen möglichst einfach halten sollte. Wenn man einfach loslegt, ohne die integrierten APIs wirklich zu verstehen und ohne zu wissen, was diese eigentlich machen, wird der eigene Code leicht übermäßig kompliziert. Sie können zwar so vorgehen, aber das könnte Sie viel mehr Zeit als das Überfliegen der Android-Dokumentation kosten. Sie müssen sich nicht alles merken, sollten sich aber selbst den Gefallen tun und einen Blick in die Dokumentation werfen. Dann wissen Sie, wie leicht Sie die bereits integrierten Funktionen nutzen und wie viel Entwicklungszeit Sie mit ihnen sparen können. Leicht können Sie viele Zeilen Code schreiben, um etwas zu machen, was sich eigentlich mit einer einzigen Zeile erledigen ließe. Die Lautstärke der Audiowiedergabe zu verändern oder ein Menü zu erstellen, ist zwar eigentlich einfach, aber wenn Sie die APIs nicht kennen, werden Sie vielleicht nur auf weitere Probleme stoßen, wenn Sie die entsprechenden Funktionen neu zu schreiben versuchen.

Durch Hinzufügen von eigentlich gar nicht benötigten Funktionen kann man eigene Projekte auch wirklich gründlich in den Sand setzen. Sie sollten möglichst die einfachsten Lösungen bevorzugen. Erstellen Sie also keine aufwendigen Dialogfelder mit Registerkarten, wenn es bereits ein paar Menüelemente tun würden. Android verfügt über genug vorgefertigte Steuerelemente (Widgets), mit denen sich nahezu alle Aufgabenstellungen bewältigen lassen. Zudem haben es Benutzer Ihrer App bei den vorgefertigten Widgets mit ihnen bereits vertrauten, geschätzten und daher leicht nutzbaren Bedienelementen zu tun.

2

Vorbereitung Ihrer Entwicklungszentrale

In diesem Kapitel

So werden Sie Android-Anwendungsentwickler

Zusammenstellung Ihres Werkzeugkastens

Das Android-SDK herunterladen und installieren

Eclipse beziehen und installieren

Mit den Android-ADT arbeiten

Die gesamte Software, die Sie zur Entwicklung von Android-Apps benötigen, ist kostenlos! Das ist das Schöne an der Entwicklung von Android-Anwendungen. Sicherlich freut es Sie zu hören, dass die für die Entwicklung umgangreicher Android-Apps benötigten Basiskomponenten, die Werkzeuge, Frameworks und selbst Quellcode kostenlos erhältlich sind. Einen kostenlosen Computer bekommen Sie zwar nicht dazu, aber Sie können damit Ihre Entwicklungsumgebung einrichten und ohne weitere Kosten und somit unschlagbar günstig mit der Entwicklung von Apps beginnen. Nun, vielleicht können Sie das jetzt noch nicht, aber Sie werden schnell dazu in der Lage sein.

In diesem Kapitel zeige ich Ihnen schrittweise, wie Sie die Werkzeuge und Frameworks installieren können, um anschließend mit der Erstellung aufsehenerregender Android-Anwendungen beginnen zu können.

Entdecken Sie den Android-Entwickler in sich

Es ist nicht schwierig, ein Android-Entwickler zu werden. Eigentlich ist es sogar viel leichter, als Sie vielleicht denken. Stellen Sie sich selbst diese Fragen, um festzustellen, was dazugehört:

Will ich Android-Apps entwickeln?

Sagen mir kostenlose Software-Entwicklungswerkzeuge zu?

Will ich lieber keine Entwicklungsgebühren bezahlen?

Besitze ich einen Rechner, den ich zur Entwicklung nutzen kann?

Wenn Sie all diese Fragen mit Ja beantworten können, ist heute Ihr Glückstag. Sie sind darauf vorbereitet, ein Android-Entwickler zu werden. Vielleicht wundern Sie sich über die Sache mit den »Gebühren«. Ja, Sie haben richtig gelesen. Als Android-Entwickler müssen Sie keine Gebühren zahlen.

Irgendeinen Haken muss es aber doch geben, oder? Sie können zwar nach Lust und Laune entwickeln, aber sobald Sie Ihre Anwendung über Google Play (hier laden Sie Ihre Apps hoch und veröffentlichen sie) anbieten wollen, müssen Sie eine geringe Registrierungsgebühr entrichten. Momentan beläuft sich diese auf 25 US-Dollar.

Wenn Sie Apps für einen Kunden entwickeln, können Sie ihm diese auch als verteilbares Paket übergeben. Ihr Auftraggeber kann die Anwendung dann über Google Play unter Verwendung seines Marktplatz-Kontos veröffentlichen. Auf diese Weise können Sie sicherstellen, dass Sie keine Gebühren für Auftragsarbeiten zahlen müssen. Selbst als echter Android-Entwickler müssen Sie dann nie Gebühren zahlen. Das ist doch wirklich toll.

Zusammenstellung Ihres Werkzeugkastens

Da Sie nun wissen, dass Sie bereit sind, ein Android-Entwickler zu werden, können Sie sich Ihren Computer schnappen und geschwind die zur Erstellung Ihrer ersten Hit-Anwendung benötigten Werkzeuge und Frameworks installieren.

Android-Quellcode

Sie sollten wissen, dass Android komplett Open Source ist. Sie dürfen die Android-Quelltexte also nicht nur kostenlos nutzen, sondern auch ändern. Es steht Ihnen frei, sich diese herunterzuladen und selbst eine neue Android-Version zu erstellen. Die offenen Quelltexte des Android-Projekts finden Sie unter http://source.android.com.

Linux-Kernel 2.6

Android basiert auf dem quelloffenen Linux-Kernel 2.6. Die Android-Entwickler haben sich für diesen Kernel entschieden, weil er bewährte Kernfunktionen bereitstellt, auf denen die Entwicklung des Android-Betriebssystems aufsetzen kann. Unter anderem bietet der Linux-Kernel 2.6 die folgenden Funktionen:

Sicherheitsmodell: Der Linux-Kernel kümmert sich um die Sicherheit zwischen den Apps und dem System.

Speicherverwaltung: Der Kernel übernimmt die Speicherverwaltung, wodurch Sie sich voll auf die Entwicklung Ihrer App konzentrieren können.

Prozessverwaltung: Der Linux-Kernel verwaltet auch die Prozesse und weist diesen bei Bedarf die benötigten Ressourcen zu.

Netzwerkstapel: Der Linux-Kernel kümmert sich auch um die Netzwerkkommunikation.

Treibermodell: Linux soll dafür sorgen, dass alles funktioniert. Hardwarehersteller können ihre Treiber in das Linux-Betriebssystem integrieren.

Einen recht guten ersten Eindruck der Funktionen von Linux 2.6 erhalten Sie über Abbildung 2.1.

Abbildung 2.1: Einige der Funktionen des Linux-Kernels

Android-Framework

Aufsetzend auf dem Linux-2.6-Kernel wurde das Android-Framework mit verschiedenen Funktionen entwickelt. Sie wurden von zahlreichen quelloffenen Projekten übernommen. Die Ergebnisse dieser Projekte führten zu folgender Zusammenstellung:

Die Android-Laufzeitumgebung: Die Android-Laufzeitumgebung besteht aus den Java-Kernbibliotheken und der Dalvik Virtual Machine (DVM).

Open GL (Grafikbibliothek): Diese sprachunabhängige und plattformübergreifende Anwendungsprogrammierschnittstelle (API – Application Program Interface) wird für die Erzeugung von 2D- und 3D-Computergrafiken genutzt.

WebKit: Diese quelloffene Webbrowser-Engine bietet Funktionen zur Anzeige von Webinhalten und vereinfacht das Laden von Seiten.

SQLite: Diese quelloffene Engine für relationale Datenbanken wurde entwickelt, um in Geräte integriert zu werden.

Media Frameworks: Diese Bibliotheken ermöglichen die Aufnahme und Wiedergabe von Audio und Video.

Secure Sockets Layer (SSL): Diese Bibliotheken sind für die Internet-Sicherheit zuständig.

Abbildung 2.2 können Sie eine Liste der verbreiteten Android-Bibliotheken entnehmen.

Abbildung 2.2: Android und andere Bibliotheken von Drittherstellern setzen auf dem Linux-2.6-Kernel auf.

Anwendungsframework

Nach dem Lesen des letzten Abschnitts werden Sie jetzt wahrscheinlich denken: »Das ist zwar schön und gut, aber was habe ich als Entwickler mit diesen Bibliotheken am Hut?« Das ist einfach, denn Sie können über Android auf all diese quelloffenen Frameworks zurückgreifen. Sie müssen sich nicht darum kümmern, wie Android mit SQLite und der Schnittstellenverwaltung zusammenarbeitet, sondern können sie einfach selbst als Android-Werkzeuge nutzen. Das Android-Team hat selbst auf einen Satz bewährter Bibliotheken zurückgegriffen und stellt sie Ihnen über Android-Schnittstellen bereit. Diese umhüllen die verschiedenen Bibliotheken und machen sie für die Android-Plattform und Entwickler nutzbar. Bei Android arbeiten all diese Bibliotheken im Hintergrund und stellen Ihnen ihre Funktionen bereit, ohne dass Sie die angebotenen Funktionen selbst programmieren müssen:

Activity Manager: Kümmert sich um die Lebensdauer der Aktivitäten.

Telephony Manager: Bietet Zugriff auf Telefoniedienste und einige Abonnentendaten, wie zum Beispiel Telefonnummern.

View System: Kümmert sich um die Steuerelemente (Views) und das Layout der Elemente, aus denen die Bedienschnittstelle (UI – User Interface) besteht.

Location Manager: Ermittelt die geografische Position des Geräts.

Abbildung 2.3 können Sie entnehmen, aus welchen Bibliothek das Anwendungsframework besteht.

Abbildung 2.3: Ein kurzer Blick auf einen Teil des Android-Anwendungsframeworks

Vom Kernel bis hin zu den Anwendungen wurde das Android-Betriebssystem mit bewährten quelloffenen Technologien entwickelt. Dadurch können Entwickler leistungsfähige Anwendungen erstellen, die gewissermaßen von der Open-Source-Gemeinde gepflegt wurden. Abbildung 2.4 zeigt eine vollständige Übersicht über die Schichten des Android-Anwendungsframeworks.

Abbildung 2.4: Die Schichten des Android-Anwendungsframeworks. Ihre Apps befinden sich in der obersten Schicht (Applications).

Bei der Entwicklung von Android-Apps werden Sie manchmal jene Ressourcen nutzen wollen, auf die auch der Android-Systemkern zurückgreift. Ein gutes Beispiel wäre ein Symbol für eine Menüoption EINSTELLUNGEN. Dazu können Sie auf die Android-Quelltexte zugreifen, die verschiedenen Ressourcen durchsuchen und die für Ihr Projekt benötigten herunterladen. Durch die zugänglichen Quelltexte können Sie auch tiefer eintauchen und genau sehen, wie Android seine Aufgaben erledigt. Bei der Übernahme dieser Ressourcen müssen Sie allerdings gewisse Google-Richtlinien einhalten, über die Sie unter http://developer.android.com/distribute/googleplay/promote/brand.html mehr erfahren können.http://developer. android.com/distribute/googleplay/promote/brand.html

OHA-Bibliotheken

OHA steht für »Open Handset Alliance«. Aber worum handelt es sich bei dieser »Allianz«? Nun, bei diesem Bündnis handelt es sich um keine allzu große Sache, die aber dennoch irgendwie cool ist. Letztlich handelt es sich um eine Art von Bündnis zwischen einigen wirklich pfiffigen Unternehmen, über das sie mit ihren Anstrengungen dieselben einheitlichen Ziele verfolgen.

Das Bündnis wurde im November 2007 angekündigt. Damals bestand die Allianz aus 34 Mitgliedern unter der Leitung von Google. Mittlerweile besteht die OHA aus über 80 Mitgliedern. Dabei handelt es sich um eine Gruppe von Technik- und Mobilfunkunternehmen (wie T-Mobile, Sprint, LG, Motorola, HTC, NVidia, Samsung, Sony Ericsson und Texas Instruments), die sich zusammengefunden haben, um die Innovationen im Mobilfunkbereich voranzutreiben. Das Ziel besteht darin, Nutzer mit leistungsfähigen, attraktiven und nützlichen Mobilgeräten zu versorgen. Mehr über diese Gruppe erfahren Sie unter www.openhandsetalliance.com.

Sie sollten die OHA kennen, weil alle Bibliotheken, aus denen das Android-Betriebssystem besteht, aus quelloffenem Code bestehen. Alle Mitglieder tragen auf eigene spezielle Weise bei. Chiphersteller sorgen dafür, dass ihre Chipsätze die Plattform unterstützen, Hardwarehersteller fertigen Geräte und andere Unternehmen steuern intellektuelles Eigentum (Code, Dokumentation und so weiter) bei. Die Zielsetzung besteht darin, Android den kommerziellen Erfolg zu sichern.

Mit ihren Beiträgen treiben die Mitglieder die Android-Plattform auch zunehmend voran. Einige dieser Innovationen wurden und werden in die Android-Quelltexte übernommen und einige bleiben, laut OHA-Beschluss, intellektuelles Eigentum der Bündnismitglieder.

Nur weil ein Gerät ein schickes Gimmick hat, muss das bei anderen nicht der Fall sein. Als Entwickler können Sie sich nur auf den Kern des Android-Frameworks verlassen. OHA-Mitglieder können zur besseren Unterstützung irgendwelcher Gerätefunktionen zwar zusätzliche Bibliotheken hinzufügen, aber niemand kann Ihnen garantieren, dass diese Bibliotheken auch auf anderen oder denselben Geräten in anderen Ländern vorhanden sind. Eine Ausnahme stellen dabei Entwicklungen für spezielle Einzelgeräte, wie zum Beispiel ein E-Book-Lesegerät dar. Wenn deren Hardware nur das Lesen elektronischer Bücher als Funktion unterstützt, können Sie es nur für diesen Zweck programmieren. Ein praktisches Beispiel für ein E-Book-Lesegerät mit Android als Betriebssystem ist der Nook von Barnes & Noble. Es besitzt spezielle Vor- und Zurück-Tasten, die es bei anderen Android-Geräten nicht gibt. Da es sich bei diesem Gerät um einen Spezialfall handelt, können Sie diese Tasten in Ihren Programmen nutzen (falls Sie sie für den Nook entwickeln), Sie können aber nicht erwarten, dass es diese Tasten auch bei anderen Geräten gibt.

Java-Kenntnisse

Bei der Programmiersprache Java handelt es sich um eines der wunderbaren Werkzeuge, durch die die Android-Programmierung im Gegensatz zu anderen Mobilplattformen fast zum Kinderspiel wird. Während Sie bei anderen Sprachen den Speicher verwalten und Bytes zur Verfügung stellen und wieder freigeben müssen und dann Bits wie beim Dominospielen herumschieben müssen, gibt es bei Java mit der Java Virtual Machine (JVM) einen guten Kumpel, der Ihnen diese Aufgabe abnimmt. Durch die JVM können Sie sich auf das Schreiben von Code zur Lösung eines geschäftlichen Problems (oder für das nächste wirklich tolle Action-Game, von dem Sie schon immer geträumt haben) konzentrieren und dabei eine saubere, verständliche Programmiersprache verwenden. Sie müssen sich also nicht mit der Schmutzarbeit herumplagen, nur um dafür zu sorgen, dass Ihre Programmfenster auch angezeigt werden.

Bevor Sie Ihre erste Android-Anwendung schreiben, sollten Sie die Java-Grundlagen beherrschen. Wenn Sie sich ein wenig eingerostet fühlen und einen Java-Auffrischungskurs benötigen, können Sie eine der Websites mit Java-Tutorials besuchen, wie etwa http://docs.oracle.com/javase/tutorial (englisch) oder http://www.programmersbase.net/Content/Java (deutsch).

Zwar finden Sie auch im vorliegenden Buch bereits einige Informationen zu Java, aber vielleicht wollen Sie Ihre Kenntnisse mit einem guten Buch wie Java für Dummies ja zusätzlich noch ein wenig auffrischen und/oder erweitern.

Optimierung der Hardware

Sie können Android-Apps unter verschiedenen Betriebssystemen entwickeln, zu denen Windows, Linux und Mac OS X zählen. Ich habe für dieses Buch zwar Windows verwendet, Sie können stattdessen aber auch Mac OS X oder Linux benutzen.

Betriebssystem

Android unterstützt momentan die folgenden Plattformen:

Windows XP oder neuere Versionen

Mac OS X 10.5.8 oder später (nur x86-Plattform)

Ubuntu-Linux (getestet ab Ubuntu 8.04 – »Hardy Heron«)

Beachten Sie, dass die gegebenenfalls verwendeten 64-Bit-Distributionen auch 32-BitAnwendungen ausführen können müssen. Aktuelle Einzelheiten dazu finden Sie unter http://developer.android.com/sdk/installing/index.html.

Da ich für dieses Buch Windows verwendet habe, sehen einige der Abbildungen bei Ihrem Rechner möglicherweise ein wenig anders aus. Wenn Sie einen Mac oder einen Linux-Rechner verwenden, sehen die Pfade anders aus. Die Pfadangaben in diesem Buch sehen so aus: c:\fad\zur\datei.txt

Bei einem Mac oder einem Linux-Rechner werden Pfade hingegen so angegeben: /pfad/zur/datei.txt

Computerhardware

Überzeugen Sie sich vor dem Installieren der benötigten Software davon, dass sie auf Ihrem Rechner auch ordentlich ausgeführt werden kann. Recht sicher lässt sich wohl sagen, dass nahezu alle in den letzten etwa vier Jahren hergestellten Desktop- oder Laptop-Rechner ausreichend sein dürften. Der langsamste Rechner, den ich für Eclipse genutzt habe, war ein Laptop mit einem 1,6-GHz-Pentium-D-Prozessor mit einem Gigabyte RAM. Ich habe dieselbe Konfiguration unter Windows XP und Windows 7 genutzt und beide Betriebssysteme konnten Eclipse-Anwendungen auf dieser Hardware problemlos ausführen und debuggen. (Die Entwicklungsumgebung Eclipse, mit der Sie Ihre Apps programmieren, sollte auf so ziemlich jedem halbwegs modernen Rechner flüssig laufen.)

Damit gewährleistet ist, dass Sie alle benötigten Tools und Quelltexte installieren können, sollte noch genügend Speicherkapazität auf der Festplatte frei sein. Auf der Website für Android-Entwickler unter http://developer.android.com/sdk/requirements.html finden Sie eine Liste mit Hardwareanforderungen, die grob angeben, wie viel Speicherplatz für die verschiedenen Komponenten benötigt wird.

Um Ihnen ein wenig Zeit zu sparen: Insgesamt benötigen die für die Entwicklung von Android-Anwendungen benötigten Werkzeuge und Frameworks etwa drei Gigabyte Platz auf der Festplatte.

Hilfsprogramme installieren und konfigurieren

So langsam wird es ernst. Es wird Zeit, dieses Android zum Laufen zu bringen. Vorher müssen Sie aber erst einmal ein paar Tools und SDKs (Software Development Kits) installieren:

Java-JDK: Bildet die Grundlagen für das Android-SDK.

Android-SDK: Bietet Zugriff auf Android-Bibliotheken und ermöglicht Ihnen die Entwicklung für Android.

Eclipse-IDE (Integrated Development Environment): Diese Entwicklungsumgebung vereint Java, das Android-SDK und die Android-ADT (Android Development Tools) und enthält Tools für die Android-Programmierung.

Android-ADT: Nehmen Ihnen eine Menge lästige Aufgaben ab und erstellt beispielsweise die für Android-Apps benötigten Dateien und Strukturen.

In den folgenden Abschnitten werde ich Ihnen zeigen, wie Sie sich diese Tools beschaffen und sie installieren können.

Ein Vorteil der Arbeit mit Open-Source-Software besteht darin, dass die Tools zur Softwareentwicklung meist kostenlos erhältlich sind. Android bildet hier keine Ausnahme. Alle für die Entwicklung umfassender Android-Apps benötigten Tools kosten Sie nichts.

Das JDK herunterladen und installieren

Aus irgendwelchen Gründen haben sich die Verantwortlichen bei der Namensgebung für das Java-SDK dazu entschlossen, es Java Development Kit oder kurz JDK zu nennen.

Die folgenden Schritte gelten für Windows-Rechner, gestalten sich aber für Mac- und Linux-Rechner ähnlich. Führen Sie die folgenden Schritte aus, um das JDK zu installieren:

1. Rufen Sie mit Ihrem Browser die Webseite www.oracle.com/technetwork/java/javase/downloads/index.html auf.

Die Seite JAVA SE DOWNLOADS wird angezeigt.

2. Klicken Sie die Download-Schaltfläche für JAVA PLATFORM (JDK) an (Abbildung 2.5).

Nun wird eine Downloadseite angezeigt, auf der Sie die Plattform (Windows, Linux oder Mac in 32- oder 64-Bit-Variante) auswählen müssen, die Sie für die Entwicklung nutzen wollen.

Die in Abbildung 2.5 dargestellte Webseite sieht in Zukunft möglicherweise anders aus. Um wirklich die richtige Seite aufzurufen, können Sie die Webseite mit den Systemanforderungen für das Java-SDK in der Android-Dokumentation aufrufen, auf der sich ein direkter Link zur Java-SDK-Downloadseite befindet. Sie finden sie unter der Adresse developer.android.com/sdk/requirements.html.

3. Akzeptieren Sie die Lizenzvereinbarung und klicken Sie den Download-Link für das von Ihnen verwendete Betriebssystem an.

Unter Windows entscheiden Sie sich für die 32-Bit-Installation. Wenn Sie einen 64-Bit-Rechner verwenden, können Sie wahlweise die 32-Bit- (x86) oder die 64-Bit-Version (x64) des JDKs installieren. Für die Android-Entwicklung müssen Sie aber die 32-Bit-Variante verwenden.

Windows zeigt daraufhin möglicherweise ein Dialogfeld mit einer Sicherheitswarnung an, in dem Sie dann die Option SPEICHERN wählen.

4. Wählen Sie im Dialogfeld SPEICHERN UNTER den gewünschten Speicherort für die Datei aus und klicken Sie SPEICHERN an.

Abbildung 2.5: JDK auswählen.

5. Wenn der Download abgeschlossen ist, klicken Sie die heruntergeladene Datei im Explorer doppelt an, um das JDK zu installieren.

Nun werden Sie in einem Dialogfeld der Benutzerkontensteuerung möglicherweise gefragt, ob das Programm Änderungen an Ihrem Rechner vornehmen darf.

6. Klicken Sie FORTSETZEN oder JA an.

Wenn Sie NEIN anklicken, wird die Installation abgebrochen.

7. Klicken Sie jeweils NEXT (WEITER) und zum Schluss FINISH (FERTIG STELLEN) an, um die weitere Installation durchzuführen.

Sofern Sie noch kein Benutzerkonto bei Oracle besitzen, müssen Sie es anschließend noch anlegen, um das JDK registrieren zu können.

Sie sollten sich die installierte Version des Java-SDKs merken. Falls Sie nämlich die falsche Java-Version installieren sollten, kann dies während der Entwicklung zu unerwarteten Ergebnissen führen.

Mehr müssen Sie hier nicht tun. Damit haben Sie das JDK installiert und können zur nächsten Phase übergehen.

So erhalten Sie das Android-SDK

Das Android-SDK besteht aus einem Debugger, Android-Bibliotheken, einem Geräteemulator, Dokumentation, Beispielcode und Tutorials. Ohne das SDK können Sie keine Android-Apps entwickeln.

Das Android-SDK herunterladen

Die erforderlichen Schritte beim Herunterladen und Installieren des Android-SDKs unterscheiden sich zwar zwischen den verschiedenen Versionen, sollten einander aber doch ähneln. Bei der von mir verwendeten Version musste ich diese Schritte ausführen:

1. Rufen Sie mit Ihrem Browser die Webseite http://developer.android.com/sdk/index.html auf.

2. Wählen Sie das aktuelle Starter-Paket für die von Ihnen verwendete Plattform aus, um das SDK herunterzuladen.

Je nach Version und Plattform laden Sie hier eine direkt ausführbare EXE-Datei für Windows oder komprimierte Archive (*.zip oder *.tgz) herunter, die Sie zunächst entpacken müssen.

Zur Installation führen Sie dann entweder das Installationsprogramm aus oder kopieren die Dateien in vorgegebene oder auch frei wählbare Unterverzeichnisse.

Achten Sie hier besonders auf möglicherweise angezeigte Links mit Hinweisen zur Installation und/oder Konfiguration der jeweiligen Versionen.

3. Starten Sie den SDK-Manager.

• Windows: Starten Sie den SDK-Manager aus dem Ordner heraus, in dem er installiert oder hineinkopiert wurde.

Wenn Sie das ADT-Bundle heruntergeladen haben, können Sie einfach eclipse aufrufen. Dann werden Sie nach dem Arbeitsbereich (workspace) gefragt und tra-gen C:\Users\<benutzername>\workspace ein. (Dieser Schritt wird weiter hinten noch erläutert.) Den SDK-Manager können Sie dann über die Menüleiste und WINDOW:ANDROID SDK MANAGER starten, um zum nachfolgend dargestellten Fenster zu gelangen.

Wenn Sie für Windows das komplette ADT-Bundle heruntergeladen haben, wurden bereits etliche Einstellungen vorkonfiguriert, weshalb Sie einige der hier vorgestellten Schritte eigentlich übergehen könnten und andere Einstellungen in einer anderen Reihenfolge vornehmen müssen. Blättern Sie dann im Buch ein wenig vor, um die benötigten Einstellungen vornehmen zu können.

• Mac: Klicken Sie die SDK-Datei doppelt an, um sie zu entpacken. Verschieben Sie das dabei erstellte Verzeichnis android−sdk−mac_x86 an eine geeignete Position, wie beispielsweise Ihr Anwendungsverzeichnis. Starten Sie TERMINAL und wechseln Sie mit dem Befehl cd in das Verzeichnis android−sdk−mac_x86, um dort anschließend tools/android auszuführen. Klicken Sie dann INSTALL an und folgen Sie den weiteren Anleitungen.

4. Wählen Sie die aktuelle SDK-Plattform (momentan 4.2) aus, indem Sie das entsprechende Kontrollkästchen aktivieren (siehe Abbildung 2.6).

Dieses Buch stützt sich auf die momentan aktuelle Android-Version 4.2 (API 17). Die Optionen für nicht benötigte (ältere) SDK-Versionen können Sie natürlich deaktivieren/deaktiviert lassen. Achten Sie aber darauf, dass die Optionen für die Beispiele (und die Dokumentation) der installierten SDK-Versionen aktiviert bleiben (Abbildung 2.6).

Abbildung 2.6: Wählen Sie die zu installierenden Pakete aus.

Da die benötigten Daten aus dem Internet heruntergeladen werden und zudem recht umfangreich sind, sollten Sie sich bei Ihrer Auswahl (zumindest vorläufig) auf die wirklich benötigten Pakete beschränken.

Jeweils beim Erscheinen einer neuen Version des Android-Betriebssystems veröffentlicht Google auch ein SDK, das Zugriff auf die neuen Funktionen dieser Versionen bietet. Wenn Sie in Ihre Apps beispielsweise Bluetooth-Funktionen integrieren wollen, müssen Sie darauf achten, dass Sie mindestens die Version 2.0 des Android-SDKs benutzen, da diese in älteren Versionen nicht verfügbar sind.

5. Klicken Sie INSTALL PACKAGES an.

Das Dialogfeld CHOOSE PACKAGES wird angezeigt.

6. Aktivieren Sie die Option ACCEPT ALL und klicken Sie INSTALL an (siehe Abbildung 2.7).

Abbildung 2.7: Das Dialogfeld CHOOSE PACKAGES

Da das Herunterladen und die Installation der gewählten Optionen eine Weile dauern kann, werden Sie nun über ein Balkendiagramm über den Fortschritt der Installation auf dem Laufenden gehalten.

7. Schließen Sie nach Abschluss der Installation der Archive das Dialogfeld ANDROID SDK MANAGER.

Das Android-NDK hinzufügen

Beim Android-NDK (Native Development Kit) handelt es sich um eine Reihe von Werkzeugen, mit denen Sie Komponenten einbetten können, die nativen Code und damit Code verwenden, den Sie in nativen Sprachen wie C oder C++ geschrieben haben.

Wenn Sie das NDK nutzen wollen, müssen Sie weiterhin das SDK herunterladen. Das NDK ist kein Ersatz für das SDK. Es bietet vielmehr zusätzliche Funktionen, die das SDK erweitern.

Setzen Sie den Pfad zu Ihren Tools

Dieser Schritt ist zwar optional, ich kann Ihnen aber nur dringend dazu raten, den Pfad zu den Tools zu setzen, weil Sie sich ihn dann beim Zugriff auf die Android Debug Bridge (ADB) über die Befehlszeile nicht mehr merken und eintragen müssen.

Mit ADB können Sie den Zustand eines Emulators oder Android-Geräts verwalten, um Ihre Apps zu debuggen oder auf höherer Ebene mit dem Gerät zu interagieren. Das ADB-Tool ist höchst anspruchsvoll, weshalb ich hier nicht gerade ausführlich darauf eingehen werde. Nähere Einzelheiten dazu können Sie aber der Android-Dokumentation entnehmen.

Um die Android-Tools unter Windows zur Pfadvariablen Ihres Systems hinzuzufügen, führen Sie diese Schritte aus:

1. Starten Sie die Systemsteuerung und klicken Sie das Symbol SYSTEM doppelt an.

2. Klicken Sie den Link ERWEITERTE SYSTEMEINSTELLUNGEN an (siehe Abbildung 2.8), um das Dialogfeld SYSTEMEIGENSCHAFTEN zu öffnen.

Abbildung 2.8: Der Link ERWEITERTE SYSTEMEINSTELLUNGEN

3. Klicken Sie die Schaltfläche UMGEBUNGSVARIABLEN an, um das gleichnamige Dialogfeld zu öffnen.

4. Im Dialogfeld UMGEBUNGSVARIABLEN klicken Sie die Schaltfläche NEU an.

Das in Abbildung 2.9 dargestellte Dialogfeld NEUE SYSTEMVARIABLE wird angezeigt.

Abbildung 2.9: Das Dialogfeld NEUE SYSTEMVARIABLE

5. Tippen Sie in das Feld NAME DER VARIABLENANDROID ein.

6. Tragen Sie den vollständigen Pfad zum Ordner tools in das Feld WERT DER VARIABLEN ein.

Wenn Sie das Installationsprogramm unter den 64-Bit-Versionen von Windows Vista/7 zur Installation benutzt haben, könnte er beispielsweise C:\Program Files (x86)\Android\android−sdk−windows\tools lauten. In Abbildung 2.9 wurde das ADT-Bundle in einen Benutzerordner kopiert.

7.Klicken Sie OK an.

Das in Abbildung 2.10 dargestellte Dialogfeld UMGEBUNGSVARIABLEN wird angezeigt.

8.Im Dialogfeld UMGEBUNGSVARIABLEN markieren Sie im Bereich SYSTEMVARIABLEN die Variable Path (Abbildung 2.10).

Abbildung 2.10: Bearbeitung der Systemvariablen Path

9.Klicken Sie unten im Dialogfeld BEARBEITEN an und fügen Sie dort am Ende des Feldes WERT DER VARIABLEN die folgende Ergänzung hinzu:

;%ANDROID%/tools;%ANDROID%/platform−tools

10. Klicken Sie jeweils OK an, um das Dialogfeld SYSTEMVARIABLE BEARBEITEN und die anderen noch geöffneten Dialogfelder zu schließen.

Auf einem Mac öffnen Sie die Datei .profile in Ihrem Heimatordner (/Users/<Ihr Benutzername >) mit TextEdit. Wenn keine versteckten Dateien angezeigt werden, drücken Sie im Dialogfeld DATEI ÖFFNEN die Tastenkombination [Mac Bef]+[Shift]+[.]. Fügen Sie am Ende der Datei .profile Folgendes hinzu:

export ANDROID=<Pfad_zum_Android_SDK>
export PATH=$PATH:$ANDROID/tools:$ANDROID/platform−tools

Tragen Sie für Pfad_zum_Android_SDK den entsprechenden vollständigen Pfad ein, wie beispielsweise /Applications/android−sdk−mac_x86. Speichern Sie die Datei dann und starten Sie Ihren Mac neu.

Damit haben Sie den Wert der Systemvariablen Path geändert. Wenn Sie nun auf den Ordner mit den Android-Tools zugreifen wollen, können Sie dazu einfach die gerade erstellte Systemvariable benutzen.

Bei den meisten Betriebssystemen wird die Systemvariable Path nicht aktualisiert, wenn Sie sich nicht ab- und anschließend wieder anmelden. Wenn Sie feststellen, dass die Werte der Variablen Path nicht wie gewünscht gesetzt sind, melden Sie sich also ab und dann wieder an.

So erhalten Sie Eclipse

Da Sie das SDK nun installiert haben, benötigen Sie eine integrierte Entwicklungsumgebung (IDE – Integrated Development Environment), um es zu benutzen. Es wird Zeit, Eclipse herunterzuladen!

Beim Herunterladen des ADT-Bundles können Sie diesen Abschnitt überspringen und lediglich Ihre Einstellungen vornehmen oder kontrollieren, da Eclipse dann bereits installiert wurde.

Eclipse installieren

Um Eclipse separat herunterzuladen und zu installieren, rufen Sie zunächst die Downloadseite www.eclipse.org/downloads auf. Wählen Sie dann ECLIPSE IDE FOR JAVA DEVELOPER (ECLIPSE IDE FOR JAVA EE DEVELOPERS können Sie auch verwenden) für Ihre Plattform aus, um die entsprechende Zip-Datei herunterzuladen.

Das heruntergeladene Eclipse-Archiv müssen Sie in einem Ordner Ihrer Wahl extrahieren. Da es einige Windows-Versionen gar nicht mögen, wenn manuell Dateien in die Programmordner kopiert oder entpackt werden, habe ich hier im Beispiel den Ordner C:\Eclipse benutzt. Sie können aber natürlich auch den Anwendungsordner oder einen Ordner im Benutzer- oder Home-Verzeichnis verwenden.

Um Eclipse nach dem Entpacken zu konfigurieren, führen Sie diese Schritte aus:

1. Starten Sie Eclipse (entweder über die ausführbare Datei oder eine eingerichtete Verknüpfung).

Sie sollten Eclipse als Administrator starten, da ansonsten einige Optionen nicht funktionieren. Gerätetreiber darf beispielsweise nur ein Administrator installieren. Dazu können Sie das Programm unter Vista/7 entweder nach einem Rechtsklick über die Option ALS ADMINISTRATOR AUSFÜHREN starten oder im Kontext EIGENSCHAFTEN und auf der Registerkarte KOMPATIBILITÄT unten das Kontrollkästchen PROGRAMM ALS EIN ADMINISTRATOR AUSFÜHREN aktivieren.

Je nach Konfiguration Ihrer Windows-Version und verwendeter Eclipse-Versionen wird eine Warnung oder Nachfrage angezeigt. Teilweise können Sie dabei über entsprechende Optionen dafür sorgen, dass die Meldungen nicht erneut angezeigt werden. Ansonsten lassen Sie den Zugriff einfach zu oder lassen das Programm weiter ausführen.

2. Richten Sie Ihren Arbeitsbereich ein.

Wenn Eclipse startet, wird zunächst das Dialogfeld WORKSPACE LAUNCHER angezeigt (Abbildung 2.11). Hier können Sie zwar einen anderen als den vorgeschlagenen Ordner verwenden, aber für dieses Buch behalte ich die Vorgabe bei, die unter Windows so aussieht:

C:\Users\<benutzername>\workspace

Lassen Sie das Kontrollkästchen USE THIS AS THE DEFAULT deaktiviert und klicken Sie die Schaltfläche OK an.

Abbildung 2.11: Den Ordner für den Arbeitsbereich festlegen

Wenn Sie mehrere Apps entwickeln wollen, empfehle ich die Verwendung getrennter Arbeitsbereiche (workspaces) für die verschiedenen Projekte. Wenn Sie mehrere Projekte in einem Arbeitsbereich speichern, wird es schwer, den Überblick zu behalten, und man ändert allzu leicht ähnlich benannte Dateien eines anderen Projekts. Wenn Sie für die Projekte getrennte Arbeitsbereiche verwenden, finden Sie sich auch besser zurecht, wenn Sie später einmal Fehler beheben müssen.

Wenn Eclipse geladen wurde, wird dessen Willkommen-Bildschirm angezeigt (Abbildung 2.12).

Abbildung 2.12: Der Willkommen-Bildschirm von Eclipse

3. Klicken Sie den gewölbten Pfeil rechts im Fenster an, um die Workbench zu starten.

Eclipse ist installiert und leicht aufrufbar. Wie Sie die ADT (Android Development Tools) hinzufügen können, erfahren Sie im nächsten Abschnitt.

Eclipse konfigurieren

Die Android-Entwicklungswerkzeuge (ADT – Android Development Tools) erweitern Eclipse um Funktionen, die Ihnen eine Menge Arbeit abnehmen. Mit ADT können Sie
leicht neue Android-Projekte erstellen,
schneller in die Programmierung Ihrer Apps einsteigen,

Ihre Anwendungen mit den Android-SDK-Tools debuggen und
schließlich Ihre Apps direkt aus Eclipse heraus in signierte Android-Pakete (APK – Android Package) exportieren, ohne dazu Befehlszeilenwerkzeuge einsetzen zu müssen.

Anfangs mussten Programmierer verschiedene Befehlszeilenprogramme bemühen, um APK-Dateien erstellen zu können. Das war zwar nicht allzu schwierig, aber mühsam und zuweilen frustrierend. ADT nehmen Ihnen derartige Arbeiten ab und erledigt das über einen Assistenten aus Eclipse heraus. Wie Sie eine signierte APK-Datei exportieren können, erfahren Sie in Kapitel 8.

Eclipse mit den ADT einrichten

Um Eclipse mit den Android-Entwicklungswerkzeugen einzurichten, führen Sie diese Schritte aus:

1. Wenn es nicht bereits läuft, starten Sie Eclipse.

2. Wählen Sie HELP:INSTALL NEW SOFTWARE.

Daraufhin wird das Fenster INSTALL angezeigt (Abbildung 2.13), über das Sie neue Plugins in Eclipse installieren können.

3. Klicken Sie die Schaltfläche ADD an, um über das Dialogfeld ADD SITE eine neue Website hinzuzufügen.

Bei Sites handelt es sich um die Adressen von Webseiten, auf denen im Internet Software abgelegt ist. Wenn Sie eine Site in Eclipse hinzufügen, können Sie die Software leichter aktualisieren, sobald neue Versionen erscheinen.

Das in Abbildung 2.13 dargestellte Dialogfeld ADD REPOSITORY wird angezeigt.

4. Tragen Sie im Feld NAME eine Bezeichnung ein.

Ich würde Ihnen empfehlen, hier Android ADT einzutragen, Sie können aber auch beliebige andere Namen verwenden.

5. Tragen Sie im Feld LOCATION https://dl−ssl.google.com/android/eclipse/ als Adresse ein.

Abbildung 2.13: Tragen Sie eine Bezeichnung und die Adresse der Website ein.

6. Klicken Sie OK an.

Im Listenfeld WORK WITH im Dialogfeld INSTALL ist ANDROID ADT ausgewählt und in der Mitte des Fensters werden die Namen und Versionen der verfügbaren Optionen angezeigt (Abbildung 2.14).

Abbildung 2.14: Markieren Sie DEVELOPER TOOLS.

7. Aktivieren Sie das Kontrollkästchen neben DEVELOPER TOOLS und klicken Sie die Schaltfläche NEXT an.

Im Dialogfeld INSTALL DETAILS sollten ANDROID DDMS (Dalvik Debug Monitor Server), ANDROID DEVELOPMENT TOOLS und ANDROID HIERARCHY VIEWER aufgeführt werden (Abbildung 2.15). (DDMS wird im Abschnitt »Physische Erfahrungen mit echten Android-Geräten« weiter hinten in diesem Kapitel eingehender behandelt.)

8. Klicken Sie die Schaltfläche NEXT an, um zum Dialogfeld mit den Softwarelizenzen zu gelangen.

9. Aktivieren Sie die Option I ACCEPT THE TERMS OF THE LICENSE AGREEMENT, um sich mit den Lizenzvereinbarungen einverstanden zu erklären.

Abbildung 2.15: DDMS und ADT werden im Dialogfeld INSTALL DETAILS aufgeführt.

10. Klicken Sie die Schaltfläche FINISH an.

Nun werden die gewünschten Komponenten heruntergeladen und installiert. Das kann wieder einmal eine Weile dauern. Möglicherweise müssen Sie zwischendurch noch in dem ein oder anderen Dialogfeld der Installation unsignierter Elemente zustimmen und sie für vertrauenswürdig erklären.

11. Wenn Sie dazu aufgefordert werden, klicken Sie die Schaltfläche YES an, um Eclipse neu zu starten.

Das ADT-Plugin wurde installiert.

Sollten Sie Probleme damit haben, die Tools von https://dl-ssl.google.com/android/eclipse herunterzuladen, können Sie es auch über http:// dl−ssl.google.com/android/eclipse versuchen.

SDK-Speicherort festlegen

In diesem Abschnitt geleite ich Sie durch die Konfiguration. Ich weiß, dass Sie hier scheinbar eine Menge machen müssen, aber Sie sind bereits fast fertig und schließlich müssen Sie diese Arbeit nur einmal erledigen (zumindest für einen Rechner). Führen Sie diese Schritte aus:

1. Wählen Sie in Eclipse WINDOW:PREFERENCES.

Das Dialogfeld PREFERENCES wird angezeigt (Abbildung 2.16).

Abbildung 2.16: Geben Sie im Dialogfeld PREFERENCES den Ordner an, in dem sich das SDK befindet.

2. Markieren Sie auf der linken Seite ANDROID.

3. Tragen Sie auf der rechten Seite im Feld SDK LOCATION ein, wo sich das SDK auf der Festplatte befindet, oder wählen Sie es über die Schaltfläche BROWSE aus.

Wenn Sie das Android-SDK in der 32-Bit-Version unter einer 64-Bit-Version von Windows Vista/7 über die ausführbare Setup-Datei installiert haben, befindet es sich beispielsweise im Ordner C:\Program Files (x86)\Android\android−sdk−windows. Wenn Sie das ADT-Bundle installiert haben, sollte der eingetragene Pfad bereits korrekt sein.

4. Klicken Sie OK an.

Eclipse ist konfiguriert und Sie können mit der Entwicklung von Android-Apps loslegen.

Machen Sie sich mit den Android-SDK vertraut

Hoppla! Das SDK umfasst eine Menge Ordner! Aber keine Angst, wenn Sie den Bogen erst einmal herausbekommen haben, lässt sich die Ordnerstruktur des Android-SDKs recht leicht durchschauen. Um es umfassend nutzen zu können, müssen Sie die Struktur des SDKs aber verstehen. Tabelle 2.1 skizziert, was sich in den jeweiligen Ordnern befindet.

SDK-Ordner | Beschreibung

usb_driver

Enthält die Treiber für Android-Geräte. Wenn Sie Ihr Android-Gerät an den Rechner anschließen, müssen Sie diesen Treiber installieren, damit Sie mit den ADT Apps ansehen, debuggen und auf Ihr Mobilgerät übertragen können. Der Ordner usb_ driver wird erst bei Installation des USB-Treibers erstellt.

tools und platform−tools

Enthält verschiedene Tools, die Sie während der Entwicklung nutzen können, wie zum Beispiel zum Debuggen, zur Verwaltung von Steuerelementen (Views) und zum Erstellen Ihrer Apps.

temp

Dient dem SDK als temporäres Zwischenlager für Dateien. Manchmal benötigt das SDK zur Erledigung seiner Aufgaben vorübergehend Speicherplatz. Zu diesem Zweck benutzt es diesen Ordner.

samples

Enthält eine Reihe von Beispielprojekten, mit denen Sie experimentieren können. Der vollständige Quelltext wird mitgeliefert.

platforms

Enthält die Zielplattformen, die Sie beim Erstellen von Android-Apps verwenden können. Hier finden Sie zum Beispiel die Ordner android−16 (für Android 4.1) und/oder android−8 (für Android 2.2).

docs | Enthält ein lokales Exemplar der Dokumentation des Android-SDKs.

add−ons

Enthält zusätzliche APIs mit Erweiterungsfunktionen. In diesem Ordner befinden sich die Google-APIs, mit denen Sie Landkarten anzeigen lassen können. Dieser Ordner bleibt leer, bis Sie die APIs von Google Maps installiert haben.

Tabelle 2.1: Ordner des Android-SDKs

Android-Zielplattformen

Android-Plattform ist nur eine seltsame Umschreibung von Android-Version. Mittlerweile gibt es eine ganze Reihe verschiedener Android-Versionen bis hinauf zu Android 4.2. Sie können Apps für alle verfügbaren Versionen entwickeln, wobei die Versionen vor 2.2 (Froyo) bereits als veraltet gelten und 4.2 gerade erst erschienen ist.

Denken Sie daran, dass die älteren Android-Versionen (vor 4.0) noch verbreitet auf Mobiltelefonen laufen, für die sich die 3.x-Versionen nicht eigneten. Wenn Sie möglichst viele Anwender erreichen wollen, sollten Sie besser für ältere Zielplattformen entwickeln. Wenn Sie in Ihren Apps allerdings Funktionen nutzen wollen, die von den älteren Plattformen nicht unterstützt werden, dann bleibt Ihnen nichts anderes übrig, als für neuere Plattformen zu entwickeln. Bluetooth wird so zum Beispiel erst ab Version 2.0 unterstützt.

Abbildung 2.17 zeigt, welche Android-Versionen von den Geräten benutzt wurden, die den Google Play Store in den letzten zwei Wochen vor Anfang November 2012 besucht haben. Die aktuelle Plattformstatistik finden Sie unter http://developer.android.com/resour ces/dashboard/platform−versions.html. Beachten Sie dabei, dass bei diesen Prozentzahlen die neueren Geräte sicherlich überrepräsentiert sind, da sich Besitzer neuer Geräte häufiger auf dem Google-Marktplatz umsehen und Anwendungen aktualisieren werden. Zudem verbreiten sich die neuen Versionen in Deutschland erst mit ein wenig Verzögerung.

Abbildung 2.17: Android-Distributionen (Stand: 1. November 2012)

Auffällig an den Zahlen ist zudem, dass Android 4.x es seit Mai auf etwa 20 Prozent Zuwachs unter den Android-Geräten gebracht hat. Damit läuft laut dieser Statistik bereits gut jedes vierte Gerät unter Android 4.x.

Für die im November 2012 erschienene Version 4.2, für die ebenfalls der Codename »Jelly Bean« verwendet wird, enthält Abbildung 2.17 noch keine Zahlen.

SDK-Tools zur Programmentwicklung nutzen

Die SDK-Tools sind die Grundbausteine bei der Entwicklung von Android-Apps. Mit den zusätzlichen Funktionen neuer SDK-Versionen können Sie auch Apps für die jeweils neuesten Android-Versionen entwickeln.

Lernen Sie den Emulator kennen

Google stellt Ihnen nicht nur die für die Entwicklung von Apps benötigten Tools zur Verfügung, sondern auch noch einen großartigen kleinen Emulator, den Sie zum Testen Ihrer Apps benutzen können! Er weist zwar einige Beschränkungen auf und kann bestimmte Hardwarekomponenten wie den Beschleunigungssensor naturgemäß nicht nachbilden. Darüber müssen Sie sich aber keine Gedanken machen, da auch so eine Menge Apps nur unter Verwendung des Emulators entwickelt und getestet werden können.

Wenn Sie zum Beispiel eine App entwickeln, die Bluetooth nutzt, sollten Sie ein reales Gerät mit Bluetooth-Unterstützung verwenden. Wenn Sie Ihre Apps auf einem schnellen Rechner entwickeln, geht auch das Testen schnell. Auf langsameren Rechnern kann der Emulator aber auch bereits zur Erledigung scheinbar einfacher Aufgaben viel Zeit benötigen. Wenn Sie bei der Entwicklung ältere Rechner nutzen, sollten Sie besser auf echte Geräte zurückgreifen. Beim Einsatz leistungsfähigerer Rechner sollten Sie den Emulator bevorzugen.

Der Emulator ist besonders praktisch, wenn Sie Ihre App für verschiedene Auflösungen und Bildschirmabmessungen testen wollen. Häufig werden Sie kaum etliche verschiedene Geräte gleichzeitig an Ihren Rechner anschließen können. Sie können aber mehrere Emulatoren für unterschiedliche Auflösungen und Bildschirmabmessungen starten.

Physische Erfahrungen mit echten Android-Geräten

Der Emulator ist zwar toll, aber manchmal müssen Sie Ihre Programme einfach auf echten Geräten testen. Mit DDMS (Dalvik Debug Monitor Server) können Sie Ihre App auf echten Geräten debuggen. Das ist dann praktisch, wenn in den von Ihnen entwickelten Apps Hardwarefunktionen genutzt werden, die sich nicht emulieren lassen. Nehmen Sie einmal an, dass Sie eine App entwickeln, die die jeweilige Position des Nutzers aufzeichnet. Sie können das Gerät zwar manuell mit Koordinaten versorgen, aber irgendwann werden Sie während der Entwicklung Ihre App wahrscheinlich testen und feststellen wollen, ob sie tatsächlich die wirkliche Position anzeigt. Das lässt sich nur mit einem echten Android-Gerät machen.

Wenn Sie zur Entwicklung einen Windows-Rechner benutzen und Ihre App auf einem echten Gerät testen wollen, müssen Sie dazu einen Treiber installieren. Bei Verwendung eines Mac- oder Linux-Rechners können Sie diesen Abschnitt überspringen, weil Sie den USB-Treiber dann nicht installieren müssen.

Wenn die USB-Treiber von Google, die sich eigentlich nur für ADP-Geräte (Android Developer Phones) wie die Geräte der Nexus-Reihe eignen sollen, für die von Ihnen genutzten Geräte nicht funktionieren, sollten Sie sich auf deren Herstellerseiten darüber informieren, ob und wie Sie sie über die ADB (Android Debug Bridge) verwenden können.

Um den USB-Treiber für Android-Geräte für Windows herunterzuladen, führen Sie diese Schritte aus:

1. Wählen Sie in Eclipse WINDOW:ANDROID SDK MANAGER.

Das Dialogfeld ANDROID SDK MANAGER wird angezeigt (Abbildung 2.18).

2. Erweitern Sie bei Bedarf den Eintrag EXTRAS und aktivieren Sie die Option GOOGLE USB DRIVER PACKAGE.

3. Klicken Sie die Schaltfläche INSTALL PACKAGE an.

Das Dialogfeld CHOOSE PACKAGES TO INSTALL wird angezeigt.

Abbildung 2.18: Die verfügbaren Pakete

4. Aktivieren Sie die Option ACCEPT ALL, um sich mit den Lizenzvereinbarungen einverstanden zu erklären, und klicken Sie die Schaltfläche INSTALL an (Abbildung 2.19).

Abbildung 2.19: Klicken Sie die Schaltfläche INSTALL an.

Ein Fortschrittsbalken gibt über den weiteren Verlauf der Installation Auskunft.

Nur Administratoren dürfen Gerätetreiber installieren. Um das USB-Treiberpaket installieren zu können, müssen Sie Eclipse unter Windows daher in jedem Fall mit Administratorberechtigungen starten.

5. Wenn das Paket heruntergeladen und installiert wurde, schließen Sie das Dialogfeld.

6. Schließen Sie das Dialogfeld ANDROID SDK MANAGER.

Beseitigung von Programmfehlern

DDMS stattet Sie mit den zum Aufspüren lästiger Fehler benötigten Werkzeugen aus. Damit können Sie, während Ihre App läuft, auch einen Blick hinter die Kulissen werfen und den Status drahtloser Verbindungen prüfen. Aber es leistet noch mehr! Es simuliert auch Aktionen, für die Sie normalerweise echte Geräte benötigen, wie zum Beispiel das manuelle Senden von GPS-Koordinaten (Global Positioning System), simulierte Telefonanrufe oder Textmitteilungen. Sie sollten sich bei http://developer.android.com/guide/developing/tools/ddms.html einen Überblick über die Möglichkeiten von DDMS verschaffen.

Probieren Sie die API- und SDK-Beispiele aus

Die API- und SDK-Beispiele werden mitgeliefert, um Ihnen zu zeigen, wie sich die im API und SDK enthaltenen Funktionen nutzen lassen. Sollten Sie einmal nicht mehr weiterkommen und nicht wissen, wie Sie bestimmte Dinge zum Laufen bringen können, sollten Sie die Webseite http://developer.android.com/resources/samples/index.html besuchen. Hier finden Sie Beispiele für fast alle Zwecke von der Bluetooth-Nutzung bis hin zur Erstellung von Textanwendungen oder 2D-Spielen.

Auch das Android-SDK enthält einige Beispiele. Öffnen Sie einfach den Ordner samples des Android-SDKs, der verschiedene Beispiele von der Zusammenarbeit mit Diensten bis hin zur Änderung lokaler Datenbanken enthält. Investieren Sie ein wenig Zeit, um sich die Beispiele näher anzusehen. Am besten lernen Sie die Entwicklung von Android-Apps, wenn Sie sich vorhandene Codebeispiele ansehen und sie in Eclipse ausprobieren.

Sehen Sie sich die API-Demos im Testlauf an

Bei den API-Demos im Ordner samples handelt es sich um eine Zusammenstellung von Apps, die den Einsatz der verfügbaren APIs verdeutlichen sollen. Hier finden Sie Apps mit Unmengen von Beispielen für:

Benachrichtigungen (Notifications)

Wecktöne (Alarms)

Intents

Menüs

Suche

Einstellungen (Preferences)

Hintergrunddienste

Wenn Sie einmal nicht weiterkommen sollten oder sich auf das Schreiben Ihrer nächsten spektakulären Android-App vorbereiten wollen, erfahren Sie alle Einzelheiten unter http://developer.android.com/resources/samples/ApiDemos/index.html.

Teil II

Ihre erste Android-App erstellen und veröffentlichen

In diesem Teil …

In Teil II lernen Sie die Schritte zur Entwicklung einer nützlichen Android-App kennen. Er beginnt mit einer Einführung in die Android-Tools und wendet sich dann der Entwicklung der Fenster und Widgets für den Startbildschirm zu, mit denen Benutzer interagieren. Wenn die Anwendung fertig ist, werden Sie sehen, wie Sie sie für die Verteilung über Google Play digital signieren können. Schließlich finden Sie in diesem Teil noch eine ausführliche Darstellung der Veröffentlichung Ihrer Anwendung über den Google Play Store.

3

Ihr erstes Android-Projekt

In diesem Kapitel

Ein neues, leeres Projekt in Eclipse erstellen

Fehlermeldungen verstehen

Einen Emulator erstellen

Startkonfigurationen einrichten und kopieren

Ihre erste App starten

Ein Projekt sezieren

Sie können es gar nicht mehr erwarten, den nächsten Android-Superhit auf die Menschheit loszulassen? Gut! Um Ihnen einige der wesentlichen Aspekte bei der Erstellung von Android-Projekten zu verdeutlichen, werde ich Ihnen aber vor Ihrem nächsten Megaerfolg erst noch schrittweise zeigen, wie Sie Ihre erste Android-App erstellen können. Sie werden eine sehr einfache »Hallo Welt«-App erstellen, für die Sie gar keinen Code schreiben müssen. Wie jetzt? Kein Code? Ist das möglich? Begleiten Sie mich dabei und Sie werden es gleich sehen.

Ein neues Projekt in Eclipse anlegen

Ganz am Anfang müssen Sie Eclipse erst einmal starten. Danach sollte auf Ihrem Bildschirm ein Fenster angezeigt werden, das dem in Abbildung 3.1 ähnelt. Und schon können Sie mit Android loslegen.

Wenn Sie Ihre Entwicklungsumgebung noch nicht eingerichtet haben, wenden Sie sich Kapitel 2 zu. Dort werden alle zum Erstellen von Android-Apps erforderlichen Werkzeuge und Frameworks und dabei auch das Eclipse-Plugin mit den Android-Entwicklungswerkzeugen (ADT – Android Development Tools) installiert. Mit dem ADT-Plugin können Sie neue Android-Apps direkt über das FILE-Menü in Eclipse anlegen.

Führen Sie diese Schritte aus, um Ihr erstes Android-App-Projekt anzulegen:

1. Wählen Sie in Eclipse FILE:NEW:OTHER. Markieren Sie dann unter ANDROID die Option ANDROID APPLICATION PROJECT und klicken Sie die Schaltfläche NEXT an.

Das Dialogfeld NEW ANDROID PROJECT wird angezeigt (Abbildung 3.2).

2. Im Feld APPLICATION NAME tragen Sie Hallo Android ein.

Bei der Angabe im Feld APPLICATION NAME handelt es sich um den für Android geltenden Anwendungsnamen. Wenn die App auf dem Emulator oder einem physischen Gerät installiert wird, wird dieser Name im Anwendungsstarter (Application Launcher) angezeigt.

Abbildung 3.1: Die Eclipse-Entwicklungsumgebung

Abbildung 3.2: Das Dialogfeld NEW ANDROID APPLICATION

Das Feld PROJECT NAME sollte automatisch für Sie ausgefüllt werden. Über diese Angabe lässt sich das Projekt im Eclipse-Arbeitsbereich identifizieren. Nachdem es erzeugt wurde, trägt ein Ordner im Arbeitsbereich den hier von Ihnen festgelegten Namen.

Beim Einrichten von Eclipse in Kapitel 2 wurden Sie dazu aufgefordert, anzugeben, wo sich Ihr vorgegebener Arbeitsbereich befinden soll. Dieser befindet sich standardmäßig in Ihrem persönlichen Ordner. Dort legt das System die Ihnen gehörenden Dateien ab. Entsprechend verweist die Abbildung unter LOCATION auch auf meinen persönlichen Ordner.

3. Im Feld PACKAGE NAME tragen Sie com.dummies.android.halloandroid ein.

Hierbei handelt es sich um den Namen des Java-Pakets (siehe Kasten »Nomenklatur für Java-Pakete«).

Nomenklatur für Java-Pakete

Bei einem Paket handelt es sich in Java um ein Verfahren zur Organisation von Java-Klassen in Namensräumen, das dem bei Modulen verwendeten ähnelt. Jedes Paket muss für die darin enthaltenen Klassen einen eindeutigen Namen tragen. Klassen im selben Paket können gegenseitig auf ihre Elemente zugreifen.

Für Java-Pakete gibt es Namenskonventionen, die einem hierarchischen Namensmuster folgen. Die verschiedenen Hierarchieebenen werden durch Punkte getrennt. Ein Paketname beginnt auf oberster Ebene mit dem Domänennamen der Organisation, an den sich die Unterdomänen in umgekehrter Reihenfolge anschließen. Am Ende des Paketnamens kann das Unternehmen frei einen Namen für das Paket festlegen. Für dieses Beispiel werden Sie den Paketnamen com.dummies.android.halloandroid verwenden.

Beachten Sie dabei, dass die Domäne der obersten Ebene (com) vor dem Paketnamen angegeben wird. Nachfolgende Unterdomänen werden durch Punkte getrennt. Der Paketname durchläuft die Unterdomänen und endet mit dem Paketnamen halloandroid.

Ein gutes Beispiel für eine andere Nutzung eines Pakets wäre ein Java-Paket für alle Kommunikationsaktivitäten über das Web. Immer, wenn Sie eine Ihrer Java-Klassen für die Webkommunikation benötigen, könnten Sie das Java-Paket öffnen und die entsprechenden Java-Klassen bearbeiten. Pakete erleichtern Ihnen die Verwaltung Ihres Quellcodes.

4. Im Feld MINIMUM REQUIRED SDK wählen Sie API 8: ANDROID 2.2 (FROYO).

Im Feld MINIMUM REQUIRED SDK wird der Code der Android-Version eingetragen, die von einem Gerät mindestens unterstützt werden muss, um Ihre App ausführen zu können.

Die Auswahl einer älteren Version unter MINIMUM REQUIRED SDK als unter TARGET SDK weist darauf hin, dass Ihre App auch noch auf älteren Geräten läuft, wie hier beispielsweise bis hinunter zu Android 2.2.

5. Wählen Sie in der Liste TARGET SDK die Option API16: ANDROID 4.1 (JELLY BEAN) aus.

Unter TARGET SDK können Sie angeben, mit welcher API-Version (Application Programming Interface) Sie Ihr jeweiliges Projekt entwickeln wollen. Wenn Sie ANDROID 4.2 auswählen, wollen Sie das Android-4.2-Framework nutzen. Dann können Sie die Android-4.2-APIs verwenden, die Funktionen wie den Backup-Manager, Spracherkennungs-APIs oder Android Beam unterstützen.

Wenn Sie hingegen Android 1.6 als Zielplattform auswählen würden, können Sie keine der erst von späteren Versionen unterstützten Funktionen nutzen. Es werden nur die vom Ziel-Framework angebotenen Funktionen unterstützt.

Mehr zu diesem Thema erfahren Sie im Abschnitt »Die Bedeutung der Einstellungen Target SDK und Min Required SDK« weiter hinten in diesem Kapitel.

Wenn Sie für TARGET SDK und MINIMUM REQUIRED SDK unterschiedliche Werte einstellen, können Sie keine der Funktionen neuerer APIs nutzen. Wenn Sie beispielsweise Android Beam auf einem Gerät mit Android 2.2 nutzen wollten, würde die App abstürzen.

Weitere Informationen dazu erhalten Sie im Abschnitt »Die Bedeutung der Einstellungen Target SDK und Min Required SDK« weiter hinten in diesem Kapitel.

6. Wählen Sie in der Liste COMPILE WITH die Option API 17: ANDROID 4.2 (JELLY BEAN) aus.

Hier sollten Sie eigentlich immer die aktuellste angebotene Version auswählen.

7. Klicken Sie NEXT an, um zur zweiten Seite des Dialogfelds NEW ANDROID APPLICATION zu gelangen (Abbildung 3.3).

Die Einstellungen auf der zweiten Seite dieses Dialogfelds befanden sich in früheren Versionen noch mit auf der Seite mit den bereits dargestellten Optionen. In der bei der Übersetzung verwendeten Version wurden die weiteren Optionen in ein eigenes Dialogfeld ausgelagert, das in Abbildung 3.3 dargestellt wird. Möglicherweise befinden sich die nachfolgend dargestellten Optionen daher bei Ihnen auch mit im in Abbildung 3.2 dargestellten Dialogfeld.

8. Unter CREATE PROJECT IN WORKSPACE behalten Sie die Vorgabe-Einstellung bei und lassen das Kontrollkästchen aktiviert.

Diese Voreinstellungen werden beim Erzeugen eines neuen Projekts automatisch ausgewählt.

Wenn Sie Ihre Dateien lieber in einem anderen als dem vorgegebenen workspace-Ordner speichern wollen, deaktivieren Sie das Kontrollkästchen CREATE PROJECT IN WORKSPACE. Dadurch wird das Textfeld LOCATION nutzbar. Klicken Sie die Schaltfläche BROWSE an und wählen Sie einen Ordner aus, in dem Ihre Dateien gespeichert werden sollen.

9. Behalten Sie die übrigen Einstellungen bei und klicken Sie NEXT an.

Nun wird das Fenster CONFIGURE LAUNCHER ICON angezeigt.

Abbildung 3.3: Die zweite Seite des Dialogfelds NEW ANDROID APPLICATION

10. (Optional) Erstellen Sie ein Symbol für Ihr Projekt oder wählen Sie eines aus und klicken Sie NEXT an.

11. Lassen Sie im Dialogfeld CREATE ACTIVITY das gleichnamige Kontrollkästchen aktiviert, markieren Sie die Option BLANKACTIVITY und klicken Sie NEXT an.

Daraufhin wird die Seite NEW BLANK ACTIVITY angezeigt (Abbildung 3.4).

12. Tragen Sie im Feld ACTIVITY NAME den Text MainActivity ein oder behalten Sie diese Vorgabe bei.

Hier legen Sie den Namen der Startaktivität fest. Dabei handelt es sich um den Eintrittspunkt Ihrer App. Wenn Ihre Anwendung von Android ausgeführt wird, handelt es sich dabei um die erste Datei, auf die zugegriffen wird. Ein verbreitetes Namensmuster für die erste Aktivität in Ihrer Anwendung ist MainActivity.java. (Echt kreativ, oder?)

13. Behalten Sie die übrigen Einstellungen im Dialogfeld bei und klicken Sie FINISH an.

Damit sind Sie bereits fertig! Nun sollte in Eclipse links im PACKAGE EXPLORER ein einzelnes Projekt angezeigt werden (Abbildung 3.4).

Abbildung 3.4: Einrichtung Ihrer neuen Aktivität

Abbildung 3.5 zeigt das Ergebnis, wie es bei der Installation des ADT-Bundles aussieht. Wenn Sie die Werkzeuge einzeln installiert haben, gestaltet sich das Ergebnis in Eclipse wesentlich sparsamer! Was nun besser oder schlechter ist, bleibt letztlich Geschmackssache.

Analyse Ihres Projekts

Bei dem von Eclipse erzeugten Android-Projekt handelt es sich um ein neues, sauberes Projekt ohne kompilierte Binärquellen. Manchmal benötigt Eclipse eine Weile, um mit Ihrer Geschwindigkeit mitzuhalten, weshalb sich Ihr Rechner möglicherweise vorübergehend ein wenig seltsam verhält. Sie müssen auch verstehen, was auf höherer Ebene unter der Haube von Eclipse vorgeht. Darauf werde ich in den folgenden Abschnitten näher eingehen.

Auf Fehlermeldungen reagieren

Wenn Sie schnell genug hingesehen haben (oder die Geschwindigkeit Ihres Rechners am unteren Ende des Leistungsspektrums liegt), ist Ihnen direkt nach dem Anklicken der FINISH-Schaltfläche im Eclipse-Fenster vielleicht ein über dem Symbol des Ordners HALLO ANDROID im PACKAGE EXPLORER schwebendes kleines rotes Symbol aufgefallen. Wenn Sie es nicht sehen konnten, finden Sie ein Beispiel in Abbildung 3.6. Über dieses Symbol weist Eclipse darauf hin, dass beim Projekt im Arbeitsbereich Probleme aufgetreten sind.

Abbildung 3.5: Die Eclipse-Entwicklungsumgebung mit Ihrem ersten Android-Projekt »Hallo Android«

Abbildung 3.6: Ein fehlerhaftes Projekt in Eclipse

Standardmäßig wird Eclipse so eingerichtet, dass es Ihnen diesen optischen Hinweis gibt, wenn Fehler in einem Projekt erkannt wurden. Wie kann es aber Fehler in diesem Projekt geben? Sie haben es doch gerade erst mit dem Assistenten NEW ANDROID APPLICATION erstellt. Was ist los? Hinter den Kulissen erledigen Eclipse und die Android Development Tools (ADT) einige Aufgaben für Sie:

Rückmeldungen zum Arbeitsbereich: Diese Rückmeldungen informieren Sie darüber, wenn irgendwelche Probleme mit den Projekten im Arbeitsbereich auftreten. Überlagernde Symbole wie in Abbildung 3.6 geben Ihnen in Eclipse die entsprechenden Hinweise. Ein anderes Symbol, das Ihnen häufiger begegnen könnte, ist ein gelbes Warnsymbol, das Sie auf einige im Projekt vorhandene Warnungen aufmerksam macht.

Automatische Kompilierung: Standardmäßig kompiliert Eclipse die Apps in Ihrem Arbeitsbereich automatisch, wenn irgendwelche der zugehörigen Dateien geändert und anschließend gespeichert wurden.

Wenn Sie die automatische Neukompilierung deaktivieren wollen, wählen Sie PROJECT:BUILD AUTOMATICALLY. Dann wird die automatische Erzeugung des Projekts abgeschaltet. Wenn diese Option deaktiviert ist, müssen Sie Ihr Projekt nach Quelltextänderungen jeweils durch Drücken von [Strg]+[B] (PROJECT:BUILD ALL) manuell erzeugen.

Warum kommt es also zu einem Fehler bei der ersten Kompilierung? Wenn das Projekt zum Arbeitsbereich hinzugefügt wird, übernimmt Eclipse und stellt zusammen mit ADT fest, dass beim Projekt im Arbeitsbereich ein Fehler vorgelegen hat. Zu dem Fehler in Eclipse ist es deshalb gekommen, weil der Ordner gen mit all seinen Inhalten nicht vorhanden war. (Auf diesen Ordner werde ich im Abschnitt »Die Projektstruktur« weiter hinten in diesem Kapitel noch näher eingehen.)

Beim Kompilieren wird der Ordner gen von Eclipse und den ADT automatisch erstellt. Der Vorgang läuft so ab:

1. Nach Abschluss des Assistenten NEW ANDROID APPLICATION wurde ein neues Projekt erzeugt und im Eclipse-Arbeitsbereich gespeichert.

2. Eclipse erkennt das und teilt Ihnen mit, dass es einige neue Dateien in seinem Arbeitsbereich vorgefunden hat, meldet Ihnen auftretende Fehler und kompiliert das Projekt.

3. Deshalb hat Eclipse ein Fehlersymbol beim Ordner angezeigt (Abbildung 3.6).

4. Direkt anschließend kompiliert Eclipse das Projekt.

Dabei wurde der Ordner gen von Eclipse erzeugt und das Projekt wurde erfolgreich zusammengebaut.

5. Dann erkennt Eclipse, dass im Projekt kein Fehler mehr vorliegt, und entfernt das Fehlersymbol beim Ordnersymbol.

Sie bekommen einen bereinigten Arbeitsbereich und nur noch das Ordnersymbol ohne Fehlerhinweis zu sehen (Abbildung 3.7).

Abbildung 3.7: Ein fehlerfreies Projekt im PACKAGE EXPLORER. Beachten Sie das Ordnersymbol ohne den Fehlerhinweis.

Die Bedeutung der Android-Versionscodes

Die Versionscodes sind nicht mit den Versionsnamen identisch. Wie jetzt? Android kennt Versionsnamen und Versionscodes. Jedem Versionsnamen ist genau ein Versionscode zugeordnet. Die folgende Tabelle fasst die Versionsnamen und die zugehörigen Versionscodes zusammen. Die entsprechenden Angaben finden Sie auch im Listenfeld TARGET SDK des Dialogfelds NEW ANDROID APPLICATION.

Version Name (Platform Level) | Versionscode (API Level)
1.5 | 3
1.6 | 4
2.0 | 5
2.0.1 | 6
2.1 | 7
2.2 | 8
2.3 | 9
2.3.3/2.3.4 | 10
3.0 | 11
3.1 | 12
3.2 | 13
4.0.0 – 4.0.2 | 14
4.0.3 | 15
4.1 | 16
4.2 | 17

Die Bedeutung der Einstellungen Target SDK und Min Required SDK

Bei der Zielplattform der Kompilierung (TARGET SDK) handelt es sich um das beim Schreiben Ihrer Quelltexte verwendete Betriebssystem. Wenn Sie beispielsweise 2.2 als Zielplattform verwenden, können Sie die Wi-Fi-Direct-APIs beispielsweise nicht nutzen, die erst mit Version 4.0 eingeführt wurden. Wenn Sie hingegen 4.1 als Ziel-SDK wählen, können Sie diese APIs in Ihren Quelltexten nutzen.

Bevor Sie eine Android-App schreiben, sollten Sie wissen, für welche Zielplattform sie sein soll. Stellen Sie fest, welche Android-Funktionen benötigt werden, damit Ihre App wie beabsichtigt funktionieren kann. Wenn Sie wissen, dass Bluetooth-Unterstützung erforderlich ist, müssen Sie mindestens 2.0 als Zielplattform wählen. Wenn Sie nicht sicher wissen, welche Versionen die von Ihnen benötigten Funktionen unterstützten, finden Sie die entsprechenden Informationen auf den plattformspezifischen Seiten im SDK-Bereich von http://d.android.com. Die Seite für die Plattform Android 4.2 finden Sie unter http://d.android.com/sdk/android-4.2.html.

Die Android-Betriebssystemversionen sind aufwärtskompatibel. Wenn Sie beispielsweise die Android-Version 2.2 als Zielplattform verwenden, dann läuft Ihre Anwendung unter Android 4.x, 3.x und natürlich auch unter 2.2. Der Vorteil der Benutzung des 2.2-Frameworks besteht darin, dass Ihre App von einem viel größeren Teil der Anwender genutzt werden kann. Ihre App lässt sich dann auf Geräten mit den Android-Versionen 2.2, 3.0 oder 4.x (und zukünftigen Versionen, sofern es bei den neuen Versionen des Android-Betriebssystems zu keinen durchbrechenden Framework-Änderungen kommt) installieren. Es bleibt aber nicht ohne Folgen, wenn Sie eine ältere Version wählen. Wie bereits erwähnt, bleiben Sie dann auf die Funktionen dieser Version beschränkt und können keine APIs nutzen, die erst von neueren Plattformversionen unterstützt werden. Beispielsweise stehen Ihnen die APIs für soziale Medien nicht zur Verfügung, wenn Sie für die Zielplattform 2.2 entwickeln.

Bei der Einstellung MIN REQUIRED SDK handelt es sich um die Android-Version, die ein Nutzer ausführen muss, damit die Anwendung auf seinem Gerät richtig läuft. Der Wert in diesem Feld wird zwar für die Erzeugung einer App nicht unbedingt benötigt, ich empfehle Ihnen aber trotzdem, ihn zu setzen. Wenn Sie keinen Wert in MIN REQUIRED SDK angeben, wird ansonsten nämlich der Standardwert 1 verwendet, der angibt, dass Ihre App mit allen Android-Versionen kompatibel ist.

Wenn Ihre App nicht mit allen Android-Versionen kompatibel ist und zum Beispiel erst mit dem Versionscode 5 (Android 2.0) eingeführte APIs nutzt, dann wird Ihre App beim Versuch des Zugriffs auf die nicht verfügbaren APIs abstürzen, wenn Sie MIN REQUIRED SDK nicht deklariert haben und sie auf einem System mit einem niedrigeren SDK-Versionscode als 5 installiert wurde. Um derartige Abstürze zu vermeiden, sollten Sie daher in Ihren Apps möglichst immer einen Wert für MIN REQUIRED SDK eintragen.

Versionscodes und Kompatibilität

Der Wert von MIN REQUIRED SDK wird auch vom Google Play Store (dieser wird in Kapitel 8 ausführlich behandelt) verwendet. Er bestimmt auf der Grundlage der von Ihnen verwendeten Android-Version, welche Anwendungen Ihnen angezeigt werden. Wenn auf Ihrem Gerät Android 2.2 (Versionscode 8) läuft, dann werden Sie lieber nur die für Ihre und nicht auch die nur für neuere Versionen wie 4.2 (Versionscode 17) geeigneten Apps sehen wollen. Google Play entscheidet auf der Grundlage des Wertes von MIN REQUIRED SDK, welche Apps dem jeweiligen Nutzer angezeigt werden.

Wenn Sie nicht recht wissen, für welche Zielplattform Sie entwickeln wollen, kann Ihnen vielleicht das Schaubild zur aktuellen Verbreitung der Versionen weiterhelfen. Sie finden es unter http://developer.android.com/about/dashboards. Das Schaubild können Sie als Anhaltspunkt nutzen, wenn Sie entscheiden, welche Version Ihnen die potenziell größten Marktanteile bietet. Je mehr Geräte Sie erreichen können, desto größer wird Ihr Publikum. Je häufiger Ihre App installiert wird, desto erfolgreicher ist sie.

Einen Emulator einrichten

Was soll’s! Ich wette, Sie haben jetzt gedacht, dass Sie Ihr Schätzchen jetzt gleich starten könnten. Nur Geduld, gleich ist es so weit. Sie müssen aber noch ein paar letzte Dinge erledigen, bevor Sie nach all der Einrichtungsarbeit sehen können, wie ihre Anwendung HALLO ANDROID zum Leben erwacht. Um diese App laufen zu sehen, müssen Sie noch wissen, wie Sie einen Emulator über die verschiedenen Einstellungen für die Startkonfigurationen einrichten können.

Erst einmal müssen Sie ein AVD (Android Virtual Device) erstellen. Dieses »virtuelle Android-Gerät« wird auch Emulator genannt. Es sieht aus und verhält sich wie ein echtes Android-Gerät. AVDs lassen sich so konfigurieren, dass Sie eine beliebige Android-Version ausführen, deren SDK heruntergeladen und installiert wurde.

Dazu müssen wir uns mit dem AVD MANAGER, dem Kollegen des SDK-Managers befassen, der früher mit diesem zusammengefasst war und ANDROID SDK AND AVD MANAGER hieß. Führen Sie diese Schritte zum Erstellen Ihres ersten virtuellen Android-Geräts aus:

1. Um den ANDROID SDK AND AVD MANAGER zu starten, klicken Sie das Symbol in der EclipseWerkzeugleiste an, auf das der Zeiger in Abbildung 3.8 deutet.

Das Dialogfeld ANDROID AVD MANAGER wird angezeigt.

Abbildung 3.8: Das Symbol für den ANDROID AVD MANAGER in der Eclipse-Werkzeugleiste

2. Klicken Sie rechts oben im Dialogfeld die Schaltfläche NEW an.

Daraufhin wird das in Abbildung 3.9 dargestellte Dialogfeld CREATE NEW ANDROID VIRTUAL DEVICE (AVD) angezeigt.

3. Geben Sie für dieses AVD im Feld NAME den Text 4_2_Default_WVGA ein.

Weitere Information zur Benennung Ihrer AVDs finden Sie im Kasten »AVD-Nomenklatur«.

4. Wählen Sie im Listenfeld TARGET die Option ANDROID 4.2 – API LEVEL 17.

5. Im Bereich SD CARD können Sie die Felder leer lassen.

Ihre App verwendet keine Speicherkarte. Sie würden die Option SD CARD nutzen, wenn Sie Daten auf einer SD-Speicherkarte speichern müssten. Wenn Sie später andere Emulatoren einrichten, können Sie hier die Kapazität der SD-Speicherkarte in Megabyte (MIB) eintragen, die erstellt werden soll. Dann wird eine emulierte Speicherkarte erstellt und im lokalen Dateisystem Ihres Rechners angelegt.

Abbildung 3.9: Das Dialogfeld CREATE NEW ANDROID VIRTUAL DEVICE (AVD)

6. Behalten Sie unter KEYBOARD und SKIN die Vorgabeeinstellungen bei.

Im Emulator ist es sinnvoll, wenn eine Hardwaretastatur vorhanden ist. Bei der Emulatoroberfläche werden standardmäßig Hardwaretasten eingeblendet.

7. Klicken Sie OK an.

Das Dialogfeld ANDROID VIRTUAL DEVICE MANAGER sollte nun wie in Abbildung 3.10 aussehen.

Abbildung 3.10: Die gerade erzeugte AVD im Dialogfeld ANDROID VIRTUAL DEVICE MANAGER

AVD-Nomenklatur

Passen Sie bei der Benennung Ihrer AVDs auf. Android wird in der realen Welt von vielen Geräten benutzt. Dazu zählen Telefone, E-Book-Lesegeräte, Netbooks und Tablets. Irgendwann werden Sie Ihre Apps für verschiedene Konfigurationen testen müssen. Daher kann es Ihnen später nur helfen, wenn Sie sich beim Erstellen Ihrer AVDs an eine gemeinsame Nomenklatur halten. Dann können Sie viel leichter erkennen, welches AVD Sie für welchen Zweck eingerichtet haben. Ich verwende bevorzugt diese Nomenklatur:

{TARGET_VERSION}_{SKIN}_{SCREENSIZE}[{_Options}]

Der Name 4_2_Default_WVGA verwendet. Die Zielplattform (TARGET_VERSION) ist Android 4.2. Der Versionsname 4.2 wird dabei in 4_2 umgewandelt. Die Unterstriche anstelle von Punkten werden verwendet, um die AVD-Namen zusammenzuhalten. Wenn ein einzelnes zusammenhängendes Wort für den AVD-Namen verwendet wird, kann das später hilfreich sein, wenn Sie fortgeschrittene AVD-Aufgaben über die Befehlszeile lösen.

Bei SKIN handelt es sich um den Namen der Oberfläche des Emulators. Emulatoren können verschiedene Skins (Themen) haben, durch die sie wie echte Geräte aussehen. Das Standardthema wird vom Android-SDK zur Verfügung gestellt.

Beim Wert SCREENSIZE handelt es sich um die Bildschirmgröße unter Bezug auf den guten alten VGA-Monitorstandard (Video Graphics Array). Die Vorgabe lautet WVGA800. Weitere Optionen sind beispielsweise QVGA und HVGA.

8. Schließen Sie das Dialogfeld ANDROID VIRTUAL DEVICE MANAGER.

Möglicherweise erhalten Sie nach der Erzeugung Ihres virtuellen Geräts die Fehlermeldung Android requires .class compatibility set to 5.0. Please fix project properties. Sollte das bei Ihnen der Fall sein, können Sie den Fehler dadurch beheben, dass Sie das Projekt in Eclipse mit der rechten Maustaste anklicken und im Kontextmenü ANDROID TOOLS:FIX PROJECT PROPERTIES wählen.

Launch-Konfigurationen erzeugen

Nun ist es fast so weit, dass Sie Ihre App laufen lassen können. Eine Run-Konfiguration gibt an, welches Projekt ausgeführt, welche Aktivität gestartet und mit welchem Emulator oder Gerät eine Verbindung hergestellt werden soll. Hoppla! Das sind zwar eine Menge Dinge auf einmal, aber nichts, worüber Sie sich Sorgen machen müssten, denn mit den ADT lassen sich etliche der wesentlichen Schritte automatisieren. Letztlich bekommen Sie Ihre App damit recht schnell zum Laufen.

Mit den Android-ADT haben Sie zwei Optionen zum Erstellen von Launch-Konfigurationen:

Run-Konfiguration: Diese benutzen Sie, wenn Sie Ihre App auf einem bestimmten Gerät ausführen wollen. Während Ihrer Karriere als Android-Entwickler werden Sie zumeist Run-Konfigurationen nutzen.

Debug-Konfiguration: Diese verwenden Sie, um Fehler in Ihren Apps zu beheben, während sie auf einem bestimmten Gerät laufen. Mehr über die Fehlersuche erfahren Sie in Kapitel 5.

Wenn Sie ein Projekt erstmals als Android-App über RUN:RUN AS starten, erstellen die ADT automatisch eine Run-Konfiguration für Sie. Dabei wird Ihnen die Option ANDROID APPLICATION angeboten. Wurde die Run-Konfiguration erst einmal erstellt, wird sie von da an jeweils als Vorgabekonfiguration verwendet, wenn Sie RUN:RUN wählen. RUN AS finden Sie als Option auch im Kontextmenü, wenn Sie ein Projekt im PACKAGE EXPLORER mit der rechten Maustaste anklicken.

Wenn Sie ein wenig ehrgeiziger sind und Ihre Run-Konfiguration lieber manuell erstellen wollen, lesen Sie hier weiter. Keine Sorge, das ist wirklich einfach. Führen Sie diese Schritte aus:

1. Wählen Sie RUN:RUN CONFIGURATIONS.

Das Dialogfeld RUN CONFIGURATIONS wird angezeigt (Abbildung 3.11). In diesem Dialogfeld können Sie vielerlei Arten von Konfigurationen erstellen. Auf der linken Seite des Dialogfelds werden diese aufgeführt, dabei sollten Sie aber nur auf die beiden folgenden achten:

• ANDROID APPLICATION

• ANDROID JUNIT TEST

2. Markieren Sie das Element ANDROID APPLICATION und klicken Sie das Symbol NEW LAUNCH CONFIGURATION an (siehe Abbildung 3.12).

Sie können auch ANDROID APPLICATION mit der rechten Maustaste anklicken und im Kontextmenü NEW wählen.

Abbildung 3.11: Das Dialogfeld RUN CONFIGURATIONS

Abbildung 3.12: Das Symbol NEW LAUNCH CONFIGURATION

Im Dialogfeld wird unter ANDROID APPLICATION ein neues Element erzeugt, dessen Eigenschaften rechts im Dialogfeld angezeigt werden.

3. Tragen Sie im Feld PROJECT den Text Beispielkonfiguration ein.

4. Auf der Registerkarte ANDROID wählen Sie das Projekt aus, für das Sie diese Launch-Konfiguration erstellen und klicken dazu die Schaltfläche BROWSE an.

Das Dialogfeld PROJECT SELECTION wird angezeigt.

5. Markieren Sie HALLO ANDROID und klicken Sie die Schaltfläche OK an (siehe Abbildung 3.13).

Das Dialogfeld RUN CONFIGURATIONS wird wieder angezeigt.

Abbildung 3.13: Auswahl des Projekts für die neue Launch-Konfiguration

6. Behalten Sie auf der Registerkarte ANDROID im Bereich LAUNCH ACTION die Voreinstellung LAUNCH DEFAULT ACTIVITY bei.

Die Vorgabeaktivität ist in diesem Fall MainActivity. Das haben Sie weiter vorn in diesem Kapitel im Abschnitt »Ein neues Projekt in Eclipse anlegen« festgelegt.

7. Auf der Registerkarte TARGET lassen Sie die Option AUTOMATICALLY PICK … ausgewählt (Abbildung 3.14).

Beachten Sie, dass im Bereich SELECT A PREFERRED ANDROID VIRTUAL DEVICE FOR DEPLOYMENT ein virtuelles Android-Gerät (AVD) aufgeführt wird.

8. Wählen Sie das Gerät 4_2_DEFAULT_WVGA aus.

Dabei handelt es sich um das vorhin erstellte virtuelle Android-Gerät. Wenn Sie es auswählen, startet diese Launch-Konfiguration dieses AVD, wenn die App über RUN:RUN gestartet wird. Auf dieser Registerkarte gibt es die Optionen ALWAYS PROMPT TO PICK DEVICE (früher MANUAL) und AUTOMATICALLY PICK ... Wenn Sie ALWAYS PROMPT TO PICK DEVICE wählen, können Sie bei Verwendung dieser Launch-Konfiguration jeweils auswählen, zu welchem Gerät eine Verbindung hergestellt wird. Bei der Option AUTOMATICALLY PICK … wird immer das hier festgelegte virtuelle Gerät beim Starten der App verwendet.

Abbildung 3.14: Eine neue, manuell erstellte Launch-Konfiguration namens BEISPIELKONFIGURATION

9.Lassen Sie die übrigen Einstellungen unverändert und klicken Sie die Schaltfläche APPLY an.

10. Schließen Sie das Dialogfeld über die Schaltfläche CLOSE.

Wenn Sie eine Launch-Konfiguration nicht mehr benötigen, markieren Sie sie im linken Fensterbereich und klicken das Delete-Symbol in der Werkzeugleiste an, um sie zu entfernen. Alternativ können Sie auch hier DELETE wieder im Kontextmenü auswählen.

Die App »Hallo Android« starten

Glückwunsch! Sie haben es geschafft! Bei den zum Starten einer Android-Anwendung erforderlichen Grundlagen handelt es sich um einen recht einfachen Vorgang, bei dem aber eine ganze Reihe von Einzelheiten beachtet werden sollten. Nun können Sie das Ergebnis Ihrer harten Arbeit in Aktion sehen. Sie haben eine Launch-Konfiguration und ein virtuelles Android-Gerät erstellt und können Ihre App damit starten. Endlich!

Die App im Emulator ausführen

Das Ausführen der Anwendung ist einfach. Auf Ihren Befehl hin starten die ADT einen Emulator mit der von Ihnen vorhin in diesem Kapitel erstellten und vorgegebenen Launch-Konfiguration. Um Ihre App zu starten, müssen Sie jetzt nur noch RUN:RUN wählen oder [Strg]+[F11] drücken. In beiden Fällen wird die Anwendung unter Verwendung der vorgegebenen Launch-Konfiguration in einem Emulator (hier: BEISPIELKONFIGURATION) gestartet. Die ADT kompilieren Ihre App und reichen sie an den Emulator weiter.

Wenn Sie RUN:RUN wählen, ohne zuvor eine Launch-Konfiguration erstellt zu haben, wird Ihnen entweder eine Fehlermeldung angezeigt (wählen Sie dann RUN:RUN AS) oder es wird Ihnen das Dialogfeld RUN AS angezeigt (Abbildung 3.15). Wenn Sie dann ANDROID APPLICATION wählen, wird eine Launch-Konfiguration für Sie erstellt.

Abbildung 3.15: Wenn noch keine Launch-Konfiguration für ein auszuführendes Projekt erstellt wurde, wird das Dialogfeld RUN AS (oder eine Fehlermeldung) angezeigt.

Wenn Sie die BEISPIELKONFIGURATION erzeugt haben, sollte der Emulator gestartet werden (Abbildung 3.16).

Eigentlich sollte Ihr Programm ja nun starten. Zuweilen wird aber doch eine Fehlermeldung angezeigt, was beispielsweise daran liegen könnte, dass Sie sich bei den Pfadangaben vertippt haben oder Eclipse vielleicht bereits einmal installiert hatten. Wählen Sie dann in der Menüleiste von Eclipse WINDOW:SHOW VIEW: PROBLEMS. Die dann angezeigten Informationen sollten Ihnen zumindest so weit helfen, dass Sie sich mit den Angaben im Internet auf die Suche nach der Fehlerursache begeben können.

Hilfe! Mein Emulator wird einfach nicht gestartet! Er bleibt immer beim ANDROID-Bildschirm hängen! Keine Sorge, Kamerad. Wenn der Emulator erstmals gestartet wird, kann das System durchaus bis zu zehn Minuten benötigen, bis es damit fertig ist. Das liegt daran, dass im Emulator ein virtuelles Linux-System ausgeführt wird. Das muss der Emulator erst einmal hochfahren und initialisieren. Und je langsamer Ihr Rechner ist, desto länger dauert das.

Abbildung 3.16: Der Emulator wird geladen, wobei die von ihm benutzte Portnummer und der Name des virtuellen Geräts in der Titelleiste des Fensters angezeigt werden.

Beim Hochfahren zeigt der Emulator mehrere verschiedene Bildschirme an. Den ersten zeigt Abbildung 3.16. In der Titelleiste wird die verwendete Portnummer (5554) und der AVD-Name (4_2_DEFAULT_WVGA) angezeigt. Während der Hälfte der für das Hochfahren benötigten Zeit wird dieser Bildschirm angezeigt.

Auf dem zweiten Bildschirm wird dann das Android-Logo angezeigt (Abbildung 3.16). Das Logo ist dasselbe, das Nutzern von Geräten mit dem Android-Betriebssystem standardmäßig nach dem Einschalten präsentiert wird, zumindest sofern der Gerätehersteller keine eigenen Anpassungen der Benutzeroberfläche vorgenommen hat, wie es zum Beispiel beim HTC Sense der Fall ist.

Der dritte und letzte Bildschirm zeigt dann den geladenen Emulator (Abbildung 3.17).

Abbildung 3.17: Der Emulator 4_2_DEFAULT_HVGA wurde hochgefahren.

Sie können wertvolle Zeit sparen, wenn Sie den Emulator laufen lassen. Dann muss er nicht jeweils hochgefahren werden, wenn Sie Ihre Anwendung starten wollen. Während der Emulator läuft, können Sie Ihre Quelltexte ändern und Ihre App neu starten. Die ADT finden den laufenden Emulator und übertragen Ihre App dorthin.

Wenn der Emulator seine Ladephase abgeschlossen hat, wird der gesperrte Vorgabebildschirm angezeigt (Abbildung 3.18). Um den Begrüßungsbildschirm zu entsperren, klicken Sie das Schloss-Symbol an und ziehen es nach rechts. Hierbei ändert sich typischerweise die Hintergrundfarbe des Schloss-Symbols und der Bildschirm wird entsperrt.

Abbildung 3.18: Entsperren des Startbildschirms im Emulator

Sie können die virtuellen Geräte (Emulatoren) wie echte Geräte konfigurieren und die für Deutschland üblichen Einstellungen vornehmen und beispielsweise das Datumsformat umstellen. Das erledigen Sie dann über SETTINGS:LANGUAGE & KEYBOARD:SELECT LANGUAGE. Derartige Einstellungen bleiben erhalten.

Wenn der Emulator entsperrt wurde, wird der Startbildschirm angezeigt (Abbildung 3.19).

Abbildung 3.19: Der Startbildschirm des Emulators nach geänderter Spracheinstellung

Unmittelbar danach starten die ADT die Anwendung HALLO ANDROID. Es sollte ein nahezu leerer Bildschirm und darauf die Worte Hello World angezeigt werden (Abbildung 3.20).

Glückwunsch! Sie haben gerade Ihre erste Android-App erstellt und gestartet.

Abbildung 3.20: Die App HALLO ANDROID im Emulator

Wenn Sie den Bildschirm nach dem Hochfahren des Emulators nicht entsperren, können die ADT Ihre Anwendung nicht starten. Wenn Sie den Startbildschirm entsperrt haben und Ihre App nicht innerhalb von fünf bis zehn Sekunden gestartet wird, starten Sie sie einfach erneut von Eclipse aus über RUN:RUN. Die Anwendung wird dann erneut zum Gerät übertragen und ausgeführt. Über den Status der Installation informiert Sie die Konsolenansicht in Eclipse (Abbildung 3.21).

Abbildung 3.21: Die Konsole informiert Sie in Eclipse darüber, was hinter den Kulissen geschieht, während Ihre App zum Gerät übertragen wird.

Status der Bereitstellung prüfen

In der Konsolenansicht können Sie Information sehen, die den Status der Bereitstellung Ihrer App betreffen. Es folgt ein entsprechendes Beispiel:

[2012-12-05 13:13:46 - Hallo Android] ------------------------------

[2012-12-05 13:13:46 - Hallo Android] Android Launch!

[2012-12-05 13:13:46 - Hallo Android] adb is running normally.

[2012-12-05 13:13:46 - Hallo Android] Performing com.dummies.android.

halloandroid.MainActivity activity launch

[2012-12-05 13:13:46 - Hallo Android] Automatic Target Mode: using

existing emulator ‘emulator-5554‘ running compatible AVD ‘2_2_Default_HVGA‘

[2012-12-05 13:13:48 - Hallo Android] Application already deployed.

No need to reinstall.

[2012-12-05 13:13:48 - Hallo Android] Starting activity com.dummies

.android.halloandroid.MainActivity on device

[2012-12-05 13:13:49 - Hallo Android] ActivityManager: Starting:

Intent { act=android.intent.action.MAIN cat= [android.intent.category.LAUNCHER] cmp=com.dummies. android.halloandroid/.MainActivity }

[2012-12-05 13:13:49 - Hallo Android] ActivityManager: Warning:

Activity not started, its current task has been  brought to the front

Die Konsolenansicht liefert wertvolle Informationen über den Status der Bereitstellung der Anwendung. Hier wird Ihnen mitgeteilt, dass eine Aktivität gestartet wird, welches Gerät die ADT nutzen, und es werden Warnungen wie in der letzten Zeile des Beispiels angezeigt:

[2012-12-05 13:13:49 - Hello Android] ActivityManager:
Warning: Activity not started, its current task has been brought to the front

Hier informieren die ADT Sie darüber, dass die Aktivität MainActivity in diesem Fall nicht gestartet wurde, weil sie bereits lief. Deshalb wurde dieser Task in den Vordergrund (auf den Android-Bildschirm) geholt, um ihn sichtbar zu machen.

Die Projektstruktur

Sie haben Ihre erste App erzeugt. Und dazu mussten Sie nicht einmal Quelltexte schreiben. Schön, dass die ADT Sie mit den für das schnelle Erstellen einer ersten Anwendung benötigten Tools versorgt. Beim Erstellen Ihrer nächsten Erfolgsanwendung wird Ihnen das aber nicht helfen. Am Anfang dieses Kapitels haben Sie eine einfache Android-App mit dem Assistenten NEW ANDROID APPLICATION erstellt. Von nun an werden Sie die Dateistrukturen nutzen, die der Android-Assistent für Sie erstellt hat.

Die folgenden Abschnitte sollten Sie nicht überspringen (sie sind wirklich wichtig!), da Sie während Ihrer gesamten Android-Entwicklungslaufbahn durch diese Ordner navigieren werden. Für die Android-Entwicklung ist es wesentlich, dass Sie wissen, welchem Zweck diese Ordner dienen und weshalb sie sich dort befinden.

Durch die Ordner der App navigieren

In Eclipse können Sie den Ordner des Projekts HALLO ANDROID erweitern. Dann sollte die Darstellung auf dem Bildschirm Abbildung 3.22 entsprechen.

Nachdem der Ordner des Projekts HALLO ANDROID erweitert wurde, finden Sie dort diese Unterordner:

src
gen

Abbildung 3.22: Das Projekt HALLO ANDROID mit erweiterter Ordnerstruktur im Package Explorer

Android-Version (zum Beispiel Android 4.2)

assets
res

Neben diesen Ordnern kann es in einem Android-Projekt noch weitere geben. Die aufgeführten Ordner werden vom Assistenten NEW ANDROID APPLICATION standardmäßig erstellt. Weitere Ordner können zum Beispiel bin, libs und Android Dependencies sein.

Zwei wichtige Dateien im Projekt sind AndroidManifest.xml und project.properties. Die Datei AndroidManifest.xml hilft Ihnen bei der Identifizierung der Komponenten, die die Anwendung zusammenbauen und ausführen, während Sie mit Hilfe der Datei project.properties die Vorgabeeigenschaften des Android-Projekts ermitteln können (wie die Android-Version).

All diese Ordner und Dateien werden in den folgenden Abschnitten ausführlicher beschrieben.

Der Ordner src (Source)

Im Ordner src befinden sich die Quelltexte Ihres Android-Projekts und damit der Dateiwinzling MainActivity.java, den Sie mit dem Assistenten NEW ANDROID APPLICATION vorhin in diesem Kapitel erstellt haben. Um sich den Inhalt des Ordners src ansehen zu können, müssen Sie ihn erweitern.

Führen Sie dazu diese Schritte aus:

1. Markieren Sie den Ordner src und klicken Sie links daneben das kleine Dreieck an, um ihn zu erweitern.

Es wird das Standardpaket com.dummies.android.halloandroid des Projekts angezeigt.

2. Markieren Sie das Standardpaket und erweitern Sie es.

Nach diesem Schritt wird die Datei MainActivity.java im Zweig des Pakets com. dummies.android.halloandroid angezeigt (Abbildung 3.23).

Abbildung 3.23: Im erweiterten Ordner src wird der Zweig der Datei MainActivity.java im Java-Paket com.dummies.android.halloandroid angezeigt.

Sie sind in Ihren Android-Apps nicht auf ein einziges Paket beschränkt. Tatsächlich empfiehlt es sich, in Ihren Java-Klassen die verschiedenen Komponenten mit Kernfunktionen in (mehrere) Pakete auszulagern. Ein Beispiel wäre eine Klasse, die für die Kommunikation mit einem Web-API über XML (eXtensible Markup Language) zuständig ist. Zudem kann es in Ihrer App Customer-Objekte geben, bei denen es sich um Domänenmodelle von Kunden handelt, die über die Web-API-Klassen abgerufen werden. Hier werden Sie möglicherweise zwei weitere Java-Pakete verwenden, die die zusätzlichen Java-Klassen enthalten:

com.dummies.android.halloandroid.models
com.dummies.android.halloandroid.http

Diese Pakete würden dann ihre jeweiligen Java-Komponenten enthalten. com.dummies.android.halloandroid.models würde die Java-Klassen für das Domänenmodell und com.dummies.android.halloandroid.http die für HTTP zuständigen Java-Klassen (Web-APIs) enthalten. Ein so eingerichtetes Android-Projekt könnte im PACKAGE EXPLORER wie in Abbildung 3.24 aussehen.

Abbildung 3.24: Ein Beispiel mit mehreren vorhandenen Paketen im Ordner src, die jeweils eigene Java-Klassen enthalten

Ordner der Android-Zielbibliothek

Einen Moment, ich habe den Ordner gen übersprungen! Damit werde ich mich ausführlicher befassen, wenn ich zum Ordner res komme. Erst einmal werde ich mich aber auf den Ordner mit der Android-Zielbibliothek konzentrieren. Dabei handelt es sich um keinen Ordner im eigentlichen Sinne, sondern eher um ein Element, das Ihnen von den ADT in Eclipse präsentiert wird.

Zu seinen Elementen zählt die Datei android.jar, mit dessen Unterstützung Ihre Anwendung zusammengebaut wird. Die Version dieser Datei wurde über die im Assistenten NEW ANDROID PROJECT festgelegte Zielplattform (build target) bestimmt. Wenn Sie das Element ANDROID 2.2 im Projekt erweitern, wird die Datei android.jar und der zugehörige Installationspfad angezeigt (Abbildung 3.25).

Der Ordner assets

Der Ordner assets ist standardmäßig leer. Er wird zum Speichern so genannter »raw asset files« genutzt. Diese Dateien können Assets oder Rohdaten beinhalten, die zum Funktionieren Ihrer Anwendung benötigt werden. Ein gutes Beispiel wäre eine Datei mit Daten in einem proprietären Format, die vom Gerät benötigt wird. Android umfasst den Asset Manager, der alle aktuell im Ordner assets vorhandenen Assets auflistet. Beim Lesen eines Assets könnte Ihre App die in der Datei enthaltenen Daten lesen. Wenn Sie eine App mit eigenem Wörterbuch zum Nachschlagen (vielleicht zur automatischen Vervollständigung) erstellen wollen, werden Sie das Wörterbuch vielleicht in das Projekt einbinden wollen, indem Sie dessen Datei (üblicherweise eine XML-Datei oder eine Binärdatei, wie zum Beispiel eine SQLite-Datenbank) im Ordner assets ablegen.

Abbildung 3.25: Die Version ANDROID 4.2 mit der Datei android.jar und dessen Position

Android verwendet bei Assets eine recht laxe Ressourcenverwaltung. Es gibt keine Einschränkungen hinsichtlich der im Ordner assets abgelegten Daten. Beachten Sie aber, dass die Arbeit mit Assets ein wenig mühsamer als die mit Ressourcen werden kann, weil Sie dann mit Byteströmen arbeiten und diese erst in die gewünschten Objekte (Audio, Video, Text und so weiter) umwandeln müssen.

Assets werden im Gegensatz zu den Ressourcen im Ordner res keine Ressourcen-IDs zugewiesen. Wenn Sie auf die Inhalte zugreifen wollen, müssen Sie manuell mit Bits, Bytes und Datenströmen hantieren.

Der Ressourcenordner res

Der Ordner res enthält die verschiedenen von Ihrer Anwendung benötigten Ressourcen. Die von Ihren Anwendungen verwendeten Ressourcen sollten Sie immer extern speichern. Zu den klassischen Beispielen für derartige Ressourcen zählen Zeichenketten und Bilder. So sollten Sie es beispielsweise vermeiden, Strings in Ihre Quelltexte zu integrieren. Stattdessen erstellen Sie eine String-Ressource und verweisen in Ihrem Quelltext darauf. Wie das geht, werde ich Ihnen später in diesem Buch noch zeigen. Derartige Ressourcen sollten Sie in dem am besten dafür geeigneten Unterverzeichnis des Ordners res zusammenstellen und ablegen.

Sie sollten auch für bestimmte Gerätekonfigurationen alternative Ressourcen bereitstellen und sie in passend benannten Ressourcenordnern zusammenfassen. Während der Laufzeit ermittelt Android, auf welcher Konfiguration die Anwendung läuft, und wählt die entsprechende Ressource (oder den Ressourcenordner) aus, um sie zu laden. Vielleicht werden Sie beispielsweise unterschiedliche Benutzeroberflächen für verschiedene Bildschirmabmessungen oder je nach Spracheinstellung andere Strings verwenden wollen.

Wenn Sie Ihre Ressourcen extern ablegen, können Sie darauf im Code über Ressourcen-IDs zugreifen, die von den ADT in der Klasse R erzeugt werden. (Mehr dazu erfahren Sie im Abschnitt »Der mysteriöse Ordner gen« weiter hinten in diesem Kapitel.

Sie sollten alle Ressourcen in bestimmten Unterverzeichnissen des zu Ihrem Projekt gehörenden res-Ordners ablegen. Bei den in Tabelle 3.1 aufgeführten Ordnern handelt es sich um die gängigsten der dort vorhandenen Ressourcenordner.

Verzeichnis | Ressourcentyp
anim/ | XML-Dateien, die Animationen definieren
color/ | XML-Dateien, die Farblisten definieren

drawable/

Bitmap-Dateien (.png, .9.png, .jpg, .gif) oder XML-Dateien, die zu den folgenden grafischen Oberflächenelementen (Drawables) kompiliert werden können

drawable-xhdpi/

Grafische Oberflächenelemente für hochauflösende Bildschirme. Die Abkürzung XHDPI steht dabei für »Extra High DPI«. Der Inhalt ist derselbe wie beim Ressourcenordner drawable/, nur dass alle hier gespeicherten Bitmap- oder XML-Dateien zu besonders hochauflösenden Drawables kompiliert wurden.

drawable-hdpi/

Grafische Oberflächenelemente für hochauflösende Bildschirme. Die Abkürzung HDPI steht dabei für »High Dots Per Inch«. Der Inhalt ist derselbe wie beim Ressourcenordner drawable/, nur dass alle hier gespeicherten Bitmap- oder XML-Dateien zu hochauflösenden Drawables kompiliert wurden.

drawable-ldpi/

Grafische Oberflächenelemente für Bildschirme mit niedriger Dichte (LDPI – Low Dots Per Inch). Der Inhalt ist derselbe wie beim Ressourcenordner drawable/, nur dass alle hier gespeicherten Bitmap- oder XML-Dateien zu Drawables mit niedriger Dichte kompiliert wurden.

drawable-mdpi/

Grafische Oberflächenelemente für Bildschirme mit mittlerer Dichte (MDPI – Medium Dots Per Inch). Der Inhalt ist derselbe wie beim Ressourcenordner drawable/, nur dass alle hier gespeicherten Bitmap- oder XML-Dateien zu Drawables mit mittlerer Dichte kompiliert wurden.

layout/ | XML-Dateien, die das Layout einer Benutzeroberfläche definieren
menu/ | XML-Dateien, die Anwendungsmenüs darstellen
raw/ | Beliebige in ihrem Rohformat gespeicherte Dateien. Dateien in diesem Verzeichnis werden vom System nicht komprimiert.

values/

XML-Dateien, in denen einfache Werte für Zeichenketten, Ganzzahlen und Farben gespeichert werden. Während XML-Ressourcendateien in den anderen Unterverzeichnissen des Ordners res auf der Grundlage der XML-Dateinamen einzelne Ressourcen definieren, enthalten die Dateien im Unterverzeichnis values/ mehrere Ressourcen für verschiedene Einsatzzwecke.

Tabelle 3.1: Unterstützte Unterverzeichnisse des Ordners res

»Auflösungen« werden für Bildschirme und Digitalkameras meist in Pixeln (zum Beispiel 1.920x1.080 Bildschirmpunkte), für Drucker und Scanner in dpi (zum Beispiel 1.200x1.200 dpi) angegeben. Die letzte der beiden Varianten wird auch »Dichte« (Punktdichte oder Pixeldichte) genannt und gibt an, wie viele Punkte je Quadratzoll ein Gerät darstellen kann. Da die horizontale und vertikale Punktdichte verschieden sein können, werden oft zwei Werte angegeben. Wird nur ein Wert angegeben, sind horizontale und vertikale Punktdichte normalerweise gleich. Typische Werte für die Bildschirme von Desktoprechnern sind 1.920x1.080 Pixel bei 90 bis 100 dpi. Bei Bildschirmen von Netbooks, Mobiltelefonen oder Tablets sind die einzelnen Bildpunkte heute oft deutlich kleiner (beispielsweise 118 dpi bei einem typischen 10,1-Zoll-Bildschirm mit 1.024x600 Bildpunkten).

Benennung von Ressourcen im Verzeichnis values

Für die Ressourcen, die Sie im Verzeichnis values erstellen können, gibt es einige Dateinamenkonventionen:

arrays.xml für Array-Ressourcen (gemeinsame Speicherung von ähnlichen Elementen wie Strings oder Ganzzahlen)

colors.xml für Ressourcen, die Farbwerte definieren und auf die über die Klasse R.colors zugegriffen werden kann
dimens.xml für Ressourcen, die Abmessungen definieren (20px entspricht beispielsweise 20 Bildpunkten) und auf die über die Klasse R.dimens zugegriffen werden kann
strings.xml für Stringwerte, auf die über die Klasse R.strings zugegriffen werden kann
styles.xml für Ressourcen, die Formatvorlagen entsprechen, auf die über die Klasse R.styles zugegriffen werden kann. Diese »Styles« ähneln dabei den »kaskadierenden Formatvorlagen« (CSS – Cascading Style Sheet) in HTML. Sie können viele Formate definieren, die Eigenschaften voneinander erben können.

Speichern Sie niemals Ressourcendateien direkt im Ordner res. Sollten Sie es doch einmal tun, wird Sie der Compiler dafür mit einer Fehlermeldung bestrafen.

Die in Tabelle 3.1 aufgeführten und in den verschiedenen Unterverzeichnissen abgelegten Ressourcen definieren das standardmäßige Aussehen Ihrer Android-App. Für die unterschiedlichen Android-Geräte werden aber möglicherweise andere Ressourcen benötigt. Wenn der Bildschirm eines Geräts beispielsweise größer als weitgehend üblich ist, müssen Sie dementsprechende alternative Layoutressourcen bereitstellen.

Die resource/-Mechanismen von Android sind äußerst leistungsfähig. Darüber ließe sich leicht ein eigenes Kapitel schreiben, aber wir werden uns in diesem Buch nur auf die Grundlagen beschränken, die Sie für den Einstieg benötigen. Die resource/-Mechanismen können Sie bei der Anpassung an verschiedene Sprachen und Länder, Bildschirmabmessungen und Auflösungen und/oder sogar Ressourcen für verschiedene Gerätemodi unterstützen. Wenn Sie sich eingehend mit dem Bereich der Ressourcen befassen wollen, erfahren Sie mehr darüber im Abschnitt »Providing Resources« des Entwicklerhandbuchs (Dev Guide) in der Android-Dokumentation, den Sie unter der Adresse http://d.android.com/guide/topics/ resources/providing-resources.html finden können.

Die Ordner bin, libs und referenzierte Bibliotheken

Bibliotheksordner werden von Ihrer App HALLO ANDROID eigentlich nicht benötigt, Sie sollten aber einige dieser zusätzliche Ordner dennoch kennen. Bei einem davon handelt es sich um das Verzeichnis libs/. Er kann Bibliotheken (libraries) von Drittanbietern enthalten, die Ihnen bestimmte Funktionen zur Verfügung stellen. Ein Beispiel wäre JTwitter, eine JavaBibliothek für das Twitter-API. Wenn Sie JTwitter in Ihre Android-App einbinden wollen, könnten Sie die Bibliothek jtwitter.jar beispielsweise im Verzeichnis libs ablegen.

Wenn Sie eine Bibliothek im Ordner libs abgelegt haben, müssen Sie sie noch zu Ihrem Java-Build-Pfad hinzufügen. (Damit sie angezeigt wird, müssen Sie möglicherweise [F5] drücken, um die Ansicht zu aktualisieren.) Dabei handelt es sich um den Pfad zur Klasse, der beim Zusammenbauen eines Java-Projekts verwendet wird. Wenn Ihr Projekt von Bibliotheken von Drittanbietern abhängig ist, muss Eclipse wissen, wo es diese finden kann. Und genau das teilen Sie Eclipse durch Setzen des Build-Pfads mit. Wenn Sie jtwitter.jar beispielsweise in das Verzeichnis libs kopiert haben, können Sie es leicht zu Ihrem Build-Pfad hinzufügen. Klicken Sie dazu die Datei jtwitter.jar mit der rechten Maustaste an und wählen Sie im Kontextmenü BUILD PATH:ADD TO BUILD PATH.

Beim Hinzufügen von JTwitter zum Build-Pfad ist Ihnen vielleicht aufgefallen, dass dabei der Ordner Referenced Libraries erstellt wurde (Abbildung 3.26). Dieser Ordner soll Sie darüber informieren, auf welche Bibliotheken in Ihrem Eclipse verwiesen wird.

Mehr über JTwitter erfahren Sie unter www.winterwell.com/software/jtwitter.php.

Wir werden den Ordner libs in diesem Buch nicht verwenden. Entwickler greifen aber in Android-Apps häufig auf Bibliotheken von Dritten zurück.

Der mysteriöse Ordner gen

Und schließlich sollen Sie auch noch erfahren, was es mit dem geheimnisvollen Ordner gen auf sich hat. Wenn Sie eine Android-App erstellen, existiert dieser Ordner erst einmal nicht. Die ADT erstellen ihn und seinen Inhalt aber beim erstmaligen Kompilieren des Projekts.

Der Ordner gen enthält die von den ADT erzeugten Java-Dateien. Die ADT erstellen eine Datei namens R.java (mehr dazu gleich). Ich habe den Ordner res vor dem Ordner gen behandelt, weil Letzterer Elemente enthält, die aus dem Inhalt des Ersteren erzeugt werden. Wenn Sie nicht wissen, worum es sich beim Ordner res handelt und welchem Zweck er dient, fehlen Ihnen alle Hinweise auf die Funktion des Ordners gen. Da Sie nun aber bereits wissen, was es mit dem Ordner res auf sich hat, kann ich mich auch dem Ordner gen zuwenden.

Abbildung 3.26: Der Ordner libs mit jtwitter.jar

Wenn Sie in Android Java-Code schreiben, werden Sie irgendwann auf die Elemente im Ordner res verweisen müssen. Das machen Sie über die Klasse R. Bei der Datei R.java handelt es sich um einen Index für alle im Ordner res definierten Ressourcen. Sie verwenden diese Klasse als Kürzel, um auf Ressourcen zu verweisen, die Sie in Ihr Projekt eingebunden haben. Das ist insbesondere im Zusammenhang mit der Codevervollständigung von Eclipse nützlich, weil Sie die richtigen Ressourcen damit schnell ausfindig machen können.

Wenn Sie im Projekt HALLO ANDROID den Ordner gen und dann den darin enthaltenen Paketnamen erweitern, wird die Datei angezeigt. Klicken Sie nun die Datei R.java doppelt an, um sie zu öffnen. Sie sehen eine Java-Klasse, die verschachtelte Java-Klassen enthält. Diese tragen dieselben Namen wie einige der im Abschnitt »Der Ressourcenordner res« beschriebenen Unterverzeichnisse. Unterhalb der Unterklassen werden Mitglieder aufgeführt, die dieselben Namen wie die Ressourcen in den jeweiligen Unterverzeichnissen des Ordners res tragen (einmal abgesehen von ihren Dateinamenserweiterungen).

Die Datei R.java des Projekts HALLO ANDROID sollte etwa so aussehen:

/* AUTO-GENERATED FILE.  DO NOT MODIFY.

*

* This class was automatically generated by the

* aapt tool from the resource data it found.  It

* should not be modified by hand.

*/

package com.dummies.android.halloandroid;

public final class R {

public static final class attr {

}

public static final class drawable {

public static final int icon=0x7f020000;

}

public static final class layout {

public static final int main=0x7f030000;

}

public static final class string {

public static final int app_name=0x7f040001;

public static final int hello=0x7f040000;

}

}

Und was hat es mit all dem 0X-Zeug auf sich? Die ADT erzeugen diesen Code für Sie, damit Sie sich nicht darum kümmern müssen, was hinter den Kulissen vorgeht. Wenn Sie Ressourcen hinzufügen und das Projekt erneut kompiliert wird, erzeugen die ADT die Datei R.java neu. Die Datei enthält dann Mitglieder, die auf die kürzlich hinzugefügten Ressourcen verweisen.

Sie sollten die Datei R.java nie manuell bearbeiten. Ansonsten wird Ihre App möglicherweise nicht mehr kompiliert. Wenn Sie die Datei einmal versehentlich ändern sollten und sich die Änderungen nicht mehr rückgängig machen lassen, können Sie aber zwecks Fehlerbehebung den Ordner gen einfach löschen und Ihr Projekt neu erzeugen lassen. Dann erzeugen die ADT auch die Datei R.java neu.

Die Manifest-Datei Ihrer App

Sie führen all Ihre Besitztümer in Listen auf, oder etwa nicht? Aber das ist genau das, was eine Manifest-Datei bei der Arbeit mit Android macht. Es zeichnet alles auf, was Ihre Anwendung braucht, anfordert und benötigt, um laufen zu können.

Die Manifest-Datei wird im Hauptverzeichnis Ihres Projekts gespeichert und trägt den Namen AndroidManifest.xml. Bei allen Anwendungen muss eine Android-Manifest-Datei in Ihrem Hauptverzeichnis liegen.

Die Manifest-Datei einer Anwendung liefert dem Android-System alle wesentlichen Informationen, die es haben muss, bevor es den Code Ihrer App ausführen kann. Die Manifest-Datei Ihrer Anwendung enthält folgende Angaben:

Den Namen des Java-Pakets für die Anwendung, bei dem es sich um die eindeutige Kennung Ihrer App im Android-System und auf dem Android-Marktplatz handelt

Die Komponenten der Anwendung, wie zum Beispiel die Aktivitäten und Hintergrunddienste

Die Deklaration der Berechtigungen, die erforderlich sind, damit Ihre App ausgeführt werden kann

Die Mindestversion des Android-APIs, die für Ihre App benötigt wird

Die Manifest-Datei enthält auch die Version Ihrer Anwendung. Sie müssen Ihre App mit Versionsangaben versehen. Hier müssen Sie in etwa wie bei den Versionsangaben des Android-Betriebssystems vorgehen. Es ist wichtig, dass Sie bereits frühzeitig im Entwicklungsprozess festlegen, wie Sie bei den Angaben für die aktuelle und die zukünftigen Versionen Ihrer App vorgehen wollen. Dabei müssen alle Anwendungen einen Versionscode und einen Versionsnamen haben. Auf diese Werte werde ich in den folgenden Abschnitten näher eingehen.

Versionscode

Beim Versionscode handelt es sich um einen Integer-Wert, der die Version des Anwendungscodes in Relation zu anderen Versionen Ihrer App angibt. Dieser Wert soll andere Anwendungen bei der Feststellung ihrer Kompatibilität mit Ihrer App unterstützen. Zudem benutzt der Android-Marktplatz den Versionscode als Grundlage für die interne Erkennung Ihrer App und den Umgang mit Updates.

Sie können für den Versionscode zwar einen beliebigen Integer-Wert verwenden, sollten aber dafür sorgen, dass dieser bei allen späteren Versionen höher als bei den bisherigen ist.

Typischerweise setzt man den Versionscode anfangs auf 1. Dann erhöht man monoton den Wert mit jeder neu veröffentlichten größeren oder kleineren Änderung. Das bedeutet, dass der Versionscode keine große Ähnlichkeit mit der für den Anwender sichtbaren Versionsangabe haben muss, bei der es sich um den Versionsnamen handelt (siehe nächster Abschnitt). Der interne Versionscode wird den Anwendern von Apps üblicherweise nicht angezeigt.

Wenn Sie den Code Ihrer App aktualisieren und sie veröffentlichen, ohne Ihren Versionscode zu erhöhen, werden verschiedene Versionen Ihrer App unter demselben Versionscode veröffentlicht. Stellen Sie sich vor, dass Sie Ihre App mit Versionscode 1 erstmals veröffentlichen. Ein Anwender installiert Ihre Anwendung über Google Play, bemerkt einen Fehler darin und informiert Sie darüber. Sie beheben den Fehler im Code, kompilieren und veröffentlichen die neue Codebasis aber, ohne den Versionscode in der Manifest-Datei von Android zu aktualisieren. Jetzt weiß Google Play nicht, dass sich etwas geändert hat, weil nur der Versionscode in der Manifest-Datei der Anwendung geprüft wird. Wenn der Versionscode auf einen höheren Wert als 1, also vielleicht 2, geändert wurde, erkennt Google Play hingegen, dass es sich um ein Update handelt, und informiert die Anwender über die verfügbare Aktualisierung, die noch Ihre App mit dem Versionscode 1 installiert haben. Wenn Sie den Versionscode nicht aktualisieren, würden die Anwender Ihrer App hingegen nie auf das Update hingewiesen werden und weiterhin die fehlerhafte Version benutzen. Und das dürfte niemandem gefallen!

Versionsname

Bei dem Versionsnamen handelt es sich um einen String-Wert, der die Version einer App angibt, so wie sie für die Anwender sichtbar sein soll. Bei diesem Wert handelt es sich um einen String, der der üblichen Nomenklatur bei Programmversionen folgt:

<Hauptversion>.<Unterversion>.<Stand>

Ein Beispiel für diese Versionsangabe wäre 2.1.4 oder, ohne den <Stand> (hier 4), 2.1.

Das Android-System nutzt diesen Wert nicht. Er dient nur dem Zweck, dass Apps ihn ihren Benutzern anzeigen können.

Beim Versionsnamen kann es sich auch um beliebige andere absolute oder relative Versionskennungen handeln. Die Anwendung Foursquare verwendet beispielsweise ein Schema von Versionsnamen, das dem Datum entspricht. Ein Beispiel für eine derartige Versionsangabe, die klar als Datum erkennbar ist, wäre 2012-12-28. Welche Versionsnamen Sie verwenden wollen, bleibt Ihnen überlassen. Sie sollten aber vorausschauend planen und dafür sorgen, dass das für Ihre Versionsangaben verwendete Verfahren für Sie und Ihre Anwender sinnvoll bleibt.

Berechtigungen (Permissions)

Nehmen Sie an, dass Ihre App, um bestimmte Daten zu übernehmen, auf das Internet zugreifen kann. Standardmäßig beschränkt Android den Internet-Zugriff. Damit Ihre App auf das Internet zugreifen kann, müssen Sie um die entsprechende Berechtigung bitten.

In der Manifest-Datei der Anwendung müssen Sie festlegen, welche Berechtigungen benötigt werden, damit sie läuft. In Tabelle 3.2 werden einige der besonders häufig angeforderten Berechtigungen aufgeführt.

Berechtigung | Beschreibung
Internet | Die Anwendung benötigt Zugang zum Internet.
Write External Storage | Die Anwendung muss Daten auf die Speicherkarte (SD Card – Secure Digital Card) schreiben können.
Camera | Die Anwendung muss auf eine Kamera zugreifen können.

Access Fine Location

Die Anwendung muss die GPS-Position (Global Positioning
System) feststellen können.

Read Phone State

Die Anwendung muss den Status des Telefons ermitteln und
feststellen können, ob es zum Beispiel klingelt.

Tabelle 3.2: Häufig für Apps angeforderte Berechtigungen

Die Datei project.properties

Die Datei project.properties wird in Verbindung mit ADT und Eclipse verwendet. Sie enthält Projekteinstellungen, wie zum Beispiel die Zielplattform. Diese Datei ist ein wesentlicher Bestandteil des Projekts und sollte daher nicht abhandenkommen!

Die Datei project.properties sollte nie manuell editiert werden. Um ihren Inhalt zu bearbeiten, verwenden Sie den Editor in Eclipse. Ihn erreichen Sie, wenn Sie den Projektnamen im PACKAGE EXPLORER mit der rechten Maustaste anklicken und im Kontextmenü PROPERTIES wählen. Daraufhin wird der Editor für die Projekteigenschaften angezeigt (Abbildung 3.27).

Abbildung 3.27: Der Properties-Editor in Eclipse

Mit diesem Editor können Sie die verschiedenen Projekteigenschaften ändern, indem Sie eine der Kategorien links im Dialogfeld auswählen und im rechten Fensterbereich deren Einstellungen bearbeiten. So können Sie zum Beispiel links ANDROID auswählen und den Pfad zum Android-SDK ändern.

Ihr Projekt schließen

Wenn Sie Ihre Anwendung erstellt haben, müssen Sie alle in Eclipse offenen Dateien erst einmal schließen, bevor Sie damit beginnen können, Ihre nächste App zu erstellen. Dazu können Sie die Dateien entweder einzeln schließen oder die entsprechenden Reiter mit der rechten Maustaste anklicken und im Kontextmenü CLOSE ALL wählen.

Wenn Sie alle Dateien geschlossen haben, müssen Sie noch das Projekt selbst schließen. Klicken Sie dazu in Eclipse im Package Explorer das Projekt mit der rechten Maustaste an und wählen Sie im Kontextmenü Close Project. Damit teilen Sie Eclipse mit, dass Sie aktuell nicht mehr mit dem Projekt arbeiten wollen. Dann kann Eclipse einige verwendete Ressourcen freigeben, da der Projektstatus nicht mehr protokolliert werden muss, was Ihre Anwendungen ein wenig beschleunigen kann.

Eclipse-Macken

Wenn Sie Android-Apps mit Eclipse erstellen, können Sie es zuweilen mit recht seltsamen und irreführende Fehlermeldungen zu tun bekommen. Hier und da werden Sie in den folgenden Kapiteln Hinweise auf derartige Macken und entsprechende Maßnahmen finden. Auf einige soll aber hier bereits hingewiesen werden.

Fehlende import-Anweisungen

Wenn Sie Android-Apps mit Eclipse erstellen, werden Sie immer wieder die für verwendete Komponenten benötigen Module importieren müssen. Das können Sie zwar einerseits jeweils bei den Befehlen erledigen, für die Fehler angezeigt werden, Sie können aber auch das betreffende Projekt öffnen und im PACKAGE EXPLORER markieren und im Kontextmenü SOURCE:ORGANIZE IMPORTS wählen. Das hat zudem den Vorteil, dass Sie sich nicht mit den möglicherweise irreführenden Fehlerbeschreibungen auseinandersetzen müssen, die an den jeweiligen Stellen im Quelltext angezeigt werden.

Projekte bereinigen

Unter bestimmten Umständen kann es vorkommen, dass die Fehlermeldung »Error: Your project contains errors, please fix them ...« angezeigt wird, obwohl (mit den Quelltexten) eigentlich alles in Ordnung ist. In derartigen Fällen können Sie in Eclipse im Menü PROJECT die Option CLEAN wählen. Über das Dialogfeld CLEAN (Abbildung 3.28) können Sie dafür sorgen, dass Ihr Projekt neu aufgebaut wird. In Abbildung 3.28 wird dies für das später in diesem Buch noch zu erstellende Projekt Terminplaner erledigt.

Abbildung 3.28: Ein Projekt bereinigen

4

Design der Benutzeroberfläche

In diesem Kapitel

Die Anwendung »Lautlosmodus-Umschalter« einrichten

Das Layout der Benutzeroberfläche gestalten

Die Benutzeroberfläche entwickeln

Ein Bild und eine Schaltfläche als Widget hinzufügen

Ein Startsymbol erstellen

Vorschau Ihrer Arbeit

In Kapitel 3 haben Sie erfahren, was Android ist und wie Sie Ihre erste Anwendung erstellen können. Damit kommen wir zu den Teilen, die wirklich Spaß machen. Sie werden eine echte Anwendung erstellen, die Sie benutzen und über Google Play veröffentlichen können.

Mit der Anwendung, die Sie erstellen werden, können deren Benutzer den Alarmmodus ihres Telefons einfach durch Antippen einer Schaltfläche umschalten. Das klingt zwar einfach, löst aber ein praktisches Problem.

Stellen Sie sich vor, dass Sie sich bei der Arbeit befinden und ein Meeting ansteht. Sie können die Lautstärke Ihres Telefons vollständig bis zur Lautlosigkeit hinunterregeln und dem Treffen beiwohnen. (Sie wollen doch bestimmt nicht »dieser Typ« sein, dessen Telefon während eines Meetings klingelt, oder?) Das Problem dabei ist, dass Sie es bevorzugen, wenn Ihr Telefon zwar laut, aber nicht allzu laut klingelt. Sie verwenden nie die lauteste, sondern immer nur die zweitlauteste Einstellung. Wenn Sie das Meeting verlassen, denken Sie zwar daran, die Lautstärke Ihres Alarms wieder heraufzusetzen, müssen dazu aber immer bis zur maximalen Lautstärke hochregeln und wieder eine Stufe zurückschalten, um die gewünschte Einstellung zu erhalten. Das ist zwar bestimmt nichts wirklich Schwerwiegendes, es nervt Sie aber regelmäßig, dass Sie diese Schritte jeweils vornehmen müssen, wenn Sie den Klingelton mal wieder abschalten mussten.

Es wäre also toll, wenn Sie eine App hätten, mit der Sie den Klingelton mit einem Fingertipp abschalten und beim Verlassen des Meetings mit einem erneuten Fingertipp wieder mit der zuletzt gewählten Einstellung aktivieren könnten. Nie wieder müssten Sie die Lautstärke Ihres Klingeltons neu einstellen. Und genau eine solche Anwendung werden Sie nachfolgend erstellen.

Erstellen der App »Lautlosmodus-Umschalter«

Erstellen Sie eine neue Anwendung durch Auswahl von FILE:NEW:PROJECT. Wählen Sie ANDROID:ANDROID APPLICATION PROJECT aus der Liste aus und klicken Sie die Schaltfläche NEXT an. Verwenden Sie für die Projekteinstellungen auf den verschiedenen Seiten des Dialogfelds die Angaben aus Tabelle 4.1 und klicken Sie jeweils NEXT an.

Einstellung | Wert
APPLICATION NAME | Lautlosmodus−Umschalter
PROJECT NAME | Lautlosmodus−Umschalter
CONTENTS | Behalten Sie die Voreinstellung bei (CREATE PROJECT IN WORKSPACE)
TARGET SDK | ANDROID 4.2
COMPILE WITH | ANDROID 4.2
PACKAGE NAME | com.dummies.android.lautlosmodus
CREATE ACTIVITY | Kontrollkästchen (jeweils) aktivieren (BlankActivity mit Namen MainActivity)
MIN REQUIRED SDK | 8 (2.2)

Tabelle 4.1: Projekteinstellungen für die App »Lautlosmodus-Umschalter«

Klicken Sie die Schaltfläche FINISH an. Nun sollte die App LAUTLOSMODUS-UMSCHALTER im PACKAGE EXPLORER angezeigt werden (Abbildung 4.1).

Abbildung 4.1: Die App LAUTLOSMODUS-UMSCHALTER in Eclipse

Wenn eine Fehlermeldung wie »The project cannot be built until build path errors are resolved« angezeigt wird, können Sie das Problem dadurch beheben, dass Sie das Projekt mit der rechten Maustaste anklicken und im Kontextmenü ANDROID TOOLS:FIX PROJECT PROPERTIES wählen. Dadurch wird Ihr Projekt im IDE-Arbeitsbereich neu eingeordnet. Bei anderweitigen Fehlern finden Sie im Unterfenster PROBLEMS, das Sie über die Option WINDOW:SHOW VIEW:PROBLEMS in der Eclipse-Menüleiste einblenden können, weitere Hinweise.

Beachten Sie, dass Sie als Zielplattform ANDROID 4.2 gewählt und bei MIN REQUIRED SDK den Wert 8 eingetragen haben. Damit haben Sie Android mitgeteilt, dass der Code auf beliebigen Geräten mit mindestens dem Versionscode 8 (Android 2.2) läuft. Wenn Sie den Wert auf den Versionscode 16 ändern würden, könnte Ihre App auf allen Geräten ausgeführt werden, auf denen mindestens Android 4.1 läuft. Wenn Sie eine neue App erstellen, sollten Sie entscheiden, ob sie auch unter älteren Versionen laufen soll.

Sollten Sie nicht mehr wissen, wie Sie eine neue Android-App in Eclipse erstellen können, erfahren Sie mehr dazu in Kapitel 3.

Layout der Anwendung

Nachdem Sie die App LAUTLOSMODUS-UMSCHALTER in Eclipse erstellt haben, ist es an der Zeit, ihre Bedienschnittstelle zu entwerfen. Benutzer interagieren über die Bedienschnittstelle mit Ihrer App. Bei diesem Teil Ihrer Anwendung ist es besonders wichtig, dass Sie ihn in jeder Hinsicht möglichst flott gestalten.

Ihre App soll eine einzige Schaltfläche zentriert in der Mitte des Bildschirms haben, um darüber den Lautlosmodus umschalten zu können. Direkt oberhalb der Schaltfläche soll sich ein Bild befinden, das den Modus optisch anzeigt und den Benutzer darüber informiert, ob sich das Telefon im Lautlosmodus oder im normalen Betriebsmodus befindet. Da ein Bild mehr als tausend Worte sagt, werfen Sie einen Blick auf Abbildung 4.2 und Abbildung 4.3, um sich anzuschauen, wie die fertige App später aussehen soll.

Abbildung 4.2: Die App LAUTLOSMODUS-UMSCHALTER im normalen Betriebsmodus

Abbildung 4.3: Die App Lautlosmodus-Umschalter im Lautlosmodus

Die XML-Layoutdatei nutzen

Alle Layoutdateien für Ihre App werden im Ordner res/layouts Ihres Android-Projekts in Eclipse gespeichert. Als Sie vorhin die App LAUTLOSMODUS-UMSCHALTER angelegt haben, wurde von den ADT (Android Development Tools) eine Datei namens activity_main.xml im Ordner res/layouts erstellt. Dabei handelt es sich um die vorgegebene Layoutdatei, die von den ADT beim Anlegen einer neuen Anwendung für Sie erstellt wird.

Klicken Sie diese Datei doppelt an, um sie zu öffnen. Dann sollte im Fenster des Eclipse-Editors ein wenig XML-Code angezeigt werden (Abbildung 4.4).

Wenn Sie das ADT-Bundle installiert haben, werden Ihnen direkt nach dem Erstellen gleich mehrere Fensterbereiche angezeigt. Dazu zählen:

links der Package Explorer
rechts oben ein Bereich, in dem Sie das Layout Ihrer App grafisch oder über XML-Quelltext gestalten können
rechts unten ein Fensterbereich, in dem beispielsweise nähere Informationen zu aufgetretenen Problemen angezeigt werden

Am unteren Rand des Layout-Fensterbereichs können Sie über die Reiter zwischen GRAPHICAL LAYOUT (oder LAYOUT), also einer grafischen Layoutdarstellung, und der XML-Darstellung umschalten. Sollte bei Ihnen das grafische Layout angezeigt werden, klicken Sie den Reiter ACTIVITY_MAIN.XML an, um zur Codeanzeige umzuschalten. Auf die grafische Variante werde ich gleich noch näher eingehen.

Abbildung 4.4: Die in Eclipse geöffnete Layoutdatei activity_main.xml

Abbildung 4.4 enthält den Quelltext für ein einfaches Layout, bei dem ein Textwert mitten auf dem Bildschirm angezeigt wird. Nur um sicherzugehen, dass bei Ihnen dieselbe Seite angezeigt wird, sollte der Code so aussehen:

<RelativeLayout xmlns:android=

"http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent" >

tools:context=".MainActivity" />

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerHorizontal="true"

android:layout_centerVertical="true"

android:text="@string/hello_world"

</RelativeLayout>

Diese XML-Datei definiert genau, wie Ihr Steuerelement (View) aussehen wird. In den folgenden Abschnitten werde ich Ihnen die Elemente in dieser Datei einzeln vorstellen.

XML-Standarddeklaration

Das erste Element der Datei kann die XML-Standarddeklaration enthalten, die Texteditoren wie Eclipse und Plattformen wie Android darüber informieren, um welche Art von Datei es sich handelt und welche Zeichensatzkodierung verwendet wird. Diese Anweisung, die in der hier verwendeten Version der Entwicklungsumgebung weggefallen ist, lautet:

<?xml version="1.0" encoding="utf−8"?>

Layouttyp

Das nächste Element definiert den Layouttyp. In diesem Fall arbeiten Sie mit einem Layout, das RelativeLayout heißt, bei dem die einzelnen Elemente relativ zueinander positioniert werden können. Bei RelativeLayout handelt es sich um einen Container für andere Elemente, die Views (oder Steuerelemente) genannt werden und auf dem Bildschirm angezeigt werden.

<RelativeLayout xmlns:android=

"http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity" >

Wie Sie wahrscheinlich wissen, werden Beginn- und Endauszeichner in HTML und XML meist einfach »Tags« genannt. Hier wird der Beginnauszeichner angezeigt. Da das Tag <RelativeLayout> als Container für andere Elemente dient, folgt der Endauszeichner </RelativeLayout> erst am Ende der Datei.

In Tabelle 4.2 finden Sie weitere Informationen über die verschiedenen möglichen Layouttypen wie RelativeLayout.

Views

RelativeLayout enthält Views, bei denen es sich um Steuerelemente handelt, die unter Android allgemein als einfache Bausteine oder Bestandteile von Bedienschnittstellen fungieren. Im folgenden Code ist das Element TextView enthalten, das für die Anzeige von Text auf dem Bildschirm sorgt:

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerHorizontal="true"

android:layout_centerVertical="true"

android:text="@string/hello_world" />

Eine View nimmt einen rechteckigen Bereich auf dem Bildschirm ein und ist für die Anzeige und die Ereignisbehandlung verantwortlich. Alle Elemente, die auf dem Bildschirm eines Geräts angezeigt werden können, sind Views. Die Klasse View ist die Superklasse, von der alle Elemente in Android abstammen.

Ganz am Ende finden Sie die Zeile mit dem abschließenden Tag des Containers RelativeLayout:

</RelativeLayout>

Im folgenden Abschnitt werde ich den Dschungel der verschiedenen Layouttypen ein wenig lichten.

Die Layoutwerkzeuge des Android-SDKs nutzen

Beim Erstellen von Bedienschnittstellen müssen Sie manchmal Komponenten relativ zueinander oder in Tabellen anordnen oder unter bestimmten Umständen absolute Positionsangaben verwenden. Glücklicherweise haben die genialen Android-Entwickler bei Google an all dies gedacht und Sie mit den für die Erstellung derartiger Layouts benötigten Werkzeugen versorgt. Tabelle 4.2 stellt die im Android-SDK (Software Development Kit) enthaltenen gängigen Layoutvarianten kurz vor.

Layout | Beschreibung
LinearLayout | Bei diesem Layout werden die Kinder in einer einzelnen Zeile angeordnet.
RelativeLayout | Bei diesem Layout lassen sich die Positionen der Kinder relativ zueinander oder zu ihren Eltern beschreiben.

FrameLayout

Dieses Layout spart einen Bereich auf dem Bildschirm zur Anzeige eines einzelnen Elements aus. Sie können zu einem FrameLayout-Element zwar mehrere Kinder zuordnen, aber alle Kinder werden links oben auf den Bildschirm gesetzt. Die Kinder werden im Stapel gezeichnet, wobei das zuletzt hinzugefügte Kind oben auf dem Stapel liegt. Dieses Layout wird meist als Möglichkeit genutzt, um an absoluten Positionen zu zeichnen.

TableLayout | Bei diesem Layout werden die Kinder in Zeilen und Spalten angeordnet.

Tabelle 4.2: Layouts im Android-SDK

Es gibt weitere Arten von Layoutwerkzeugen, wie zum Beispiel TabHost zum Erstellen von Registerkarten und SlidingDrawer zum Verbergen und Anzeigen von Views mit einem Fingerwisch. Da diese aber nur in Spezialfällen verwendet werden, gehe ich hier nicht näher darauf ein. Die in Tabelle 4.2 aufgeführten Elemente werden in Layouts am häufigsten verwendet.

Den visuellen Designer nutzen

Und damit kommen wir zur guten Nachricht, denn Eclipse enthält einen visuellen Designer. Damit sind aber auch ein paar schlechte Nachrichten verbunden, denn der Designer ist in seinen Möglichkeiten beschränkt (wie alle visuellen Designwerkzeuge).

Den visuellen Designer öffnen

Um den visuellen Designer mit der Datei activity_main.xml im Eclipse-Editor zu öffnen, aktivieren Sie die Registerkarte GRAPHICAL LAYOUT (siehe Abbildung 4.5).

Abbildung 4.5: Über den Reiter GRAPHICAL LAYOUT aktivieren Sie den visuellen Designer.

Wenn der visuelle Designer auf dem Bildschirm angezeigt wird, können Sie Elemente aus den Werkzeugkästen wie beispielsweise LAYOUTS in die zu gestaltende Benutzeroberfläche ziehen.

Die Eigenschaften einer View untersuchen

Wenn Sie den visuellen Designer verwenden, können Sie die Eigenschaften einer bestimmten View einfach dadurch betrachten, dass Sie sie anklicken. Sollte der PROPERTIES-Bereich bei Ihnen nicht angezeigt werden, gehen Sie so vor:

1. Wählen Sie WINDOW:SHOW VIEW:OTHER.

2. Erweitern Sie GENERAL und wählen Sie PROPERTIES.

Damit wird die Registerkarte PROPERTIES in Eclipse angezeigt (Abbildung 4.6).

3. Markieren Sie das Textelement »Hello world« im visuellen Designer.

Um das Element herum wird ein blauer Rahmen mit Anfassern angezeigt und seine Eigenschaften werden im Fensterbereich PROPERTIES angezeigt.

4. Blättern Sie ein wenig in der Liste der Eigenschaften, um sich anzusehen, welche Eigenschaften der View Sie ändern können.

Abbildung 4.6: Ein im visuellen Designer ausgewähltes Element, dessen Eigenschaften auf der Registerkarte PROPERTIES angezeigt werden.

Sie können die verschiedenen Bereiche in Eclipse Ihrem Geschmack entsprechend anordnen. Dazu klicken Sie die jeweiligen Fensterleisten an und ziehen sie an die gewünschte Stelle im Eclipse-Hauptfenster oder, wenn Sie lieber mit mehreren Programmfenstern arbeiten wollen, auch irgendwo auf Ihren Bildschirm.

Wie Sie Abbildung 4.5 entnehmen können, gibt es dadurch auch durchaus gewisse Unterschiede zwischen den verschiedenen Varianten der Eclipse-IDE. Abbildung 4.5 zeigt so den Fensterbereich STRUCTURE, der unten rechts einen Properties-Bereich enthält. Den PALETTE- und den STRUCTURE-Bereich können Sie hier auf eine schmale Leiste verkleinern, wenn Sie die kleinen Dreiecke im Titel des Fensterbereichs anklicken.

Wenn Sie sich nicht sicher sind, welche Eigenschaften eine View besitzt, öffnen Sie den visuellen Designer, aktivieren die Registerkarte PROPERTIES und sehen sich dort um. So können Sie schnell feststellen, was eine View zu bieten hat. Wenn die Registerkarte PROPERTIES nicht angezeigt wird, können Sie sie über WINDOW:SHOW VIEW:OTHER:GENERAL:PROPERTIES aktivieren.

In Abhängigkeit vom Layout seiner Eltern können sich die verfügbaren Eigenschaften einer View ändern. Wenn eine TextView beispielsweise ein Kind von LinearLayout ist, besitzt sie einen anderen Satz von Eigenschaften (für das Layout), als wenn sie ein Kind von RelativeLayout ist.

Der visuelle Designer eignet sich gut für einfache Szenarien mit statischen Inhalten. Was ist aber, wenn Elemente in Abhängigkeit von Nutzereingaben dynamisch auf dem Bildschirm dargestellt werden müssen? Dann muss der Designer passen und kann Ihnen nicht helfen. Er eignet sich am besten für Situationen mit statischen Inhalten. Dabei erstellen Sie ein Layout ein einziges Mal, ohne dass es anschließend dynamisch aktualisiert werden muss. Der Text eines TextView-Elements oder angezeigte Bilder können sich dabei zwar möglicherweise noch ändern, aber die eigentliche Anordnung der Views innerhalb des Layouts ändert sich nicht.

Die Bedienschnittstelle entwickeln

Gut, nun wird es aber wirklich Zeit, Ihre Bedienschnittstelle zu entwickeln. Kehren Sie erst einmal zur XML-Ansicht Ihres Layouts zurück. Klicken Sie dazu den Reiter ACTIVITY_MAIN.XML an, der sich direkt neben dem Reiter GRAPHICAL LAYOUT befindet, über den Sie den visuellen Designer aktiviert haben. In der XML-Ansicht löschen Sie das Element TEXTVIEW aus Ihrem Layout und nehmen die nachfolgend dargestellten Änderungen vor. Ihr Layout sollte dann so aussehen:

<LinearLayout xmlns:android=

"http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity" >

</LinearLayout>

XML-Layoutattribute betrachten

Bevor es weitergeht, lassen Sie mich erst einmal den XML-Code des Android-Layouts erläutern, das Sie gerade bearbeiten. Sehen Sie sich dazu Tabelle 4.3 an.

Layoutelement | Beschreibung
xmlns:android="..." | Definiert den XML-Namensraum, den Sie beim Referenzieren von Android-SDK-Bestandteilen benutzen werden.
orientation="vertical" | Teilt Android mit, dass die View vertikal angeordnet wird (wie das Hochformat beim Drucken).

android:layout_ width="match_parent"

Die View soll den gesamten verfügbaren horizontalen Platz im Elternfenster einnehmen. Kurz gesagt, soll sie die vollständige Breite des Elternelements in Anspruch nehmen.

android: layout_height=" match_parent"

Die View soll den gesamten verfügbaren vertikalen Platz im Elternfenster einnehmen. Kurz gesagt, soll sie die vollständige Höhe des Elternelements in Anspruch nehmen.

Tabelle 4.3: XML-Layoutattribute

Jetzt haben Sie durch Setzen von Breite und Höhe auf match_parent Ihr Layout so definiert, dass es den gesamten Bildschirm füllt.

Die weiteren (ab ADT 17) automatisch erzeugten Zeilen im Beispiel zum speziellen Namensraum tools können Sie ignorieren. Sie dienen speziellen Zwecken und haben hier weiter keine Funktion.

Mit Views arbeiten

Wie bereits erwähnt, sind Views in Android die Grundbausteine für die Komponenten der Bedienschnittstelle. Immer wenn Sie im Android-System eine Komponente in eine Bedienschnittstelle einbauen, wie zum Beispiel Layout oder TextView, verwenden Sie eine View. Wenn Sie in Java mit Views arbeiten, müssen Sie sie in einen geeigneten Typ umwandeln, um damit arbeiten zu können.

Werte für layout_width und layout_height setzen

Bevor eine View auf dem Bildschirm dargestellt werden kann, müssen erst einige ihrer Einstellungen geändert werden, damit Android weiß, wie sie dort aussehen soll. Die erforderlichen Attribute sind layout_width und layout_height. Diese werden im Android-SDK LayoutParams genannt.

Über das Attribut layout_width legen Sie die Breite und über layout_height die Höhe einer View fest.

Werte für match_parent und wrap_content setzen

Die Attribute layout_width und layout_height können zur Angabe der jeweiligen Abmessungen beliebige Pixelwerte oder dichteunabhängige Pixelwerte annehmen. Die beiden gängigsten Werte für diese Attribute sind aber die Konstanten match_parent und wrap_ content.

Der Wert match_parent teilt dem Android-System mit, dass basierend auf dem Layout der Eltern möglichst viel Platz auf dem Bildschirm beansprucht werden soll. Beim Wert wrap_ content wird nur der zur Darstellung der View erforderliche Platz auf dem Bildschirm benutzt. Wenn der Inhalt der View größer wird, wie dies bei einer TextView der Fall sein kann, wird die View selbst ebenfalls vergrößert. Dieses Verhalten entspricht der Eigenschaft Autosize bei der Erstellung von Windows-Formularen.

Wenn Sie ein statisches Layout verwenden, müssen diese zwei Attribute im XML-Layout gesetzt werden. Wenn Sie Views dynamisch über Code erzeugen, müssen die Parameter über Java-Code gesetzt werden. In beiden Fällen kommen Sie aber nicht ohne sie aus. Die dynamische Erstellung von Views wird in diesem Buch nicht behandelt. Wenn Sie mehr darüber erfahren wollen, sehen Sie sich dazu die mit dem Android-SDK gelieferten API-Beispiele an.

Wenn Sie vergessen, Werte für layout_width oder layout_height vorzugeben, wird Ihre Android-App bei der Berechnung der View abstürzen. Glücklicherweise werden Sie das aber recht schnell beim Testen Ihrer Anwendung herausfinden.

Mit Android 2.2 wurde fill_parent in match_parent umbenannt. Wenn Sie Apps für Geräte mit älteren Android-Versionen als 2.2 entwickeln, müssen Sie fill_parent anstelle von match_parent verwenden.

Bilder zu Ihrer App hinzufügen

Nun müssen Sie dafür sorgen, dass auf dem Bildschirm etwas angezeigt wird! Die Betrachtung von Text mag ja Spaß machen, die wirklich interessanten Komponenten kommen aber erst mit Eingabeelementen und Bildern hinzu. In den folgenden Abschnitten werde ich Ihnen zeigen, wie Sie Bilder in Ihre Anwendung einbauen beziehungsweise integrieren können.

Ein Bild auf dem Bildschirm anzeigen

Zunächst müssen Sie die Bilder mit dem Telefon zum Bildschirm hinzufügen. Diese wurden bereits in Abbildung 4.2 und Abbildung 4.3 weiter vorn im Kapitel dargestellt. Dafür benötigen Sie erst einmal das entsprechende Bild selbst. Sie können es zusammen mit den Quelltexten zum Buch herunterladen, die online bereitgestellt werden, oder ein eigenes Bild verwenden.

Bilder zu eigenen Projekten hinzuzufügen, ist leicht. Allerdings scheint es dabei Unterschiede zwischen den jeweiligen Versionen und/oder Plattformen zu geben. Sie können Bilddateien
mit dem Windows-Explorer oder anderen Dateimanagern in die entsprechenden Ordner Ihres Android-Projekts (hier res/drawable−mdpi) kopieren und anschließend [F5] betätigen, um das Projekt aufzufrischen,
in das Eclipse-Fenster hinüberziehen und dort fallen lassen
oder im Kontextmenü Ihres Dateimanagers KOPIEREN und im Eclipse-Fenster PASTE wählen.

Bei der Variante mit Kopieren und Einfügen gehen Sie unter Windows so vor:

1. Navigieren Sie im Windows-Explorer zu dem Ordner, der das einzufügende Bild enthält.

2. Klicken Sie die Bilddatei (phone_on.png) mit der rechten Maustaste an und wählen Sie im Kontextmenü KOPIEREN (Abbildung 4.7).

Beachten Sie, dass es hier zwei Bilder für die beiden Zustände der App gibt, eins für den normalen Betriebsmodus (wie in Abbildung 4.2) und eins für den Lautlosmodus (wie in Abbildung 4.3).

3. Klicken Sie in Eclipse den Ordner mit der rechten Maustaste an, in den das Bild eingefügt werden soll (res/drawable−mdpi), und wählen Sie im Kontextmenü PASTE (Abbildung 4.8).

Die Bilddatei wird jetzt in den entsprechenden Ordner Ihres Projekts kopiert.

Weshalb die Ordner für verschiedene Dichten wichtig sind

Android unterstützt unterschiedliche Bildschirmauflösungen und Dichten. Vorhin haben Sie ein Bild im Ordner mdpi abgelegt, der für Geräte mit mittlerer Bildschirmdichte vorgesehen ist. Und was ist mit Geräten mit niedriger und hoher Dichte? Wenn Android angeforderte Ressourcen nicht in der eigentlich benötigten Dichte finden kann, greift es auf andere vorhandene Ressourcen zurück. Was das bedeutet? Wenn eine App auf einem Bildschirm mit hoher Dichte läuft, wird ein Bild geringerer Auflösung verwendet und gestreckt, was meist schlechter aussieht, weil dann einzelne Pixel gut erkennbar sind. Werden Bilder höherer Auflösung auf Geräten mit niedriger Bildschirmdichte verwendet, werden die Bilder so verkleinert, dass sie für die tatsächlichen Bildschirmabmessungen passen. Um dies zu vermeiden, erstellen Sie mehrere Versionen Ihrer Bilder für die verschiedenen Auflösungen. Weitere Informationen finden Sie bei »Supporting Multiple Screens« in der Android-Dokumentation unter http://developer.android.com/guide/practices/screens_support.html.

Abbildung 4.7: Bilddatei im Windows-Explorer kopieren

Abbildung 4.8: Bilddatei in Eclipse in einen Ordner einfügen

4.Wiederholen Sie die Schritte 1 bis 4 mit der Bilddatei für den Lautlosmodus (phone_silent.png).

Die Namen der beiden Bilddateien lauten im übrigen Kapitel phone_on.png (für den normalen Betriebsmodus) und phone_silent.png (für den Lautlosmodus). Wenn Ihre Bilddateien andere Namen tragen, können Sie sie über die Option DELETE im Kontextmenü aus Ihrem Projekt entfernen, entsprechend umbenennen und erneut einfügen. Ihr Eclipse-Projekt sollte nun wie in Abbildung 4.9 aussehen.

Wenn Sie die Bilder in Eclipse einfügen, erkennen die ADT, dass sich die Dateistruktur des Projekts geändert hat. Dann erstellen die ADT das Projekt neu, da im Menü PROJECT die Option BUILD AUTOMATICALLY aktiviert ist. Dadurch wird der Ordner gen neu erzeugt, in dem sich die Datei R.java befindet. Diese enthält nun Verweise auf die beiden eben hinzugefügten Bilddateien.

Diese Referenzen auf die Ressourcen können Sie benutzen, um Bilder zu Ihrem Layout hinzuzufügen, oder im Code oder der XML-Definition verwenden. Sie werden die Bilder im folgenden Abschnitt im XML-Layout definieren.

Abbildung 4.9: Das Projekt LAUTLOSMODUS-UMSCHALTER mit den Telefonbildern

Die Bilder zum Layout hinzufügen

Nun ist es an der Zeit, das Bild zum Layout hinzuzufügen. Dazu müssen Sie das Folgende in die Datei activity_main.xml eingeben und dabei den aktuellen Inhalt der Datei überschreiben:

<LinearLayout xmlns:android=

"http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity" >

<ImageView

android:id="@+id/phone_icon"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:src="@drawable/phone_on" />

</LinearLayout>

Mit diesem Schritt haben Sie das Element ImageView in die View LinearLayout eingefügt. Mit dem Element ImageView können Sie ein Bild auf dem Gerätebildschirm anzeigen lassen.

Die Bildeigenschaften setzen

Die ImageView enthält einige Extraparameter, die Sie noch nicht kennen. Dabei handelt es sich um:

Die Eigenschaft "android:id="@+id/phone_icon": Über das Attribut id wird eine eindeutige Kennung für die View im Android-System festgelegt. Ich habe zwar in einer Lehrveranstaltung versucht, eine bessere Erklärung zu geben, aber sie wird bei Bedarf in der eigentlichen Android-Dokumentation zu diesem Thema unter http://developer.android.com/guide/topics/ui/declaring−layout.html gut erläutert. phone_icon ist dabei jedenfalls der Name, den Sie selbst frei vergeben können und der die View eindeutig kennzeichnen soll.

Die Eigenschaft layout_gravity: Sie legt fest, wie die View platziert werden soll, und gibt Werte für die x- und y-Achse und die Eltern an. Im Beispiel wurde der Wert über die Konstante center_horizontal festgelegt. Dieser Wert sorgt dafür, dass das Android-System das Objekt horizontal mittig innerhalb seines Containers anzeigt, ohne dessen Größe zu ändern. Sie können viele andere Konstanten verwenden, wie zum Beispiel center_vertical (vertikal zentriert), top (oben), bottom (unten), left (links) oder right (rechts). In der Android-Dokumentation zu LinearLayout.LayoutParams finden Sie eine vollständige Liste.

Die Eigenschaft android:src="@drawable/phone_on": Bei dieser Eigenschaft handelt es sich um einen direkten Nachkommen der Klasse ImageView. Sie verwenden sie, um den Namen der Bilddatei anzugeben, die auf dem Bildschirm angezeigt werden soll.

Beachten Sie den Wert "@drawable/phone_on" der Eigenschaft src. Hier sehen Sie den Nutzen der Datei R.java. Dabei wird im XML-Code auf eine auf dem Bildschirm darstellbare Ressource verwiesen. Dazu geben Sie das At-Symbol (@) und den Namen der zu verwendenden Ressource an.

Bestimmte Android-Attribute beginnen mit dem Präfix layout_, wie beispielsweise android:layout_width, android:layout_height und android: layout_gravity. Die layout_-Konvention teilt Ihnen mit, dass sich das Attribut auf das Elternelement der View bezieht. Attribute, die nicht mit layout_ beginnen, beziehen sich auf die View selbst. Daher teilt das Attribut android:src von ImageView diesem Element mit, welches Bild es verwenden soll, während android:layout_gravity hingegen das Elternelement von ImageView (hier LinearLayout) dazu auffordert, ImageView mittig zu positionieren.

Darstellbare Ressourcen festlegen

Für den Bezeichner der darstellbaren Ressource schreiben Sie nicht @drawable−mdpi, sondern einfach nur @drawable. Das liegt daran, dass es zu den Aufgaben von Android zählt, mehrere Bildschirmabmessungen zu unterstützen (und Ihnen das Leben zu erleichtern)! Das Layoutsystem von Android weiß, was es über darstellbare Ressourcen (Drawables) wissen muss, und basta. Beim Design weiß es nichts von niedrigen, mittleren oder hohen Auflösungen. Zur Laufzeit ermittelt Android aber, ob und wann es darstellbare Ressourcen mit niedriger/mittlerer/hoher Auflösung verwenden kann.

Wenn die App beispielsweise auf einem hochauflösenden Gerät läuft und die angeforderte darstellbare Ressource im Ordner drawable−hdpi vorhanden ist, dann verwendet Android diese Ressource. Ansonsten greift es auf die geeignetste Ressource zurück, die es finden kann. Bei der Unterstützung verschiedener Bildschirmgrößen und Auflösungen handelt es sich um ein umfassendes (und in mancherlei Hinsicht komplexes) Thema. Wenn Sie sich damit eingehend befassen wollen, lesen Sie am besten den Artikel Managing Multiple Screen Sizes in der Android-Dokumentation, die Sie unter http://developer.android.com/guide/practices/screens_support.html im Web finden.

Der Teil phone_on gibt den Namen der zu verwendenden darstellbaren Ressource an. Der Name der Bilddatei lautet zwar eigentlich phone_on.png, um sich aber an die Java-Richtlinien für Mitgliedernamen zu halten, wird die Dateinamenerweiterung weggelassen. Wenn Sie die Datei R.java im Ordner gen öffnen, finden Sie dort nur eine Mitgliedsvariable mit dem Namen phone_on und nicht phone_on.png.

Die ADT zeigen Ihnen die verfügbaren Optionen über die Codevervollständigung auch an. Setzen Sie im Eclipse-Editor Ihren Cursor in der Eigenschaft src der ImageView direkt hinter @drawable/. Drücken Sie dann [Strg]+[Leertaste]. Daraufhin sollte das Dialogfeld der Codevervollständigung angezeigt werden (Abbildung 4.10). Sehen Sie die anderen Ressourcennamen? Dabei handelt es sich um andere Optionen, die Sie für src beim Festlegen der darstellbaren Ressource wählen können.

Ein Startsymbol für die App erstellen

Wenn Ihre App installiert wird, besitzt sie ein Symbol, das Benutzern ihre Erkennung im Anwendungsstarter erleichtern soll. Beim Erstellen der Anwendung LAUTLOSMODUS-UMSCHALTER haben die ADT für Sie automatisch ein vordefiniertes Startsymbol eingebunden, das in Abbildung 4.11 dargestellt wird.

Das wäre aber doch irgendwie langweilig, wenn alle Apps mit demselben Symbol daherkämen, oder? Und genau deshalb sollten Sie dieses Symbol durch ein eigenes ersetzen. Ich habe zu diesem Zweck mit einem Bildbearbeitungsprogramm ein rundes Telefonsymbol erstellt, das in Abbildung 4.12 dargestellt wird. Sie können Ihr eigenes Symbol erstellen (mehr dazu im folgenden Abschnitt) oder das in den heruntergeladenen Quelltexten enthaltene verwenden.

Abbildung 4.10: Codevervollständigung mit Ressourcen

Abbildung 4.11: Das vorgegebene Startsymbol

Abbildung 4.12: Das neue, unverwechselbare Telefonsymbol für die App

Ein eigenes Startsymbol entwerfen

Das Android-Projekt macht auch das Erstellen eigener Startsymbole recht einfach. Die Android-Dokumentation enthält einen eigenen Artikel namens Icon Design Guidelines, der sich mit allen Aspekten des Entwurfs eigener Symbole befasst. Er enthält eine Anleitung für die Erstellung von Symbolen für die Android-Plattform, Empfehlungen, Hinweise auf mögliche Fallstricke, Richtlinien für Farben, Abmessungen und Positionierung und insbesondere auch Symbolvorlagen, die Sie benutzen können. Diese Designrichtlinien finden Sie unter http://d.android.com/guide/practices/ui_guidelines/icon_design.html und http://d.android.com/design/style/iconography.html.

Mit Vorlagen arbeiten

Da Sie das Android-SDK bereits heruntergeladen und installiert haben, liegen die Symbolvorlagen und Materialien bereits auf Ihrer Festplatte und warten nur darauf, von Ihnen benutzt zu werden! Navigieren Sie zum Installationsverzeichnis Ihres Android-SDKs (siehe Kapitel 2) und von dort aus zum Ordner docs/shareables. Dort finden Sie verschiedene .zip-Dateien, die Vorlagen (templates) und Beispiele enthalten. Öffnen Sie die Vorlagen mit dem Bildbearbeitungsprogramm Ihrer Wahl und halten Sie sich an die Designrichtlinien in der Dokumentation, um Ihren nächsten umwerfenden Symbolsatz zu erstellen.

Symbolgrößen an Bildschirmauflösungen anpassen

Wie weiß man beim Entwurf, wie groß die Symbole sein müssen, wenn für verschiedene Bildschirmauflösungen unterschiedlich große Symbole benötigt werden? Für jede Auflösung sind spezifische Symbolabmessungen erforderlich, damit auf dem Bildschirm keine Pixeltreppchen sichtbar sind und sie auch verzerrt oder komprimiert gut aussehen.

In Tabelle 4.4 werden die Abmessungen der fertigen Symbole für die vier generalisierten Bildschirmauflösungen zusammengefasst.

Bildschirmauflösung | Größe des Symbols
Niedrige (LDPI) | 36x36 Pixel
Mittlere (MDPI) | 48x48 Pixel
Hohe (HDPI) | 72x72 Pixel
Extra-hohe (XHDPI) | 96x96 Pixel

Tabelle 4.4: Größe der fertigen Symbole

Ein eigenes Startsymbol hinzufügen

Um ein eigenes Startsymbol zum Projekt hinzuzufügen, führen Sie diese Schritte aus:

1. Benennen Sie Ihre Bilddatei in ic_launcher um.

2. Ziehen oder kopieren Sie das Symbol in den Ordner mdpi.

Eclipse fragt Sie, ob die vorhandene Datei ic_launcher.png überschrieben werden soll (Abbildung 4.13).

Abbildung 4.13: Eclipse fragt, ob die Datei ic_launcher.png überschrieben werden soll.

3. Klicken Sie YES an.

Die Datei ic_launcher.png befindet sich nun im Ordner drawable−mdpi.

Damit sind Sie noch nicht fertig! Was ist mit den Ordnern ldpi, hdpi und xhdpi? Sie benötigen noch Versionen Ihres Symbols für niedrige, hohe und extrem hohe Auflösungen. Kopieren Sie dazu die entsprechenden Bilddateien in die Ordner ldpi, hdpi und xhdpi.

Wenn Sie die Symbole für niedrige und hohe Auflösungen nicht in die jeweiligen Ordner kopieren, wird Benutzern von entsprechenden Geräten das in Abbildung 4.11 dargestellte Standardsymbol angezeigt. Nur die Benutzer von Geräten mit mittlerer Auflösung bekommen dann das neue Symbol zu sehen, das Sie bereits in das Projekt kopiert haben.

Wieso denn das? Sie haben doch die Datei in den Ordner mdpi kopiert, was soll das also? Die Ordner xhdpi, hdpi und ldpi enthalten jeweils eigene Versionen des Symbols. Wenn Sie die Ordner drawable−xhdpi, drawable−hdpi und drawable−ldpi in Ihrem Eclipse-Projekt öffnen, können Sie sehen, dass es dort für die verschiedenen Auflösungen jeweils eigene Dateien namens ic_launcher.png gibt. Achten Sie darauf, dass Sie die Symbole mit den richtigen Abmessungen in die Ordner für die jeweiligen Auflösungen kopieren.

Ein View-Objekt für den Umschalter hinzufügen

Android-Geräte sind umfassend mit verschiedenen Views ausgestattet, zu denen Schaltflächen, Kontrollkästchen und Texteingabefelder zählen, mit denen Sie schnell Ihre Bedienschnittstelle erstellen können. Einige Views sind auch komplexer, wie zum Beispiel die Steuerelemente für Datumsauswahl, Uhr und Zoom.

Views lösen bei Bedienschnittstellen auch Ereignisse aus, die darüber informieren, wenn ein Benutzer eine bestimmte View benutzt und beispielsweise eine Schaltfläche angetippt hat.

Sie müssen eine Schaltflächen-View zu Ihrer Anwendung hinzufügen, damit Sie den Lautlosmodus des Telefons umschalten können.

Um Ihr Layout um eine Schaltfläche zu ergänzen, fügen Sie den folgenden Code nach der eben eingefügten ImageView hinzu:

<Button

android:id="@+id/toggleButton"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:text="Lautlosmodus umschalten" />

Damit haben Sie eine Schaltfläche als ID-Ressource toggleButton zu Ihrer View hinzugefügt. Über diesen Namen können Sie sich in Ihrem Java-Code darauf beziehen. (Damit werden wir uns im nächsten Kapitel befassen.)

Höhe und Breite werden auf wrap_content gesetzt. Damit teilen Sie dem Android-Layoutsystem mit, dass das Widget auf dem Bildschirm angezeigt werden und nur den von ihm benötigten Platz beanspruchen soll. Die Eigenschaft gravity hat denselben Wert wie die ImageView darüber, horizontal zentriert (center_horizontal).

Bei der letzten hinzugefügten Eigenschaft dieser View handelt es sich um text und damit um die Beschriftung der Schaltfläche. Der auf der Schaltfläche anzuzeigende Text soll dabei Lautlosmodus umschalten lauten.

Der vollständige Code sollte nun so aussehen:

<LinearLayout xmlns:android=

"http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity" >

<ImageView

android:id="@+id/phone_icon"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:src="@drawable/phone_on" />

<Button

android:id="@+id/toggleButton"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:text="Lautlosmodus umschalten" />

</LinearLayout>

Vorschau der App im visuellen Designer

Nun wird es Zeit, sich anzusehen, wie das Layout im visuellen Designer aussieht. Klicken Sie die Registerkarte GRAPHICAL LAYOUT an, um den visuellen Designer anzeigen zu lassen (Abbildung 4.14).

Abbildung 4.14: Das Layout im visuellen Designer

Der visuelle Designer der ADT

Für den visuellen Designer gibt es viele verschiedene Konfigurationen, die sich zudem bei den verschiedenen Versionen noch unterscheiden können. Standardmäßig verwendet der Designer das Nexus One, eines der ersten verbreiteten Google-Smartphones. Über das Dropdown-Listenfeld DEVICES können Sie sich im visuellen Designer darüber informieren, mit welchen emulierten Geräten Sie Ihr Layout ausprobieren können. Rechts daneben werden die verschiedenen, vom Gerät unterstützten Konfigurationen angezeigt. Das Nexus One kennt zwei zulässige Laufzeitzustände:

LANDSCAPE: Das Telefon wird quer gehalten.

PORTRAIT: Das Telefon wird hochkant gehalten.

Für jedes Gerät gibt es im Dropdown-Listenfeld einen eigenen Satz möglicher Konfigurationen. Zudem können Sie auch eigene Konfigurationen erstellen, wenn Sie DEVICES: CUSTOM:CUSTOM:NEW wählen.

Die Hintergrundfarbe ändern

Sollte der Hintergrund Ihrer App jetzt schwarz und Ihr Bild weiß sein, ist etwas falsch gelaufen. Sie sollten im Layout besser gleich für einen weißen Hintergrund sorgen, mit dem das Bild verschmilzt. Gehen Sie dazu so vor:

1. Aktivieren Sie die Registerkarte ACTIVITY_MAIN.XML.

2. Fügen Sie die Eigenschaft background zum Container LinearLayout hinzu:

android:background="#ffffff"

Beim hexadezimalen Farbwert #ffffff handelt es sich um durchsichtiges Weiß. Sie können hier beliebige Farbwerte verwenden, wie zum Beispiel #ff0000, das Rot entspricht.

Unter Verwendung einer Ressource können Sie auch Hintergrundbilder benutzen.

3. Prüfen Sie, dass die Definition von LinearLayout so aussieht:

<LinearLayout xmlns:android=

"http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:orientation="vertical"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="#ffffff"

tools:context=".MainActivity" >

4. Speichern Sie die Datei.

5. Aktivieren Sie die Registerkarte GRAPHICAL LAYOUT, um den visuellen Designer zu öffnen.

Auf Ihrem Bildschirm sollte es nun ähnlich wie in Abbildung 4.15 aussehen.

Kosmetik und Kommentare

Wie Sie bei der vorliegenden Version der App in der Layout-Vorschau unschwer erkennen können, ist deren Titel »Lautlosmodus-Umschalter« ein wenig lang geraten. So etwas kann schnell einmal geschehen, und sei es auch nur deshalb, dass beispielsweise in einer neuen Android-Version in der Titelleiste das Anwendungssymbol in Miniaturansicht dargestellt wird und daher der Platz bei kleinen Bildschirmen zu knapp geworden ist.

Abbildung 4.15: Das Layout der App LAUTLOSMODUS-UMSCHALTER

Ändern wir das also kurz und öffnen dazu unter RES und VALUES die Datei strings.xml. Die entsprechende String-Ressource unseres Projekts sieht dort wie folgt aus:

<?xml version="1.0" encoding="utf−8"?>

<resources>

<!—

<string name="app_name">

Lautlosmodus−Umschalter</string>

−−>

<string name="app_name">Lautlos schalten</string>

<string name="hello_world">Hello world!</string>

<string name="menu_settings">Settings</string>

</resources>

Beachten Sie in diesem Quelltext, dass hier der alte Name der App in einen Kommentar umgewandelt wurde, der – wie in XML-Dateien üblich – mit der Zeichenfolge <!− eingeleitet und mit −−> abgeschlossen wird. Zudem können hier die eine oder andere automatisch erzeugte Ressource finden.

Abbildung 4.16: Unsere App nach kosmetischer Korrektur

Glückwunsch! Sie haben gerade ein vorläufiges Layout für die App LAUTLOSMODUS-UMSCHALTER erstellt.

5

Code für Ihre App

In diesem Kapitel

So funktionieren Aktivitäten in Android

Ihre eigene Aktivität kodieren

Nutzen Sie die Klassen des Android-Frameworks

Apps installieren

Debug-Tools nutzen

Ihre Apps einem Praxistest unterziehen

Sicherlich können Sie es kaum mehr erwarten, selbst eigenen Code für Ihre App zu schreiben. In diesem Kapitel geht es jedenfalls ausschließlich um das Schreiben von Code. Aber bevor Sie ein paar Bits und Bytes verbiegen können, müssen Sie erst einmal richtig verstehen, was Aktivitäten sind.

Grundlagen von Aktivitäten

Eine Aktivität (activity) ist eine einzelne, zielgerichtete Aktion eines Anwenders. Eine Aktivität kann beispielsweise eine Liste mit auswählbaren Menüoptionen oder Fotos mit deren Beschriftungen anzeigen. Eine App kann aus einer einzigen oder – wie die meisten Android-Anwendungen – mehreren Aktivitäten bestehen. Auch wenn Aktivitäten zusammenarbeiten und eine zusammenhängende Anwendung bilden können, sind sie eigentlich doch unabhängig voneinander.

Bei einer Aktivität handelt es sich in Android um einen wichtigen Teil im Lebenszyklus einer App, und die Art, wie Aktivitäten gestartet und zusammengestellt werden, zählt zu den Grundlagen des Android-Anwendungsmodells. Bei jeder Aktivität handelt es sich um eine Implementierung der Basisklasse Activity.

Nahezu alle Aktivitäten interagieren mit dem Benutzer. Daher kümmert sich die Klasse Activity für Sie um das Erstellen des Fensters, in dem Ihre Bedienschnittstelle (UI – User Interface) angezeigt wird. Aktivitäten werden häufig im Vollbildmodus präsentiert, manchmal schweben sie aber auch in einem Fenster oder werden in andere Aktivitäten eingebettet. Dann spricht man von Aktivitätsgruppen.

Mit Aktivitäten, Stapeln und Zuständen arbeiten

Nahezu alle Aktivitäten implementieren diese zwei Methoden:

onCreate: Hier initialisieren Sie Ihre Aktivität. Und hier legen Sie für die Aktivität unter Verwendung von Layoutressourcen-IDs insbesondere fest, welches Layout verwendet werden soll. Diese Methode lässt sich als Eintrittspunkt in Ihre Aktivität betrachten.

onPause: Hier ergreifen Sie Maßnahmen, wenn sich der Benutzer von Ihrer Aktivität abwendet. Insbesondere sollten hier alle vom Benutzer vorgenommenen Änderungen weitergegeben werden (zumindest, sofern sie gespeichert werden müssen).

Die Aktivitäten im System werden in einem Aktivitätsstapel verwaltet. Wenn eine neue Aktivität erzeugt wird, wird sie oben auf dem Stapel abgelegt und wird zur ausgeführten Aktivität. Die zuvor ausgeführte Aktivität bleibt darunter im Stapel liegen und wird nicht wieder in den Vordergrund geholt, bis die neue Aktivität beendet wird.

Um als Programmierer erfolgreich sein zu können, müssen Sie verstehen, wie und warum Aktivitäten hinter den Kulissen arbeiten. Dadurch können Sie nicht nur die Android-Plattform besser verstehen, sondern können auch leichter das seltsame Verhalten von Apps bei Laufzeitfehlern abstellen.

Eine Aktivität kann im Wesentlichen einen der vier in Tabelle 5.1 aufgeführten Zustände annehmen.

Zustand der Aktivität | Beschreibung
Aktiv laufend (Active/running) | Die Aktivität befindet sich im Vordergrund auf dem Bildschirm (oben auf dem Stapel).

Pausierend (Paused)

Die Aktivität ist zwar nicht mehr aktiv, aber noch sichtbar (eine neue Aktivität läuft transparent oder nicht auf dem gesamten Bildschirm aktiv im Vordergrund). Eine pausierende Aktivität ist noch völlig intakt, kann alle Zustands- und Mitgliedsdaten erhalten und bleibt mit der Fensterverwaltung von Android verbunden. Beachten Sie aber, dass die Aktivität bei extremem Speichermangel bis hin zu Gingerbread (3.0) vom Android-System zerstört werden kann.

Gestoppt (Stopped)

Wenn eine Aktivität von einer anderen völlig verdeckt wird, wird sie angehalten. Alle Zustands- und Mitgliederdaten bleiben zwar erhalten, der Benutzer kann sie aber nicht mehr sehen. Daher wird das Fenster versteckt und wird, wenn anderweitig Speicher benötigt wird, vom Android-System häufig zerstört.

Erzeugt und angehalten (Created and resumed)

Die Aktivität pausiert oder wurde vom System gestoppt. Das System kann den Speicher wieder für sich verwenden, indem es den Prozess auffordert, sich zu beenden, oder ihn zerstört. Wenn das System dem Benutzer diese Aktivität anzeigt, muss sie durch einen Neustart und die Wiederherstellung des vorherigen Zustands fortgesetzt werden.

Tabelle 5.1: Die vier wesentlichen Zustände von Aktivitäten

Den Lebenszyklus einer Aktivität verfolgen

Das folgende Flussdiagramm (Abbildung 5.1) zeigt die wichtigen Abläufe bei Aktivitäten. Dies wird als Lebenszyklus einer Aktivität bezeichnet.

Die Rechtecke stellen Callback-Methoden dar, die Sie implementieren können, um in der Aktivität auf Ereignisse zu reagieren. Die grau hinterlegten abgerundeten Felder repräsentieren wesentliche Zustände, in denen sich die Aktivität befinden kann.

Abbildung 5.1: Lebenszyklus einer Aktivität

Beim Lebenszyklus von Aktivitäten handelt es sich um ein umfangreiches und komplexes Thema, von dem wir hier in den nachfolgenden Abschnitten nur die wesentlichen Grundlagen behandeln können. Wenn Sie mehr darüber wissen wollen, sollten Sie einen Blick in den Artikel »Activity Life Cycle and Process Life Cycle« in der Android-Dokumentation werfen, den Sie unter der Adresse http://d.android.com/reference/android/app/Activity. html#ProcessLifecycle im Web finden.

Wichtige Schleifen überwachen

Sie könnten daran interessiert sein, diese drei Schleifen in Ihrer Aktivität zu überwachen:

Die gesamte Lebenszeit spielt sich zwischen dem anfänglichen Aufruf von onCreate() und dem abschließenden Aufruf von onDestroy() ab. Die globale Einrichtung der Aktivität erfolgt in onCreate() und in onDestroy() werden alle noch verbleibenden Ressourcen freigegeben. Wenn Sie beispielsweise einen Thread erzeugen, um im Hintergrund eine Datei aus dem Internet herunterzuladen, kann sie in der Methode onCreate() initialisiert werden. Der Thread könnte in der Methode onDestroy() gestoppt werden.

Die sichtbare Lebenszeit der Aktivität befindet sich zwischen den Methoden onStart() und onStop(). Während dieser Zeit kann der Benutzer die Aktivität auf dem Bildschirm sehen (auch wenn sie möglicherweise nicht im Vordergrund mit dem Benutzer interagiert, wie zum Beispiel, wenn der Benutzer mit einem Dialogfeld arbeitet). Zwischen diesen beiden Methoden können Sie die Ressourcen warten, die benötigt werden, um Ihre Aktivität auf dem Bildschirm anzuzeigen und laufen zu lassen. Sie können zum Beispiel eine Ereignisbehandlungsroutine zur Überwachung des Zustands des Telefons schreiben. Der Status des Telefons kann sich ändern und die Ereignisbehandlungsroutine informiert die Aktivität darüber, dass das Telefon in den Flugmodus wechselt, damit die Aktivität entsprechend reagieren kann. Dazu würden Sie eine Ereignisbehandlungsroutine in onStart() erstellen und alle benutzten Ressourcen in onStop() freigeben. Jeweils wenn die Aktivität für den Benutzer sichtbar oder unsichtbar wird, können die Methoden onStart() und onStop() aufgerufen werden.

Die Vordergrund-Lebenszeit der Aktivität beginnt mit dem Aufruf von onResume() und endet mit dem Aufruf von onPause(). Während dieser Zeit befindet sich die Aktivität vor allen anderen und interagiert mit dem Benutzer. Es ist normal, dass eine Aktivität mehrfach die Phase zwischen onResume() und onPause() durchläuft. Dies geschieht beispielsweise, wenn das Gerät in den Ruhezustand wechselt oder eine neue Aktivität ein bestimmtes Ereignis behandelt. Daher muss der Code in diesen Methoden recht knapp gehalten werden.

Eine Betrachtung der Methoden von Aktivitäten

Der gesamte Lebenszyklus lässt sich auf die folgenden Methoden reduzieren:

public class Activity extends ApplicationContext;

protected void onCreate(Bundle savedInstanceState);

protected void onStart();

protected void onRestart();

protected void onResume();

protected void onPause();

protected void onStop();

protected void onDestroy();

}

Sie können alle Methoden überschreiben und eigenen Code darin einfügen. Alle Aktivitäten implementieren onCreate() für die Initialisierung und möglicherweise auch onPause() für Aufräumarbeiten. Wenn Sie diese Methoden implementieren, sollten Sie dazu immer die Superklasse (Basisklasse) aufrufen.

Abläufe von Aktivitäten verfolgen

Allgemein finden diese Abläufe im Lebenszyklus einer Aktivität statt:

onCreate(): Wird aufgerufen, wenn die Aktivität anfangs erzeugt wird. Hier würden Sie die meisten der klassenweiten Variablen Ihrer Aktivität initialisieren. Anschließend wird immer onStart() aufgerufen. Zerstörbar: Nein. Nächste: onStart().

onRestart(): Wird aufgerufen, nachdem Ihre Aktivität gestoppt wurde und bevor sie erneut gestartet wird. Anschließend wird immer onStart() aufgerufen. Zerstörbar: Nein. Nächste: onStart().

onStart(): Wird aufgerufen, wenn Ihre Aktivität für den Benutzer sichtbar wird. Wenn die Aktivität in den Vordergrund geholt wird, wird anschließend onResume() aufgerufen. Wenn sie vor dem Benutzer verborgen wird, wird anschließend onStop() aufgerufen. Zerstörbar: Nein. Nächste: onResume() oder onStop().

onResume(): Wird aufgerufen, wenn die Aktivität für Interaktionen mit dem Benutzer verfügbar gemacht wird. Die Aktivität befindet sich dann oben auf dem Aktivitätsstapel. Zerstörbar: Nein. Nächste: onPause().

onPause(): Wird aufgerufen, wenn das System eine vorherige Aktivität fortsetzen will oder wenn der Benutzer zu einem anderen Teil des Systems, wie zum Beispiel dem Startbildschirm, wechselt. Diese Phase wird typischerweise genutzt, um veränderte Daten dauerhaft zu speichern. Wenn die Aktivität wieder in den Vordergrund geholt wird, wird onResume() aufgerufen. Wenn die Aktivität für den Benutzer unsichtbar wird, wird onStop() aufgerufen. Zerstörbar: Ja. Nächste: onResume() oder onStop().

onStop(): Wird aufgerufen, wenn die Aktivität für den Benutzer nicht länger sichtbar ist, weil eine andere Aktivität erneut gestartet wurde und sie überlagert. Das kann geschehen, weil eine andere Aktivität gestartet wurde oder weil eine vorherige Aktivität fortgesetzt wird und sich nun im Vordergrund (oben auf dem Aktivitätsstapel) befindet. Wenn die Aktivität erneut mit dem Benutzer interagieren soll, wird nachfolgend onRestart() aufgerufen. Wenn die Aktivität zerstört werden soll, wird anschließend onDestroy() aufgerufen. Zerstörbar: Ja. Nächste: onRestart() oder onDestroy().

onDestroy(): Das ist der letzte Aufruf, den Ihre Aktivität noch erhält, bevor sie zerstört wird. Diese Methode wird aufgerufen, wenn die Aktivität beendet wird (entweder manuell oder weil sie vom System wegen Speichermangel zerstört wird). Über die Methode isFinishing() können Sie feststellen, ob die Aktivität wirklich beendet oder vom System zerstört wird. Die Methode isFinishing() wird häufig innerhalb von onPause() benutzt, um dies zu ermitteln. Zerstörbar: Ja. Nächste: Nichts.

Sicherlich ist Ihnen die Anmerkung »Zerstörbar« am Ende der Beschreibung der jeweiligen Methoden aufgefallen. Das sollten Sie deshalb wissen, weil die mit »Zerstörbar« gekennzeichneten Methoden jederzeit und ohne Vorwarnung zerstört werden können. Daher sollten Sie die Methode onPause() dazu verwenden, um letzte Aufräumarbeiten durchzuführen und veränderte Daten dauerhaft zu speichern.

Konfigurationsänderungen erkennen

Zu guter Letzt müssen Sie im Lebenszyklus noch auf Konfigurationsänderungen achten. Dabei handelt es sich laut Definition beispielsweise um eine geänderte Bildschirmausrichtung (der Benutzer dreht den Bildschirm und die Darstellung wechselt dabei vom Portrait- in den Landscape-Modus oder umgekehrt), geänderte Spracheinstellungen und den Anschluss von Eingabegeräten. Konfigurationsänderungen sorgen dafür, dass die Aktivität zerstört wird und den üblichen, aus onPause(), onStop() und onDestroy() bestehenden Lebenszyklus durchläuft. Nach dem Aufruf der Methode onDestroy() erzeugt das System eine neue Instanz der Aktivität. Das geschieht, weil Ressourcen, Layoutdateien und so weiter in Abhängigkeit von der aktuellen Systemkonfiguration möglicherweise geändert werden müssen. Zum Beispiel kann eine Anwendung für den Benutzer in der Portrait-Darstellung (hochkant) völlig anders als in der Landscape-Darstellung (quer) aussehen.

Ihre erste Aktivität erstellen

Nun ist es an der Zeit, Ihre erste Aktivität zu erstellen! Nun, eigentlich haben Sie bereits in Kapitel 3 beim Erzeugen eines Projekts mit dem Assistenten mit MainActivity Ihre erste Aktivität erstellt. Öffnen Sie daher die Datei MainActivity.java in Ihrem Projekt, um sie in den nachfolgenden Abschnitten zu bearbeiten.

Wir beginnen mit onCreate

Bei der Methode onCreate() handelt es sich um den Eintrittspunkt in Ihre App. Der Code in Ihrer Datei MainActivity.java enthält bereits eine Implementierung dieser Methode. Und dort setzen Sie auch beim Schreiben Ihres ersten Codes an. Momentan sollte der relevante Code im Ordner src der App LAUTLOSMODUS-UMSCHALTER bei Ihnen noch so aussehen:

public class MainActivity extends Activity;

@Override

public void onCreate(Bundle savedInstanceState);

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

...

}

Bei den neueren Android-Versionen finden Sie hier noch ein wenig mehr Code, der der Integration der Aktionsleiste dient, der uns aber vorläufig nicht weiter interessieren soll.

Gleich werden Sie Initialisierungscode direkt unterhalb der Methode setContentView() einfügen.

Achten Sie auf diese Zeile:

super.onCreate(savedInstanceState);

Diese Zeile wird benötigt, damit die Anwendung ausgeführt werden kann. In ihr wird die Basisklasse Activity aufgerufen, um die Klasse MainActivity einzurichten. Wenn diese Zeile in Ihrem Quelltext fehlt, tritt während der Laufzeit ein Ausnahmefehler auf. Achten Sie darauf, dass Sie diese Methode immer in Ihre Methode onCreate() einbinden.

Android die Benutzeroberfläche anzeigen lassen

Erst einmal weiß eine Aktivität überhaupt nicht, welche Benutzeroberfläche sie verwenden soll. Es könnte sich um ein einfaches Formular handeln, in das Benutzer zu speichernde Daten eintragen können, es könnte sich um ein visuelle, kameragestützte Anwendung für eine virtuelle Umgebung (wie etwa Layar im Google Play Store) oder eine sich laufend ändernde Benutzeroberfläche wie in einem zwei- oder dreidimensionalen Spiel handeln. Als Entwickler müssen Sie der Aktivität mitteilen, welches Layout sie laden soll.

Damit die Benutzeroberfläche auf dem Bildschirm angezeigt wird, müssen Sie für die ContentView der Aktivität einen Wert setzen. Das geschieht mit dieser Codezeile:

setContentView(R.layout.activity_main);

Bei R.layout.activity_main handelt es sich um die Datei activity_main.xml, die sich im Ordner res/layouts befindet. Sie enthält das im letzten Kapitel definierte Layout.

Umgang mit Benutzereingaben

Die Anwendung LAUTLOSMODUS-UMSCHALTER erfordert nur wenig Interaktion mit dem Benutzer. Mehr als eine einzige Schaltfläche wird dem Benutzer nicht angeboten. Der Benutzer tippt die Schaltfläche an, um den Lautlosmodus des Telefons zu aktivieren, und kann ihn dann durch erneutes Antippen auch wieder abschalten.

Um auf das Ereignis des Antippens der Schaltfläche zu reagieren, müssen Sie einen Event-Listener (eine Ereignisüberwachungsfunktion, aber das sagt kaum jemand) registrieren. Dieser reagiert auf Ereignisse im Android-System. Dort gibt es verschiedene Ereigniskategorien, wobei Berührungsereignisse (Touch-Events oder auch Klicks) und Tastaturereignisse besonders häufig genutzt werden.

Tastaturereignisse

Tastaturereignisse treten ein, wenn bestimmte Tasten gedrückt werden. Warum sollten Sie sich dafür interessieren? Sie könnten beispielsweise Tastenkombinationen (Hotkeys) in Ihrer Anwendung anbieten. Wenn der Benutzer [Alt]+[B] drückt, soll Ihre View vielleicht in den Bearbeitungsmodus wechseln. Wenn Ihre App auf Tastaturereignisse reagiert, lässt sich das machen. Ich werde in diesem Buch zwar nicht weiter auf Tastaturereignisse eingehen, aber wenn Sie sie in zukünftigen Anwendungen benötigen, müssen Sie die Methode onKeyDown() wie im folgenden Beispiel überschreiben:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event);

// TODO Automatisch generierter Methodenrumpf

return super.onKeyDown(keyCode, event);

}

Berührungsereignisse

Berührungsereignisse treten ein, wenn der Benutzer ein Widget auf dem Bildschirm antippt. Die Android-Plattform erkennt jedes Tipp-Ereignis als Klick-Ereignis. Von nun werden die Begriffe tippen, klicken und berühren gleichbedeutend verwendet. Zu den Beispielen für Widgets, die auf Berührungsereignisse reagieren können, zählen:

Button

ImageButton

EditText

Spinner

ListView Rows

MenuItem

Alle Views im Android-System können auf Antippen reagieren. Bei einigen der Widgets wird deren Eigenschaft clickable aber standardmäßig auf den Wert false gesetzt. Sie können das in Ihrer Layoutdatei oder im Code überschreiben und dafür sorgen, dass auch diese Views auf Klicks reagieren, indem Sie deren Attribut clickable auf true setzen oder dazu die Methode setClickable() im Quelltext verwenden.

Ihre erste Ereignisbehandlungsroutine

Damit Ihre Anwendung auf das Klick-Ereignis reagieren kann, über das der Benutzer den Lautlosmodus umschalten können soll, muss die Schaltfläche auf dieses Ereignis reagieren.

Code eingeben

Geben Sie den in Listing 5.1 dargestellten Code in Ihren Editor ein. Dieser zeigt, wie Sie für toggleButton einen Click-Handler implementieren können. Dabei handelt es sich um den vollständigen Quelltext der Methode onCreate() mit dem neuen Code. Es steht Ihnen frei, nur den Code für die Schaltfläche einzugeben oder den gesamten Code von onCreate() zu überscheiben.

@Override
public void onCreate(Bundle savedInstanceState);

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Button toggleButton =

(Button)findViewById(R.id.toggleButton);

toggleButton.setOnClickListener(new

View.OnClickListener();

public void onClick(View v);

}

});

}

Listing 5.1:Die Datei der Klasse MainActivity mit der Methode OnClickListener für die Schaltfläche toggleButton

Der Code verwendet die für alle Android-Aktivitäten verfügbare Methode findViewById(). Mit ihr können Sie beliebige Views im Layout der Aktivität zwecks weiterer Nutzung auffinden. Die Methode gibt immer eine View-Klasse zurück, die Sie in den geeigneten Typ umwandeln müssen, bevor Sie damit weiterarbeiten können. Im folgenden Code (bei dem es sich um einen Auszug aus dem letzten Listing handelt) wird die zurückgegebene View durch findViewById() in den Typ Button (einer Unterklasse von View) umgewandelt.

Button toggleButton =

(Button)findViewById(R.id.toggleButton);

Unmittelbar nach dieser Codezeile beginnen Sie mit der Einrichtung der Ereignisbehandlungsroutine.

Der Code der Ereignisbehandlungsroutine wird nach der Abfrage von Button aus dem Layout eingebettet. Für die Einrichtung der Ereignisbehandlungsroutine müssen Sie nur einen neuen View.OnClickListener erzeugen. Dieser Klick-Listener enthält die Methode Click(), die nach dem Antippen der Schaltfläche aufgerufen wird. Dort werde ich den Code einfügen, der ausgeführt wird, wenn die Schaltfläche für den Lautlosmodus angetippt wird.

Wenn der Datentyp in Ihrer Layoutdatei nicht zum Zieltyp der Umwandlung passt (wenn Sie eine ImageView in der Layoutdatei beispielsweise in einen ImageButton umwandeln wollen), erhalten Sie einen Laufzeitfehler. Achten Sie darauf, dass Sie Ihre View in einen passenden Zieldatentyp umwandeln.

Bei der Eingabe des Codes in Ihren Editor sind Ihnen vielleicht einige rote gewellte Linien aufgefallen, wie sie in Abbildung 5.2 zu sehen sind. Auf diese Weise teilt Ihnen Eclipse mit, dass es nicht weiß, worum es sich bei diesem Button-Dingsbums handelt. Wenn Sie den Cursor eine Weile über der gewellten Linie ruhen lassen, wird ein kleines Kontextfenster angezeigt, das Ihnen einige Optionen zur Auswahl anbietet (Abbildung 5.2).

Führen Sie diese Schritte aus, um das Problem zu beheben:

1. Führen Sie den Mauszeiger über die gewellte Linie und lassen Sie ihn dort eine Weile ruhen.

Es wird ein kleines Kontextfenster angezeigt, in dem mehrere Optionen zur Auswahl gestellt werden (siehe Abbildung 5.2).

2. Wählen Sie die erste Option (IMPORT 'BUTTON').

Dadurch wird diese import-Anweisung am Anfang der Datei eingefügt:

import android.widget.Button;

Diese import-Anweisung teilt Eclipse mit, wo sich Button in den Android-Paketen befindet.

Wahrscheinlich wird bei Ihnen auch View auf ähnliche Weise wie Button hervorgehoben. Gehen Sie noch einmal wie bereits für Button dargestellt vor und importieren Sie auch noch das Paket android.view.

Speichern Sie dann Ihre Änderungen ab, um die Ressourcen zu aktualisieren.

Abbildung 5.2: Eclipse informiert Sie darüber, dass es keine passende Klasse für den hervorgehobenen Bezeichner finden kann.

Code in eine Methode auslagern

Der Code wird bereits ein wenig unübersichtlich und schwer lesbar. An dieser Stelle lagern Sie am besten den Code Ihrer neuen Schaltfläche in eine Methode aus, die Sie dann aus onCreate() heraus aufrufen können. Dazu erstellen Sie eine private void-Methode namens setButtonClickListener(), die den eingegebenen Code für die Schaltfläche enthält. Den Aufruf der neuen Methode fügen Sie in die Methode onCreate() ein. Listing 5.2 zeigt den neuen Code.

public class MainActivity extends Activity;

@Override

public void onCreate(Bundle savedInstanceState);

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

setButtonClickListener(); →8

}

private void setButtonClickListener(); →11

Button toggleButton =

(Button)findViewById(R.id.toggleButton);

toggleButton.setOnClickListener(new

View.OnClickListener();

public void onClick(View v);

// TODO Automatisch generierter Rumpf

}

});

}

}

Listing 5.2: Der in eine Methode ausgelagerte Button-Listener

→8: In dieser Zeile wird die Methode setButtonClickListener aufgerufen, um die Überwachungsfunktion für die Schaltfläche einzurichten.

→11: Die neue Methode wird aufgerufen.

Nun ist es an der Zeit, um auf das Klick-Ereignis zu reagieren und ein wenig Code in die Methode onClick() Ihrer Schaltfläche einzufügen.

Mit den Klassen des Android-Frameworks arbeiten

Nun kommen wir zu den guten und praktischen Dingen an der Android-Entwicklung, die Klassen des Android-Frameworks! Ja, Aktivitäten, Views und Widgets sind zwar integrale Bestandteile des Systems, gehören aber bei allen modernen Betriebssystemen (in der einen oder anderen Art) zum Basiswerkzeug. Gleich geht der Spaß erst richtig los.

Sie werden nun den Status des Klingeltons ermitteln, um festzustellen, ob sich das Telefon im normalen Modus (mehr oder weniger laut klingelnd) oder im Lautlosmodus befindet. Dann können Sie damit beginnen, den Klingelmodus umzuschalten.

Dienste nutzen

Zur Nutzung der Klingel müssen Sie auf den AudioManager von Android zugreifen, der für die Verwaltung des Zustands der Klingel zuständig ist. Da Sie den AudioManager in dieser Aktivität nur wenig intensiv nutzen, wird er am besten in onCreate() initialisiert.

Denken Sie daran, dass alle wichtigen Initialisierungen in onCreate() vorgenommen werden müssen!

Zunächst müssen Sie eine private Variable auf Klassenebene namens mAudioManager erstellen. Geben Sie diese Zeile direkt nach der Zeile mit der Klassendeklaration oben in Ihre Klassendatei ein (Listing 5.3).

package com.dummies.android.lautlosmodus;

import android.os.Bundle;
import android.app.Activity;
import android.media.AudioManager; →4
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity;

private AudioManager mAudioManager; →11

@Override

public void onCreate(Bundle savedInstanceState)

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

setButtonClickListener();

mAudioManager =

(AudioManager)getSystemService(AUDIO_SERVICE); →20

}

private void setButtonClickListener();

Button toggleButton =

(Button)findViewById(R.id.toggleButton);

toggleButton.setOnClickListener(new

View.OnClickListener();

public void onClick(View v);

// TODO Automatisch generierter Rumpf

}

});

}

...

// Automatisch generierter Code für Aktionsleiste

...

}

Listing 5.3: Hinzufügen der Variablen AudioManager auf Klassenebene

Es folgt eine kurze Beschreibung der hervorgehobenen Zeilen:

→4: Mit der import-Anweisung wird das zur Nutzung von AudioManager erforderliche Paket eingebunden.

→11: Die private deklarierte Variable auf Klassenebene AudioManager. Sie wird klassenweit deklariert, um auch in anderen Teilen der Aktivität auf sie zugreifen zu können.

→20: Initialisierung der Variablen mAudioManager durch Übernahme des Dienstes von der Basisklasse Activity über den Methodenaufruf getSystemService().

Langsam! Wie war das mit diesem getSystemService()-Zeug? Durch Beerben der Basisklasse Activity können Sie alle Vorteile von Aktivitäten nutzen. Hier erhalten Sie Zugriff auf den Methodenaufruf getSystemService(). Diese Methode gibt die Java-Basisklasse Object zurück. Daher müssen Sie sie in den von Ihnen benötigten Diensttyp umwandeln.

Dieser Aufruf gibt alle verfügbaren Systemdienste zurück, mit denen Sie möglicherweise arbeiten müssen. Alle zurückgegebenen Dienste finden Sie in der Android-Dokumentation bei der Klasse Context unter http://d.android.com/reference/android/content/Context.html. Zu den beliebten Systemdiensten zählen:

Audiodienste (AUDIO_SERVICE)

Standortdienste (LOCATION_SERVICE)

Alarmdienste (ALARM_SERVICE)

Den Lautlosmodus mit AudioManager umschalten

Da es nun eine klassenweite Instanz von AudioManager gibt, können Sie den Status der Klingel prüfen und diesen umschalten. Das wird in Listing 5.4 erledigt.

package com.dummies.android.lautlosmodus;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class MainActivity extends Activity;

private AudioManager mAudioManager;

private boolean mPhoneIsSilent; →14

@Override

public void onCreate(Bundle savedInstanceState);

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);

checkIfPhoneIsSilent(); →23

setButtonClickListener(); →25

}

private void setButtonClickListener();

Button toggleButton = (Button)findViewById(R.id.toggleButton);

toggleButton.setOnClickListener(new View.OnClickListener();

public void onClick(View v);

if (mPhoneIsSilent); →32

// Umschalten in normalen Modus

mAudioManager

.setRingerMode(AudioManager.RINGER_MODE_ NORMAL);

mPhoneIsSilent = false;

} else;

// Umschalten in Lautlosmodus

mAudioManager

.setRingerMode(AudioManager.RINGER_MODE_SILENT);

mPhoneIsSilent = true;

}

// UI wieder umschalten

toggleUi(); →44

}

});

}

/**

* Feststellen, ob sich Telefon im Lautlosmodus befindet.

*/

private void checkIfPhoneIsSilent(); →53

int ringerMode = mAudioManager.getRingerMode();

if (ringerMode == AudioManager.RINGER_MODE_SILENT);

mPhoneIsSilent = true;

} else;

mPhoneIsSilent = false;

}

}

/**

* UI−Bilder der Modi (Lautlos/Normal) austauschen.

*/

private void toggleUi(); →66

ImageView imageView = (ImageView) findViewById(R.id.phone_icon);

Drawable newPhoneImage;

if (mPhoneIsSilent);

newPhoneImage =

getResources().getDrawable(R.drawable.phone_silent);

} else;

newPhoneImage =

getResources().getDrawable(R.drawable.phone_on);

}

imageView.setImageDrawable(newPhoneImage);

}

@Override →84

protected void onResume();

super.onResume();

checkIfPhoneIsSilent();

toggleUi();

}

}

Listing 5.4: Den Code für den Umschalter zur App hinzufügen.

Da passieren eine Menge neuer Dinge! Zunächst einmal finden Sie oben zwei neue import-Anweisungen, die für zwei ansonsten unbekannte Bezeichner (ImageView und Drawable) in den Code integriert werden müssen. Die übrigen Änderungen in den hervorgehobenen Zeilen werden nachfolgend kurz beschrieben:

→14: Richtet eine neue boolean-Variable auf Klassenebene namens mPhoneIsSilent ein, in der der Status der Klingel gespeichert wird.

→23: Ruft die Methode checkIfPhoneIsSilent() zur Initialisierung von mPhone IsSilent auf. Der Vorgabewert einer boolean-Variablen ist false und könnte falsch sein, wenn sich das Telefon aktuell im Lautlosmodus befindet. Die Variable muss also initialisiert werden, um festzustellen, was passiert, wenn der Klingelmodus umgeschaltet wird.

→25: Der Code für die Ereignisbehandlung der Schaltfläche wurde an das Ende der onCreate()-Methode verschoben, weil er von der Einrichtung der Variablen mPhoneIsSilent abhängig ist. Auch wenn sehr wahrscheinlich nichts passieren wird, achten Sie am besten auf die Codestruktur.

Sauberer Code lässt sich später besser verwalten.

→32: Der Code zwischen den Zeilen 32 und 44 wird ausgeführt, wenn der Benutzer die Umschaltfläche antippt. Er prüft über die Variable auf Klassenebene mPhoneIsSilent, ob die Klingel aktuell aktiviert ist.

Befindet sich das Telefon im Lautlosmodus, springt der Code in den ersten if-Block und ändert den Klingelmodus in RINGER_MODE_NORMAL, der die Klingel wieder einschaltet. Die Variable mPhoneIsSilent wird für den nächsten Codedurchlauf in false geändert.

Wenn sich das Telefon nicht im Lautlosmodus befindet, springt der Code in den else-Block. Hier wird der Klingelmodus von seinem aktuellen Status in RINGER_MODE_SILENT umgeschaltet, der die Klingel abschaltet. Im else-block wird zudem die Variable mPhoneIsSilent für den nächsten Codedurchlauf auf true gesetzt.

→44: Die Methode toggleUi() ändert die Benutzeroberfläche, um optisch auf den geänderten Modus des Telefons hinzuweisen. Diese Methode muss immer aufgerufen werden, wenn der Klingelmodus geändert wird.

→53: Die Methode checkIfPhoneIsSilent() initialisiert die Variable auf Klassenebene mPhoneIsSilent in der Methode onCreate(). Ohne diese Maßnahme würde Ihre App nicht wissen, in welchem Status sich die AudioManager-Klingel befunden hat. Wenn sich das Telefon im Lautlosmodus befindet, wird mPhoneIsSilent auf true, andernfalls auf false gesetzt.

→66: Die Methode toggleUi() ändert die im Layout im letzten Kapitel erstellte ImageView in Abhängigkeit vom Status der Klingel.

Im Lautlosmodus zeigt die Bedienschnittstelle ein Bild, das darauf hinweist, dass die Klingel abgeschaltet ist (Abbildung 4.3). Wenn sich das Telefon im Normalmodus befindet, wird das Bild aus Abbildung 4.2 angezeigt.

Diese beiden Bilder wurden in Kapitel 4 in den Ressourcenordnern abgelegt. ImageView befindet sich im Layout, und nach der Ermittlung des Modus wird View aktualisiert, indem das richtige Bild durch getResources().getDrawable() übernommen und mit dem Aufruf von setImageDrawable() in ImageView eingefügt wird. Diese Methode aktualisiert das in ImageView auf dem Bildschirm angezeigte Bild.

→84: Die Methode onResume() wird überschrieben, damit Ihre App korrekt ermitteln kann, in welchem Status sich das Gerät gerade befindet. Ja, die Variable mPhoneIsSilent wird zwar dazu benutzt, um den Status der Klingel zu speichern, aber das gilt nur für die Klasse. Benutzer Ihrer Anwendung müssen auch wissen, in welchem Status sich das Telefon befindet! Daher ruft onResume() die Methode ToggleUi() auf, um die Bedienschnittstelle umzuschalten.

Der Aufruf toggleUi() befindet sich aus einem einfachen strategischen Grund in der Methode onResume(): Es wird nämlich angenommen, dass der Benutzer die App LAUTLOSMODUS-UMSCHALTER startet und anschließend zum Startbildschirm zurückkehrt und das Telefon über dessen Bedienelemente abschaltet. Wenn der Benutzer wieder zur Aktivität zurückkehrt, wird sie fortgesetzt und in den Vordergrund geholt. Dann wird onResume() aufgerufen, um den Status der Klingel zu prüfen und die Bedienschnittstelle entsprechend zu aktualisieren. Wenn der Benutzer den Modus geändert hat, reagiert die App dann so, wie es der Benutzer erwarten würde!

Ihre Anwendung installieren

Sie haben es geschafft! Sie haben Ihre erste Anwendung geschrieben. In den nächsten paar Schritten werden Sie sie auf dem Emulator installieren und in Aktion sehen können!

Zurück zum Emulator

Wenn Sie Ihre App im Emulator ausführen, sind die ADT gewitzt genug, um sich die zuletzt verwendete Startkonfiguration merken zu können, und verwenden sie anschließend als Vorgabe. (Wenn Sie die Startkonfiguration ändern müssen, um beispielsweise eine andere Activity oder App zu starten, wird die dazu notwendige Vorgehensweise in Kapitel 3 erläutert.)

Der Emulator und Eclipse verständigen sich miteinander über die ADB (Android Debug Bridge). Dieses Werkzeug haben Sie zusammen mit den ADT (Android Development Tools) installiert.

Damit wird es Zeit, die App auf dem Emulator zu installieren! Führen Sie dazu diese Schritte aus:

1. Wählen Sie in Eclipse RUN:RUN oder drücken Sie [Strg]+[F11], um die Anwendung zu starten.

Ihnen wird das Fenster RUN AS präsentiert (Abbildung 5.3).

2. Wählen Sie ANDROID APPLICATION und klicken Sie die Schaltfläche OK an. Damit wird der Emulator gestartet.

Abbildung 5.3: Das Dialogfeld RUN AS

3. Warten Sie, bis der Emulator gestartet wurde, und entsperren Sie ihn dann.

Wenn Sie nicht mehr genau wissen, wie Sie den Emulator entsperren können, lesen Sie noch einmal Kapitel 3. Nach dem Entsperren sollte Ihre App im Emulator gestartet und ausgeführt werden (Abbildung 5.4).

Sollte Ihre App im Emulator nicht ausgeführt werden, starten Sie sie noch einmal über RUN:RUN oder Betätigung von [Strg]+[F11].

Abbildung 5.4: Ihre Anwendung läuft im Emulator.

4. Klicken Sie die Schaltfläche LAUTLOSMODUS UMSCHALTEN an, um sich davon zu überzeugen, dass dann als Bild das durchgestrichene rote Telefon angezeigt wird (Abbildung 5.5).

Beachten Sie auch, dass in der Benachrichtigungsleiste ein Symbol angezeigt wird, das auf den aktivierten Lautlosmodus hinweist.

Abbildung 5.5: Die App im Lautlosmodus mit Benachrichtigungssymbol

5. Kehren Sie durch Anklicken der HOME-Schaltfläche im Emulator zum Startbildschirm zurück.

6. Wechseln Sie zur Anwendungsseite (über die mittlere Schaltfläche unten auf dem Startbildschirm).

Dort finden Sie in der Liste der Anwendungen das Symbol Ihres Programms mit der aus kosmetischen Gründen gekürzten Beschreibung LAUTLOS SCHALTEN, über das Sie Ihre App starten können.

Wenn der Emulator erst einmal gestartet wurde, läuft er eigenständig und unabhängig von Eclipse. Tatsächlich können Sie daher auch Eclipse schließen und den Emulator weiter nutzen.

Apps auf physischen Android-Geräten installieren

Die Installation der Anwendung auf einem Gerät unterscheidet sich nicht von der Installation auf dem Emulator. Sie müssen aber ein paar Anpassungen vornehmen, damit es funktioniert.

Wie in Kapitel 2 bereits erwähnt, eignet sich der USB-Treiber von Google nur für bestimmte Geräte. Wenn Sie andere Geräte verwenden wollen, benötigen Sie daher meist angepasste herstellerspezifische Treiber oder müssen Anpassungen an der einen oder anderen SDK-Initialisierungsdatei vornehmen. Entsprechende Hinweise sollten Sie auf den Websites der Hersteller oder irgendwo im Internet finden können.

Wenn Sie den benötigten Treiber bereits in Kapitel 2 installiert haben, ist der Rest recht einfach:

1. Aktivieren Sie die Installation von Anwendungen unbekannter Herkunft/Quellen, also von Apps, die nicht aus dem Google Play Store stammen.

2. Greifen Sie vom Startbildschirm Ihres Geräts aus über den Anwendungsbildschirm auf die EINSTELLUNGEN zu.

Zumeist heißt die Option UNBEKANNTE HERKUNFT oder UNBEKANNTE QUELLEN und verbirgt sich beispielsweise irgendwo in einer der beiden Unterkategorien SICHERHEIT oder APPS.

3. Aktivieren Sie das Kontrollkästchen UNBEKANNTE HERKUNFT (Abbildung 5.6).

4. Da Sie Ihr Gerät sehr wahrscheinlich auch zur Fehlersuche benutzen wollen, sollten Sie auch wissen, wo Sie das USB-Debugging aktivieren können.

Hier wird es noch ein wenig uneinheitlicher. Teilweise finden Sie hier unter ANWENDUNGEN eine Option namens ENTWICKLUNG mit der Option USB-DEBUGGING, manchmal finden Sie auf dem Anwendungsbildschirm ein separates Symbol namens DEV.-OPTIONEN oder ENTWICKLEROPTIONEN (Abbildung 5.7) oder Sie finden eine entsprechende Kategorie mit den zugehörigen Optionen unter EINSTELLUNGEN.

Mir sind in diesem Zusammenhang auch schon vereinzelt Meldungen untergekommen, in denen davon die Rede war, dass man die entsprechende Option nur über einen bestimmten Bildschirm und mehrmaliges Drücken einer Taste aktivieren und anzeigen lassen konnte. Klar ist dabei nur, dass Sie sich hierüber am besten im Internet informieren.

5. Aktivieren Sie die Option USB-DEBUGGING.

Damit können Sie Ihr Android-Gerät zur Fehlersuche in Ihren Apps nutzen (mehr dazu gleich).

6. Verbinden Sie Ihr Gerät über ein USB-Kabel mit dem Computer.

7. Wenn das Gerät von Ihrem Rechner erkannt wird, starten Sie die App entweder über RUN: RUN oder Drücken von [Strg]+[F11].

Jetzt erkennen die ADT eine weitere Option für eine Startkonfiguration und fragen Sie daher über das Dialogfeld ANDROID DEVICE CHOOSER, auf welchem Gerät die Anwendung gestartet werden soll. In Abbildung 5.8 werden neben einem laufenden Emulator ein älteres Archos- und ein neueres Samsung-Tablet aufgeführt, die zwecks besserer Identifizierbarkeit mit einem anderen Symbol als der Emulator angezeigt werden.

Abbildung 5.6: Einstellung, um die Installation von Anwendungen zuzulassen, die nicht aus dem Google Play Store stammen

Abbildung 5.7:USB-Debugging aktivieren

Der Emulator wird nur dann in der Liste der verfügbaren Geräte angezeigt, wenn er bereits läuft. Ähnliches gilt für angeschlossene USB-Geräte, für die ein USB-Debug-Treiber installiert sein muss und die laufen müssen.

8. Wählen Sie ein Gerät mit geeigneter minimaler SDK-Version in der Liste aus und klicken Sie die Schaltfläche OK an.

Damit wird die Anwendung zum Gerät übertragen und wie im Emulator gestartet. Nach wenigen Sekunden sollte Ihre App auf dem Bildschirm angezeigt werden.

Sie haben Ihre Anwendung auf Ihr Android-Gerät übertragen.

Abbildung 5.8: Das Dialogfeld ANDROID DEVICE CHOOSER

Wenn Sie Ihre App ändern und erneut testen wollen, müssen Sie sie auch erneut auf Ihrem Gerät installieren. Dazu müssen Sie nur jeweils Ihr Gerät anschließen und RUN:RUN wählen oder die Tastenkombination [Strg]+[F11] betätigen.

Oh je! Auf Fehler reagieren

Sie haben perfekten Code geschrieben, oder? Aber auch wenn bisher alles tadellos und ohne Fehler gelaufen ist, wird der Tag kommen, an dem es einmal nicht wie geplant läuft. Und dann müssen Sie die Ursache der Probleme ermitteln. Um Entwicklern in der schrecklichen Situation scheinbar zufälliger Anwendungsabstürze zu unterstützen, stellen die ADT einige wertvolle Werkzeuge zur Verfügung, mit denen Sie Ihre Anwendung debuggen können.

Den DDMS (Dalvik Debug Monitor Server) benutzen

Der DDMS (Dalvik Debug Monitor Server) ist ein Debugging-Werkzeug, das Ihnen beispielsweise die folgenden Funktionen bereitstellt:

Portweiterleitung

Erstellung von Bildschirmfotos

Thread- und Heap-Informationen für das Gerät

LogCat (liefert Auszüge mit Einträgen in den Systemprotokollen)

Informationen über Prozesse und Funkverbindungen

Simulieren von Anrufen und Kurzmitteilungen (SMS)

Simulieren von Positionsdaten (GPS)

DDMS unterstützt die Zusammenarbeit mit dem Emulator und physisch angeschlossenen Geräten. DDMS befindet sich im Ordner tools des Android-SDKs. In Kapitel 1 haben Sie den Ordner tools für Ihre Plattform zum Systempfad hinzugefügt. Daher sollten Sie über die Befehlszeile auf DDMS zugreifen können.

Warum Sie DDMS kennen lernen sollten

Die Fehlersuche macht selten Spaß. Glücklicherweise enthält der DDMS die zur Entwanzung erforderlichen Werkzeuge. Bei einer der am häufigsten benutzten Funktionen handelt es sich um die LogCat-Anzeige, über die Sie die von Ihrem System ausgegebenen Meldungen im Systemprotokoll betrachten können (Abbildung 5.9).

Abbildung 5.9: Ein Protokollauszug in LogCat

In diesen Systemprotokollen finden sich Meldungen von einfachen Informationen zum Zustand der Anwendungen und des Geräts bis hin zu Warnungen und Fehlerinformationen. Wenn das Gerät meldet, dass eine Anwendung nicht mehr reagiert (Application Not Responding) oder wegen eines Fehlers zwangsweise geschlossen wird (Force Close), ist nicht klar, was passiert ist. Hier kann es Ihnen möglicherweise helfen, wenn Sie DDMS starten und die Einträge in LogCat untersuchen, denn sie liefern sogar die Zeilennummer, in der der Ausnahmefehler aufgetreten ist.

DDMS behebt zwar den Fehler nicht für Sie, kann Ihnen aber enorm dabei helfen, der Wurzel des Übels auf den Grund zu gehen.

DDMS ist auch sehr nützlich, wenn Ihnen zu Testzwecken kein physisches Gerät zur Verfügung steht. Ein gutes Beispiel dafür ist die Entwicklung einer Anwendung, die GPS-Daten (Global Positioning System) und mit Google-MapView Landkarten auf dem Bildschirm anzeigt. Ihre Anwendung beruht darauf, dass sich ein Benutzer innerhalb der Karte bewegt. Wenn Sie kein Gerät mit GPS-Unterstützung oder auch gar kein Android-Gerät besitzen, wird dies bestimmt keine leichte Aufgabe! Aber glücklicherweise kann DDMS hier helfen. DDMS enthält Werkzeuge zur Positionskontrolle. Als Entwickler können Sie manuell GPS-Koordinaten bereitstellen oder Dateien im GPX-Format (GPS eXchange Format) oder KML (Keyhole Markup Language) nutzen, die Punkten auf einer Karte entsprechen und zudem noch entsprechende Zeitangaben enthalten können. (Beispielsweise in der Art, hier fünf Sekunden verharren, dorthin gehen und von da aus weiter zum nächsten Punkt.)

Ich kann hier nur oberflächlich auf DDMS und seine Funktionen eingehen. Ich werde Ihnen zeigen, wie Sie Meldungen an DDMS übergeben und wie Sie sie mit Eclipse betrachten können.

Protokolleinträge für DDMS erzeugen

Um Protokolleinträge für DDMS erzeugen zu können, müssen Sie nur eine einzige Zeile Code schreiben. Öffnen Sie dazu die Datei MainActivity.java und fügen Sie am Ende der Methode onCreate einen Log-Eintrag hinzu (Listing 5.5).

@Override
public void onCreate(Bundle savedInstanceState);

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);

checkIfPhoneIsSilent();

setButtonClickListener();

Log.d("SilentModeApp", "Dies ist ein Test"); →12

}

Listing 5.5: Die Methode onCreate()

Der Code in der mit  →12 markierten Zeile demonstriert, wie Sie für einen Eintrag im Systemprotokoll sorgen können. SilentModeApp ist einfach nur ein Bezeichner (TAG), den Sie für diesen Protokolleintrag wählen. Beim zweiten Parameter des Log-Aufrufs handelt es sich dann um die auszugebende Meldung. Über den Bezeichner können Sie Einträge bei deren Betrachtung in DDMS filtern.

Eine gute Konvention, an die Sie sich halten können, ist die Deklaration einer Konstanten namens TAG im Code, die Sie dann verwenden können, ohne jeweils einen neuen Bezeichner eingeben zu müssen. Ein Beispiel wäre:

private static final String TAG = "SilentModeApp";

Beachten Sie in Listing 5.5 das d in Log.d. Das d weist darauf hin, dass es sich um einen Debugging-Eintrag handelt. Die anderen Optionen sind:

e: Fehler (error)

I: Information
wtf: Fürchterlicher Fehler (What a terrible failure). Ja, diese Option gibt es wirklich!

v: ausführlich (verbose)

Die verschiedenen Protokollierungsvarianten gibt es, damit Sie entscheiden können, wie unterschiedliche Einträge protokolliert werden sollen.

Damit die Protokollierung funktioniert, müssen Sie das Paket android. util.Log importieren.

DDMS-Meldungen betrachten

Wahrscheinlich fragen Sie sich, wie Sie die DDMS-Meldungen betrachten können. Dazu können Sie entweder DDMS manuell starten oder die DDMS-Perspektive in Eclipse öffnen:

Manuelle Vorgehensweise: Navigieren Sie dorthin, wo Sie das Android-SDK installiert haben. Im Ordner tools klicken Sie die Datei ddms.bat doppelt an. Damit wird die DDMS-Anwendung außerhalb der Eclipse-IDE gestartet (Abbildung 5.10).

Abbildung 5.10: Eine getrennt von Eclipse ausgeführte DDMS-Instanz

Bei den aktuellen Android-SDK-Versionen wird die DDMS-Verwendung nicht mehr empfohlen. Stattdessen sollten Sie hier besser den Android Debug Monitor verwenden. Sie können ihn ähnlich wie DDMS starten, klicken aber abschließend monitor.bat doppelt an.

In Eclipse: Mit den ADT wurde eine DDMS-Perspektive installiert. Um diese zu öffnen, klicken Sie rechts oben im Eclipse-Fenster die Schaltfläche OPEN PERSPECTIVE an. Im dann angezeigten Dialogfeld wählen Sie DDMS (Abbildung 5.11).

Abbildung 5.11: Die Schaltfläche OPEN PERSPECTIVE und das gleichnamige Dialogfeld

DDMS können Sie alternativ auch über die Menüleiste und WINDOW:OPEN PERSPECTIVE starten. Falls DDMS nicht direkt als Option angezeigt wird, sollten Sie diese über OTHER erreichen können. Damit wird eine DDMS-Perspektive zur Auswahlliste hinzugefügt, über die Sie leicht zwischen den Perspektiven umschalten können. Es sollte automatisch zur DDMS-Perspektive umgeschaltet worden sein. Hier können Sie LOGCAT (normalerweise unten im Fenster) einsehen. Ich selbst verschiebe den LOGCAT-Bereich lieber in den Hauptbereich des Eclipse-Fensters (siehe Abbildung 5.12). Dazu ziehen Sie die LOG-Registerkarte über deren Titel einfach an die gewünschte Zielposition und lassen sie dort fallen.

Starten Sie nun Ihre Anwendung über RUN:DEBUG oder drücken Sie [F11]. Wenn Ihre App im Emulator läuft, öffnen Sie die DDMS-Perspektive und betrachten Ihre Protokolleinträge. Diese sollten denen in Abbildung 5.13 ähneln. Die anderen Meldungen des Systemprotokolls können bei Ihrem Rechner zwar anders aussehen, aber der selbst definierte Protokolleintrag sollte wie bei mir aussehen.

Abbildung 5.12: Das Fenster LOGCAT im Hauptfenster von Eclipse

Abbildung 5.13: LogCat-Meldungen in Eclipse über die DDMS-Perspektive betrachten

Zur Java-Perspektive können Sie durch Anklicken der Schaltfläche JAVA PERSPECTIVE zurückkehren (Abbildung 5.14).

Abbildung 5.14: Zurück zur Java-Perspektive

Den Eclipse-Debugger nutzen

Auch wenn DDMS vielleicht einer Ihrer besten Verbündeten ist, bleibt der Eclipse-Debugger doch Ihre mächtigste Waffe gegen die Heerscharen von Wanzen. Mit dem von Eclipse zur Verfügung gestellten Debugger können Sie unter anderem verschiedene Haltepunkte (Breakpoints) setzen, Variablen im Watch-Fenster beobachten und LogCat anzeigen.

Sie können den Debugger zur Suche nach Laufzeitfehlern oder logischen Fehlern nutzen. Syntaxfehler werden bereits von Eclipse abgefangen. Dann lässt sich die Anwendung nicht kompilieren und Eclipse informiert Sie über Symbole und gewellte Linien darüber, wo Probleme im Code bestehen.

Laufzeitfehler prüfen

Laufzeitfehler sind wie die bösen Hexen des Ostens. Sie tauchen aus dem Nichts heraus auf und hinterlassen nichts als Ruinen. In Android können Laufzeitfehler bei der Ausführung Ihrer Apps auftreten. Beispielsweise könnte Ihre Anwendung längere Zeit problemlos funktionieren, um dann beim Ausführen einer bestimmten Aktion (zum Beispiel beim Anklicken eines Menüs oder einer Schaltfläche) plötzlich abzustürzen. Dafür gibt es nahezu unendlich viele mögliche Ursachen. Vielleicht haben Sie vergessen, AudioManager in der Methode onCreate() zu initialisieren, und dann später in der App versucht, auf die Variable zuzugreifen. Das würde einen Laufzeitfehler verursachen.

In derartigen Situationen kann der Debugger helfen, weil Sie mit ihm einen Haltepunkt an den Anfang von onCreate() setzen können, um die Werte der Variablen über die Debug-Perspektive untersuchen zu können. Dann würden Sie erkennen, dass Sie vergessen haben, AlarmManager zu initialisieren.

Listing 5.6 soll das geschilderte Szenario verdeutlichen. Darin wurde die AlarmManager-Initialisierung auskommentiert, was während der Laufzeit zu einem Ausnahmefehler führt.

private AudioManager mAudioManager; →1
private boolean mPhoneIsSilent;

@Override
public void onCreate(Bundle savedInstanceState);

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

// mAudioManager = →9

//     (AudioManager)getSystemService(AUDIO_SERVICE);

checkIfPhoneIsSilent();

setButtonClickListener();

Log.d("SilentModeApp", "Dies ist ein Test");

}

...

/**

* Testen, ob Telefon im Lautlosmodus

*/
private void checkIfPhoneIsSilent();

int ringerMode = mAudioManager.getRingerMode(); →22

if (ringerMode == AudioManager.RINGER_MODE_SILENT);

mPhoneIsSilent = true;

} else;

mPhoneIsSilent = false;

}

}

Listing 5.6: Auskommentieren der AlarmManager-Initialisierung

→1: Die Variable auf Klassenebene AudioManager wird eingeführt.

→9: Beim Testen habe ich diesen Code versehentlich in einen Kommentar umgewandelt. Damit bleibt die Variable mAudioManager uninitialisiert.

→22: Nach dem Aufruf von checkIfPhoneIsSilent() durch onCreate() kommt es zu einem Ausnahmefehler, weil mAudioManager den Wert null hat und ich ein Mitglied dieses (nicht existierenden!) Objekts aufzurufen versuche.

Wenn ich den Debugger auf die Methode onCreate() ansetze, kann ich der Fehlerursache auf den Grund gehen.

Haltepunkte setzen

Es gibt mehrere Möglichkeiten, Haltepunkte zu setzen:

Markieren Sie die Zeile, in der ein Haltepunkt gesetzt werden soll, durch Anklicken mit der Maus. Wählen Sie RUN:TOGGLE BREAKPOINT (Abbildung 5.15).

Markieren Sie die Zeile, in der Sie einen Haltepunkt setzen wollen, durch Anklicken mit der Maus. Drücken Sie dann [Strg]+[Shift]+[B]. Diese Tastenkombination wird auch im Menü angegeben (siehe Abbildung 5.15).

Klicken Sie die linke Randspalte im Eclipse-Editor dort doppelt an, wo der Haltepunkt gesetzt werden soll.

Klicken Sie die linke Randspalte im Eclipse-Editor dort mit der rechten Maustaste an, wo der Haltepunkt gesetzt werden soll, und wählen Sie im Kontextmenü TOGGLE BREAKPOINT.

Abbildung 5.15: Einen Haltepunkt über das Menü oder eine Tastenkombination setzen.

Bei den verschiedenen Verfahren wird ein kleines rundes Symbol in der linken Spalte im Eclipse-Editor angezeigt (Abbildung 5.16).

Abbildung 5.16: Ein gesetzter Haltepunkt wird in der linken Spalte im Eclipse-Editor angezeigt.

Nun sollen Sie sich selbst an einem praktischen Beispiel mit dem Debugger ein wenig vertraut machen. Kommentieren Sie dazu die in Listing 5.7 mit  →3 gekennzeichnete Zeile der Methode onCreate() aus.

setContentView(R.layout.main);

//mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE); →3

checkIfPhoneIsSilent(); →5

Listing 5.7:Um einen Fehler zu erzeugen, wurde hier eine Zeile auskommentiert.

→3: Die AudioManager-Zeile wurde auskommentiert.

→5: Die Methode wird aufgerufen und führt zum Fehler in der App.

Setzen Sie nun mit einem der dargestellten Verfahren einen Haltepunkt in der mit  →5 gekennzeichneten Zeile.

Den Debugger und die Debug-Perspektive starten

Bevor Sie mit der Fehlersuche beginnen können, müssen Sie noch eine Sache überprüfen beziehungsweise erledigen. Sie müssen dafür sorgen, dass die Android-App im Fehlersuchmodus ausgeführt wird. Öffnen Sie dazu über den PACKAGE EXPLORER die Datei Android Manifest.xml, aktivieren Sie unten im Fenster die Registerkarte APPLICATION, wählen Sie die Eigenschaft DEBUGGABLE und setzen Sie sie auf true (siehe Abbildung 5.17). Speichern Sie nun die Datei.

Wenn Sie die Eigenschaft DEBUGGABLE nicht auf true setzen, werden Sie nie nach Fehlern in Ihrer App suchen können. Ihre Anwendung wird nicht einmal versuchen, eine Verbindung zum Debugger herzustellen. Wenn Probleme beim Debugging auftreten, sollten Sie erst einmal hier prüfen, ob diese Eigenschaft entsprechend gesetzt wurde.

Abbildung 5.17: Die Anwendung auf die Fehlersuche vorbereiten

Führen Sie die folgenden Schritte zur Fehlersuche in Ihrem Code aus:

1. Um den Debugger zu starten, wählen Sie RUN:DEBUG oder drücken [F11].

Damit teilen Sie den ADT und Eclipse mit, dass die Anwendung auf dem Emulator (oder Gerät) installiert und dann eine Verbindung zum Debugger hergestellt werden soll.

2. Starten oder öffnen Sie den Emulator.

Die Anwendung wird installiert und Sie sehen nun einen Bildschirm, der ähnlich wie in Abbildung 5.18 aussehen sollte. Darin werden Sie darüber informiert, dass die ADT und der Emulator hinter den Kulissen eine Verbindung herzustellen versuchen.

Der Emulator braucht vielleicht eine Weile, bis die Verbindung zum Debugger hergestellt werden kann. Anschließend führt er den Code Ihrer Anwendung aus und stoppt beim Erreichen des ersten Haltepunkts.

Dann wird ein Dialogfeld angezeigt, in dem Sie gefragt werden, ob die Debug-Perspektive geöffnet werden soll (Abbildung 5.19).

Abbildung 5.18: Eine Anwendung auf einem Testgerät wartet auf die Verbindung zum Debugger.

Abbildung 5.19: Die Debug-Perspektive aktivieren

3. Klicken Sie YES an, um die Debug-Perspektive zu öffnen.

Das Programm sollte sich nun am Haltepunkt befinden (Abbildung 5.20). Sie können den Mauszeiger über Variablen verweilen lassen, um sich deren Werte anzeigen zu lassen.

4. Führen Sie den Mauszeiger über die Variable mAudioManager.

Sie sollten sehen, dass ihr Wert aktuell null ist (siehe Abbildung 5.20), weil Sie den Initialisierungscode auskommentiert haben.

Abbildung 5.20: Die Debug-Perspektive mit Erläuterungen

Abbildung 5.20 zeigt auch, über welche Schaltflächen Sie den Code schrittweise ausführen und fortsetzen können. Wenn Sie drei Mal die Schaltfläche RESUME (Fortsetzen) anklicken oder [F8] drücken, können Sie sehen, wie sich die Debug-Perspektive ändert und schließlich source not found meldet. Wenn Sie dann zum Emulator wechseln, werden Sie feststellen, dass Ihre App abgestürzt ist (Abbildung 5.21). Im Google Play Store ist dieser Bildschirm als »Force Close«, »FC« oder »Schließen erzwingen« bekannt. Das Schließen einer App wird dann erzwungen, wenn zur Laufzeit ein Ausnahmefehler aufgetreten ist, der nicht im Code behandelt wird.

5. Um die Verbindung zum Debugger zu trennen, klicken Sie die Schaltfläche DISCONNECT (Trennen) an.

Kehren Sie zur Java-Perspektive zurück und entfernen Sie die in der Datei MainActivity .java in der mit  →3 markierten Zeile (Listing 5.7) gesetzten Kommentarzeichen wieder, damit Ihre App wieder fehlerfrei kompiliert werden kann.

Abbildung 5.21: Ein »Schließen erzwingen«-Dialogfeld im Emulator, das aufgrund
eines Ausnahmefehlers zur Laufzeit angezeigt wird.

Logische Fehler aufspüren

Computer machen genau das, was Sie ihnen sagen, und wenn es um die eigentliche Logik des Programms geht, ist auch das kleine Smartphone nicht smart genug, um zu wissen, was richtig und was falsch ist. Listing 5.8 zeigt ein Beispiel für einen Fehler in der sprachlichen Logik.

/**

* UI−Bilder der Modi (Lautlos/Normal) austauschen.

*/
private void toggleUi();

ImageView imageView =

(ImageView) findViewById(R.id.phone_icon);

Drawable newPhoneImage;

if (mPhoneIsSilent); →11

newPhoneImage = getResources().

getDrawable(R.drawable.phone_silent);

} else;

newPhoneImage = getResources().

getDrawable(R.drawable.phone_on);

}

imageView.setImageDrawable(newPhoneImage);

}

@Override
protected void onResume();

super.onResume();

//checkIfPhoneIsSilent(); →26

toggleUi();

};

Listing 5.8:Dieser Code prüft nicht, ob sich das Telefon im Lautlosmodus befindet.

Listing 5.8 funktioniert wie folgt:

→11: Diese Zeile prüft, ob sich das Telefon aktuell im Lautlosmodus befindet.

→26: Damit die Methode toggleUi() dem Benutzer die richtige Bedienschnittstelle anzeigen kann, muss die App wissen, in welchem Zustand sich die Klingel aktuell befindet. In dieser Zeile habe ich versehentlich die Methode checkIfPhoneIsSilent() auskommentiert, die die Variable auf Klassenebene mPhoneIsSilentVariable aktualisiert.

Da dies in der Methode onResume() geschieht, könnte der Benutzer die App verlassen, den Status der Klingel über die Einstellungen des Telefons ändern und wieder zur App zurückkehren. Diese befände sich dann einfach wegen eines logischen Fehlers im falschen Zustand!

Bei Verwendung eines Debuggers könnten Sie einen Haltepunkt in der ersten Zeile der Methode toggleUi() setzen, um die verschiedenen Variablen zu untersuchen, die den logischen Aufruf unterstützen. Dann würden Sie feststellen, dass die Variable mPhoneIsSilent nicht gesetzt wird.

Über Anwendungsgrenzen hinweg denken

Manchmal verrichtet das Gerät extern Arbeiten, die sich auf Ihre App auswirken könnten, wie zum Beispiel das Herunterladen großer Dateien im Hintergrund, während Musik über eine Online-Radioanwendung wiedergegeben wird. Können diese starken Netzwerkaktivitäten den Ablauf der Anwendung irgendwie beeinträchtigen? Nun, das hängt davon ab. Wird Ihre App abstürzen, wenn sie auf eine Internetverbindung angewiesen ist und diese aus irgendwelchen Gründen nicht herstellbar ist? Was wird geschehen? Um die Antworten auf diese Fragen zu kennen, müssen Sie ein wenig über die Grenzen der eigenen Anwendung hinweg denken.

Nicht alle Anwendungen sind gleich gut. Und glauben Sie mir, ich habe einige gute und einige wirklich schlechte gesehen. Bevor Sie Ihre erste Android-Anwendung erstellen oder veröffentlichen, müssen Sie dafür sorgen, dass Sie die Vor- und Nachteile Ihrer App und alle Aspekte, die sie beeinträchtigen könnten, wirklich kennen. Sie müssen dafür sorgen, dass sie nicht abstürzt, wenn Benutzer die üblichen Tippereignisse erzeugen und auf dem Bildschirm navigieren.

Die Erstellung von Anwendungen für eingebettete Geräte unterscheidet sich einfach deshalb erheblich von der für einen PC oder Mac, weil Ihre Ressourcen (Speicher, Prozessor und so weiter) stark beschränkt sind. Wenn es sich bei dem Android-Gerät um ein Telefon handelt, besteht dessen Hauptaufgabe darin, seine Telefonfunktionen zu erfüllen und zum Beispiel eingehende Anrufe zu erkennen, diese zu signalisieren und Textmeldungen zu versenden und zu empfangen.

Wird ein Telefongespräch geführt, handelt es sich dabei für das Android-System um einen kritischen Prozess, während das Herunterladen von Dateien im Hintergrund weniger wichtig ist. Wenn dem Telefon die Ressourcen auszugehen beginnen, wird Android alle weniger wichtigen Prozesse zerstören, um die kritischen bedienen zu können. Dateien können erneut heruntergeladen werden, während abgebrochene Verbindungen einfach weg sind und der Anruf erneut getätigt werden muss. Natürlich können Sie noch einmal anrufen, aber das wird den Benutzer des Android-Geräts nur frustrieren, wenn er es in erster Linie als Telefon gekauft hat. Während Ihre App Dateien im Hintergrund herunterlädt, könnte sie zerstört werden, und dieses Szenario müssen Sie testen. Ähnliches könnte geschehen, wenn Funkverbindungen schlechter werden oder abreißen. Die Verbindung geht verloren und die Datei wird nicht heruntergeladen.

Sie müssen alle möglichen Lösungen prüfen und für geeignete Schutzmaßnahmen sorgen. Ansonsten wird Ihre laufende App anfällig für Ausnahmefehler, was zu schlechten Rezensionen im Google Play Store führen kann.

Mit Ihrer Anwendung interagieren

Um sich davon zu überzeugen, dass Ihre App funktioniert, müssen Sie sie einfach nur starten und mit ihren Funktionen spielen. Starten Sie andere Apps, wie zum Beispiel den Browser, während Ihre Anwendung läuft. Surfen Sie eine Weile im Internet und kehren Sie dann wieder zu Ihrer App zurück. Klicken Sie einige der Schaltflächen in Ihrer App an und sehen Sie, was passiert. Probieren Sie verschiedene Dinge aus, um zu sehen, ob es zu Resultaten kommt, an die Sie vielleicht nicht gedacht haben. Was geschieht, wenn ein Benutzer Ihre App verwendet, während er angerufen wird? Speichern Sie den Zustand wie erforderlich in onPause() und stellen ihn in onResume() wieder her?

Android nimmt Ihnen zwar die schwierige Taskverwaltung ab, aber bei Ihrer App sind ausschließlich Sie selbst für die Statusverwaltung verantwortlich.

Prüfen Sie, ob Ihre App funktioniert

Starten Sie auf Ihrem Emulator die Anwendung LAUTLOSMODUS-UMSCHALTER. Damit ist bereits der erste Testschritt erledigt, denn Sie haben sich damit davon überzeugt, dass Ihre App startet!

Wenn die App läuft, prüfen Sie durch einen Blick in die Benachrichtigungsleiste, ob sich das Telefon im Lautlosmodus befindet und dort das kleine Telefonsymbol angezeigt wird (Abbildung 5.22).

Abbildung 5.22: Das Telefonsymbol im Benachrichtigungsbereich

Klicken Sie die Schaltfläche LAUTLOSMODUS UMSCHALTEN an, um in den Klingelmodus zu wechseln. Wird das von Ihrer App angezeigte Bild geändert und wechselt zum grünen Telefon (oder zum roten)? Probieren Sie verschiedene Dinge aus, um sich davon zu überzeugen, dass Ihre App wie erwartet funktioniert. Wenn Sie einen Fehler finden, benutzen Sie die in diesem Kapitel vorgestellten Debugging-Tools, um die mögliche Ursache zu ermitteln.

Welche automatischen Testmöglichkeiten gibt es?

Angesichts der im letzten Jahrzehnt aufgekommenen agilen Methoden ist es nur eine Frage der Zeit, bis Sie sich fragen, welche automatischen Testmöglichkeiten Sie mit Android haben. Mit dem SDK werden auch Android-Testwerkzeuge installiert, mit denen Sie nicht nur Java-Klassen, sondern auch auf Android basierende Klassen und Interaktionen mit der Benutzerschnittstelle testen können. Mehr über die entsprechenden Testmöglichkeiten erfahren Sie aus der Android-Dokumentation unter http://d.android.com/guide/topics/testing/testing_android.html.

Die folgenden Tools stehen Ihnen zur Verfügung:

JUnit: Mit dem SDK wird die JUnit-Integration in die ADT installiert. JUnit ist ein sehr beliebtes Framework zum Testen von Units, das in Java verwendet wird. Mit JUnit können Sie einzelne Units (Klassen oder Methoden) oder die Interaktion mit Anwendungen testen. Mehr erfahren Sie unter www.junit.org. Um Ihnen die Nutzung zu erleichtern, enthält Eclipse integrierte Werkzeuge, mit denen Sie Tests mit JUnit aus der Eclipse-Umgebung heraus durchführen können.

Monkey: Bei Monkey handelt es sich um ein Prüfsystem für Benutzeroberflächen/Anwendungen. Das Programm wird auf Ihrem Emulator oder Gerät ausgeführt und erzeugt pseudozufällige Folgen von Benutzerereignissen. Dazu zählen Fingertipps, Gesten, Berührungen, Klicks und auch eine Reihe von Systemereignissen. Monkey bietet eine gute Möglichkeit, um Belastungstests für Ihre App durchzuführen. Monkey wird zusammen mit dem Android-SDK installiert.

6

Grundlagen von Android-Ressourcen

In diesem Kapitel

Warum Ressourcen bei Android so wichtig sind

Ressourcen übernehmen

Mit Bildressourcen arbeiten

Ressourcen wurden in diesem Buch bereits recht ausführlich beschrieben, weshalb Sie sich vielleicht fragen werden, warum sie hier erneut behandelt werden. Die Informationen über Ressourcen und deren Nutzung in den Kapiteln 3 und 4 waren notwendig, um die Grundlagen des Ressourcenordners verstehen und eine einfache Anwendung erstellen zu können. Es gibt aber viele andere triftige Gründe für die Nutzung von Ressourcen in Ihrer Anwendung, zu denen auch die Globalisierung zählt, um die es in diesem Kapitel gehen wird.

Ressourcen verstehen

Bei Ressourcen handelt es sich um die zusätzlichen statischen Inhalte und Dateien, die zwar Bestandteil Ihrer App sind, aber nicht zum Java-Code gehören. Ressourcen können unter anderem diese Anwendungselemente sein:

Layouts

Strings

Bilder

Abmessungen

Formatvorlagen (Style Sheets)

Schemas (Bildschirmdesigns)

Werte

Menüs

Farben

Layouts, Strings und Bilder habe ich Ihnen bereits vorgestellt, weil es sich dabei um die meistverwendeten Ressourcenarten bei der alltäglichen Entwicklung von Android-Apps handelt. Die Rolle der anderen Ressourcen ist Ihnen möglicherweise nicht ganz klar, weshalb ich sie hier kurz erläutern werde.

Abmessungen

In einer Android-Ressource ist eine Abmessung (Dimension) eine Zahl, der eine Maßeinheit folgt, wie zum Beispiel 10px, 2in oder 5sp. Bei der Angabe beliebiger Eigenschaften in Android, die numerische Größenangaben erfordern, verwenden Sie Abmessungen. Vielleicht soll ein bestimmter Abstand in einem Layout zehn Pixel (10px) groß sein. Die folgenden Maßeinheiten werden von Android unterstützt:

dp (density-independent pixels): Hierbei handelt es sich um eine abstrakte Einheit, die auf der physischen Dichte beziehungsweise dem Abstand der Bildschirmpixel voneinander basiert. Die Einheiten werden relativ zu einem 160-dpi-Bildschirm (dots per inch) angegeben. 1 dp entspricht daher einem Pixel auf einem 160-dpi-Bildschirm. Das Verhältnis von dp zu Pixeln ändert sich zwar mit der Bildschirmdichte, aber nicht notwendigerweise proportional. Diese Maßeinheit benutze ich bevorzugt beim Erstellen meiner Layouts.

Das dp-Konzept ist recht komplex, weshalb Sie sich näher damit befassen sollten, wenn Sie mehrere verschiedene Bildschirmdichten unterstützen wollen. Der Abschnitt »Supporting Multiple Screen Sizes« unter http://developer .android.com/guide/practices/screens_support.html ist zu diesem Thema Pflichtlektüre.

sp (scale-independent pixels): Diese Maßeinheit ähnelt »dp«, wird aber entsprechend der Benutzervoreinstellungen für die Schriftgröße skaliert. sp-Abmessungen sollten Sie für die Angabe von Schriftgrößen in Ihren Apps verwenden.

px (Pixel): Diese Maßeinheit entspricht physischen Bildschirmpunkten. Sie sollten sie nicht verwenden, weil Ihre App dann möglicherweise zwar auf einem Gerät mit mittlerer Dichte gut aussieht, aber auf einem mit hoher Dichte nur noch verzerrt angezeigt wird und verloren wirken könnte (und umgekehrt). Das liegt daran, dass auf den verschiedenen Geräten unterschiedlich viele Pixel je Zoll (oder Zentimeter) angezeigt werden.

pt (Punkt): Ein Punkt (point) misst 1/72 Zoll und basiert auf den physischen Bildschirmabmessungen. Die Nutzung von »pt« ist so wie »px« nicht empfehlenswert.

mm (Millimeter): Basiert auf den physischen Bildschirmabmessungen und ähnelt damit dem Punkt. Auch von »mm« ist abzuraten.

in (Inches/Zoll): Basiert wie pt und mm auf den physischen Bildschirmabmessungen. Auch von der Nutzung von »in« lässt sich nur abraten.

Styles (Formatvorlagen)

Mit Formaten und Formatvorlagen (Styles und Style Sheets) können Sie Ihre Apps (einheitlich) gestalten! Die Styles in Android ähneln stark den im Bereich der Webentwicklung verwendeten CSS-Vorlagen (CSS – Cascading Style Sheets). Ein Format (Style) ist eine Zusammenstellung von Eigenschaften, die auf beliebige Views (innerhalb der Layoutdatei), Aktivitäten oder ganze Apps (über die Manifest-Datei) angewendet werden können. Formate unterstützen die Vererbung, weshalb Sie erst ein recht grundlegendes Format definieren können, das Sie dann für die jeweiligen Einsatzzwecke Ihrer App ändern können. Zu den Formateigenschaften zählen beispielsweise Schriftgröße, Schriftfarbe und Bildschirmhintergrund.

Themes (Schemas)

Als Thema, Schema oder Design (im Windows-Jargon) wird ein Format bezeichnet, das auf eine komplette Aktivität oder App und nicht nur auf einzelne Steuerelemente angewendet wird. Wenn ein Format als Thema benutzt wird, erben alle Views in der Aktivität und/oder Anwendung die Formateinstellungen. Sie können so beispielsweise für alle TextView-Elemente eine bestimmte Schriftart vorgeben, die dann für alle Views in der entsprechenden Aktivität oder Anwendung benutzt wird.

Werte

Wertressourcen können höchst unterschiedliche Arten von Ressourcen für Ihre App enthalten. Dazu zählen beispielsweise:

Bool: Ein in XML definierter boolescher Wert wird unter einem beliebigen Dateinamen in der Datei res/values/<dateiname>.xml gespeichert. Dabei steht <dateiname> für den Namen der Datei. Ein Beispiel wäre bools.xml.

Integer: Ein in XML definierter ganzzahliger Wert wird unter einem beliebigen Dateinamen in der Datei res/values/<dateiname>.xml gespeichert. Ein Beispiel wäre integers.xml.

Integer-Array: Die Werte eines in XML definierten Integer-Arrays werden unter einem beliebigen Dateinamen in der Datei res/values/<dateiname>.xml gespeichert, wobei auch hier wieder <dateiname> den frei wählbaren Dateinamen repräsentiert. Mit derartigen Ganzzahlen können Sie in Ihrem Code beispielsweise Schleifen, Abmessungen und andere Elemente leichter definieren.

Typisiertes Array: Ein typisiertes Array wird zum Erstellen einer Reihe von Ressourcen (zum Beispiel drawables) verwendet. Sie können Arrays mit gemischten Datentypen erstellen. Daher müssen Arrays nicht homogen sein. Sie müssen die verwendeten Datentypen aber kennen, um sie passend umwandeln zu können. Wie bei den anderen Werten benutzen Sie auch hier zur Speicherung wieder einen beliebigen Dateinamen nach dem Muster res/values/<dateiname>.xml. Ein Beispiel wäre types.xml.

Menüs

Menüs lassen sich entweder über den Quelltext oder über XML definieren. Bevorzugt sollten Sie Menüs über XML erstellen und im Ordner menus/ ablegen. Für jedes Menü wird eine eigene .xml-Datei verwendet.

Farben

Bei der Farbdatei handelt es sich um die Datei values/colors.xml. Über diese Datei können Sie Namen für Farben verwenden, wie zum Beispiel login_screen_font_color. Dabei könnte es sich um die Farbe für die auf einer Anmeldeseite verwendeten Schriftart handeln. Farben werden über ihre jeweiligen Hexadezimalwerte definiert.

Arbeiten mit Ressourcen

In diesem Buch haben Sie bereits mehrfach mit Ressourcen gearbeitet. Mittlerweile wissen Sie wahrscheinlich bereits, dass Sie die Klasse R verwenden müssen, um aus Ihrer Anwendung heraus auf Ressourcen zuzugreifen. Wenn Sie Ihr Wissen über Ressourcen und die erzeugte Datei R noch einmal auffrischen wollen oder müssen, sehen Sie sich noch einmal den Abschnitt über Ressourcen in Kapitel 3 an.

Strings in Ressourcen verschieben

Mit zunehmender Erfahrung als Programmierer werden Sie vielleicht Kurzverfahren benutzen, um Projekte möglichst schnell kompilieren und starten zu können. Wenn Sie beispielsweise vergessen, Strings in Ressourcen abzulegen, müssen Sie das nachträglich erledigen. Nachfolgend werde ich Ihnen daher schrittweise zeigen, wie Sie Zeichenketten mit den integrierten Werkzeugen in Ressourcen überführen können.

Der lange Weg

Sie können Strings wie nachfolgend dargestellt in eine Ressource extrahieren:

1. Erstellen Sie eine neue String-Ressource.

2. Kopieren Sie ihren Namen.

3. Ersetzen Sie den String-Wert in Ihrem Layout durch den Ressourcenbezeichner.

Das bedeutet zwar keinen riesigen Aufwand, dauert bei einem durchschnittlichen Entwickler aber wohl etwa 30 bis 45 Sekunden.

Der kurze Weg

Sie können diese Aufgabe auch in weniger als 15 Sekunden bewältigen. Wenn Sie das 30 Mal am Tag machen (an einem Achtstundentag möglich), lassen sich allein beim Kopieren und Einfügen 15 Minuten Zeit sparen. Das macht fünf Stunden monatlich!

Führen Sie diese Schritte aus:

1. Wenn Eclipse nicht läuft, starten Sie es nun und öffnen Sie die Datei activity_ main.xml im Ordner layout.

2. Suchen Sie das folgende Codesegment in der Datei:

<Button

android:id="@+id/toggleButton"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:text="Lautlosmodus umschalten"

/>

3. Markieren Sie die Zeile "Lautlosmodus umschalten".

4. Drücken Sie die Taste [1] unter Windows zusammen mit [Strg] oder beim Mac zusammen mit [Mac Bef].

Daraufhin wird ein kleines Menü mit mehreren Optionen angezeigt.

5. Markieren Sie die Option EXTRACT (ANDROID) STRING und betätigen Sie [Enter], sofern die Option nicht direkt durch die Auswahl aufgerufen wird.

Nun wird das Dialogfeld EXTRACT ANDROID STRING angezeigt (Abbildung 6.1). Hier können Sie verschiedene Optionen für die Ressource einstellen.

Abbildung 6.1: Das Dialogfeld EXTRACT ANDROID STRING

6. Da hier keine der angebotenen Funktionen genutzt werden müssen, behalten Sie die Vorgaben bei und klicken Sie die Schaltfläche OK an.

Nun können Sie sehen, dass die Layoutdatei geändert wurde. Der Text "Lautlosmodus umschalten" wurde durch "@string/lautlosmodus_umschalten" ersetzt.

Wenn Sie die Datei strings.xml im Ordner res/values öffnen, finden Sie dort eine neue String-Ressource mit dazu passendem Namen und dem Wert "Lautlosmodus umschalten".

Nun, das ist doch wirklich toll! Wie Sie sehen, können Sie auf diesem Weg eine Menge Zeit sparen, wenn Sie dies 20 bis 30 Mal am Tag machen müssen.

Der Kampf mit den Bildern

Zu einem der größten Probleme im Umgang mit Ressourcen können Bilder werden. Bilder können bei mittlerer Dichte durchaus gut und bei hoher Dichte grässlich aussehen. Hier kommen die Ordner für die verschiedenen Dichten ins Spiel. Die Ordner für »Drawables« für verschiedene Dichten wurden in Kapitel 3 erläutert.

Treppeneffekte und Kompression

Probleme treten besonders häufig in Form von Treppeneffekten und bei der Kompression (beim Ändern der Bildabmessungen) auf. Um derartigen Problemen entgegenzuwirken, entwerfen Sie Ihre Grafiken in hoher Auflösung (zum Beispiel 300 dpi) und im großen Format. Erstellen Sie Ihr Startsymbol beispielsweise mit 250x250 Pixeln. Zwar benötigen Sie für Ihren xhdpi-Ordner nur ein 96x96 Pixel großes Bild (momentan die größten verwendeten Bilder), aber das bedeutet nicht viel, da bereits in wenigen Monaten Geräte mit noch höheren als den heute üblichen Auflösungen auf den Markt kommen können.

Wenn Sie Ihre Symbole allerdings aus wirklich großen Bildern erstellen wollen, sollte der für die Bildbearbeitung eingesetzte Rechner schon entsprechend leistungsfähig sein. (Die vergleichsweise lahmen Netbooks sollten Sie für derartige Zwecke jedenfalls eher nicht verwenden.) Sie sollten mir hier einfach vertrauen und mir glauben, dass sich Ausgangsbilder mit hoher Auflösung viel leichter in die benötigten Formate hinunterrechnen lassen.

Unter dem Herunterrechnen hochauflösender Bilder leidet die Qualität nicht (einmal abgesehen davon, dass natürlich Details verloren gehen), während Bilder beim Hochrechnen schnell matschig und verzerrt wirken. Wenn Sie eine große Datei als Ausgangsbasis verwenden, ist es zudem recht unwahrscheinlich, dass Sie Bilder jemals hochrechnen müssen. Die Grafiken in Ihren Apps werden dann immer knackig aussehen.

Mit Ebenen arbeiten

Wenn Sie Ihre Grafiken mit einem Bildbearbeitungsprogramm erstellen, das Zeichnungsebenen unterstützt, empfehle ich Ihnen dringend, die verschiedenen Elemente Ihrer Grafik auf verschiedenen Ebenen zu erstellen.

Dafür sprechen viele Gründe, wobei ich hier nur die wesentlichen nennen will:

Änderungen: Zuweilen werden Sie etwas an Ihren Grafiken ändern müssen, wie zum Beispiel den Hintergrund, die Schriftart oder auch das Logo. Wenn sich diese Elemente auf verschiedenen Ebenen befinden, lässt sich das leicht ohne Auswirkungen auf die übrige Grafik bewerkstelligen.

Lokalisierung: Erinnern Sie sich noch an das Beispiel weiter vorn in diesem Kapitel, in dem von Strings in verschiedenen Sprachen die Rede war? Das gilt auch für Grafiken. Als Entwickler werden Sie es häufiger mit Grafiken zu tun haben, die stilisierten Text enthalten. Wenn Ihre Anwendung ins Japanische übersetzt wird und Ihre Grafiken stilisierten deutschen oder englischen Text enthalten, werden Sie japanische Versionen dieser Grafiken erstellen und sie in einem dieser Region entsprechenden Ordner ablegen wollen, wie zum Beispiel res/drawable−ja. Die Android-Plattform erkennt, welche Sprach- und Standorteinstellungen verwendet werden (hier Japan). Wenn entsprechende Ressourcenordner (res/drawable−ja, res/values−ja und so weiter) vorhanden sind, nutzt Android sie in der Anwendung. Dazu sollte abschließend aber noch erwähnt werden, dass es immer einfacher ist, Text auch in Textressourcen und Bilder in Bildressourcen zu speichern. Textressourcen lassen sich leichter übersetzen als Bilder, für die bei jeder zusätzlichen Sprache neue Kopien der Bilder erstellt werden müssen.

Nicht nur das Bildbearbeitungsprogramm, sondern auch das verwendete Dateiformat muss Ebenen unterstützen. Daher müssen Sie Ihre Ausgangsbilder dann auch in einem entsprechenden Format (beispielsweise das eigene Dateiformat des Grafikprogramms) speichern und aufbewahren.

Globalisierung von Apps mit Ressourcen

Die Android-Plattform konnte laut ZDNet in den USA im ersten Quartal 2010 die Marktanteile des Apple iPhone übertreffen und lag nur noch hinter dem Blackberry von Research In Motion. Mittlerweile werden weltweit neue Smartphones, Tablets, Multimedia- und Web-TV-Boxen zum Anschluss an Fernseher auf Android-Basis entwickelt und angeboten. Und das bedeutet einfach, dass immer mehr potenzielle Nutzer Ihrer Apps hinzukommen.

Das bedeutet für Sie als Entwickler, dass es sich bei Android um einen riesigen Markt mit schier unüberschaubaren Gelegenheiten handelt, die nur darauf warten, genutzt zu werden. Die potenziellen Chancen sind höchst verlockend; um sie aber wirklich nutzen zu können, müssen Sie verstehen, was Ressourcen sind und wie sie zur Benutzerfreundlichkeit Ihrer Apps beitragen können. Ein Benutzer in Deutschland kann beispielsweise Ihre App nutzen, wenn sie (mit oder ohne Verwendung von Ressourcen) für ein deutsches Publikum geschrieben wurde. Wenn Sie jedoch all Ihre String-Werte fest als Konstanten im Quelltext Ihrer Steuerelemente und Aktivitäten angegeben haben und dann eine chinesische Version herausgeben wollen, müssten Sie Ihre App so umschreiben, dass sie Ressourcen nutzt. Wenn Ressourcen verwendet werden, können Sie Ihre Strings und Bildelemente von einem mit der fremden Sprache Vertrauten anpassen lassen.

In Ressourcen können Sie für Menschen lesbare Zeichenketten, Bilder und Layouts auslagern und in Ihren Apps darauf verweisen. Es können verschiedene Ressourcenordner für unterschiedliche Bildschirmgrößen, Sprachen (Strings und Drawables) und Layoutoptionen (Hoch- und Querformat) erstellt werden. Letztere sind von Bedeutung, wenn Benutzer ihren Bildschirm um 90 Grad drehen.

Wenn Ihre Apps weltweit auf möglichst vielen Android-Geräten nutzbar sein sollen, werden Sie immer mit Ressourcen arbeiten wollen. Beispielsweise empfehle ich generell die Auslagerung Ihrer Strings in die Datei strings.xml, weil sich bestimmt irgendwie, irgendwo, irgendwann jemand aus einem anderen Land für Ihre App interessieren und sie sich in einer anderen Sprache wünschen wird. Um Ihre Anwendung in eine andere Sprache zu überführen, müssen Sie dann nur die Datei strings.xml von jemandem übersetzen lassen, der mit der Zielsprache vertraut ist, und verschiedene values-Ordner für die Werte der entsprechenden Region erstellen. Die echte Schwerarbeit wird Ihnen von Android abgenommen. Wenn sich ein Benutzer beispielsweise in China befindet und den chinesischen Zeichensatz verwendet, sucht Android nach einem values-Ordner namens values−cn, in dem die chinesischen Werte und damit auch die chinesische Version der Datei strings.xml gespeichert werden. Wenn Android keinen derartigen Ordner findet, verwendet die Plattform standardmäßig den Ordner values, der die deutsche (oder vielleicht auch englische) Version der Datei strings.xml enthält. (Mehr über Strings erfahren Sie im Abschnitt »Strings in Ressourcen verschieben« weiter vorn in diesem Kapitel.)

Wenn es so weit ist, können Ihre Strings sehr leicht übersetzt und in einem neu erstellten Ordner in der Datei strings.xml gespeichert werden. Dieses Konzept lässt sich leicht auf weitere Sprachen und Geräte bis hin zum Fernseher ausdehnen. Ihr Zielpublikum besteht dann nicht mehr nur aus Benutzern von Mobilgeräten. Sie haben es vielmehr mit Android-Nutzern zu tun und damit, angesichts der bereits verfügbaren und noch kommenden Optionen, mit Milliarden potenzieller Nutzer. Wenn Sie Ressourcen richtig nutzen, können Sie Ihr Angebot sehr viel leichter auf fremde Märkte ausdehnen.

Optimal wäre es sicherlich, wenn Sie für alle zu unterstützenden Sprachen einen Muttersprachler finden könnten, der diese Aufgabe übernimmt. Das kann allerdings schwierig sein. Überlassen Sie doch einfach Google die Übersetzung. Besuchen Sie dazu http://translate.google.com/toolkit und laden Sie dort Ihre Datei strings.xml hoch, um sie automatisch maschinell übersetzen zu lassen. Die Ergebnisse mögen sich zwar weniger elegant als die von Muttersprachlern anhören, eignen sich aber zumindest für den ersten Einstieg.

Anwendungen für verschiedene Regionen zu entwickeln, ist ein umfangreiches Thema. Ausführlichere Informationen zur Lokalisierung finden Sie in der SDK-Dokumentation unter http://developer.android.com/guide/topics/resources/localization.html.

Auch wenn die Entwicklung von Anwendungen, die für den Einsatz in verschiedenen Regionen vorbereitet sind, verlockend sein mag, sollten Sie auch wissen, dass Sie für den Android-Marktplatz angeben können, für welche Region Ihre App gedacht ist. Sie müssen Ihre App nicht für alle Regionen veröffentlichen. Wenn Sie beispielsweise eine App für die Fahrpläne der Busse in Berlin entwickelt haben, dürfte eine chinesische Version kaum sinnvoll sein, sofern Sie nicht gerade chinesische Touristen und deutsche Einwohner bedienen wollen.

7

Umwandlung Ihrer App in ein Widget
für den Startbildschirm

In diesem Kapitel

Wie App-Widgets in Android funktionieren

PendingIntent verstehen

Einen AppWidgetProvider erstellen

Ihr Widget auf dem Startbildschirm platzieren

In allen Bereichen der Anwendungsentwicklung geht es in erster Linie um Benutzerfreundlichkeit. Letztlich werden Benutzer Ihre App nicht verwenden, wenn sie nicht benutzerfreundlich ist.

Sie haben die App LAUTLOSMODUS-UMSCHALTER erstellt und sie funktioniert tadellos und lässt sich leicht benutzen. Leider würde sie aber nach Veröffentlichung auf dem Android-Marktplatz nicht besonders beliebt sein. Warum? Kurz gesagt, muss der Benutzer die App starten und dann eine Schaltfläche anklicken, um das Telefon in den Lautlosmodus umzuschalten. Wenn er für die App keine Verknüpfung auf dem Startbildschirm erstellt hat, versteckt sie sich im Anwendungsstarter unter vielleicht dreißig weiteren Apps. Dann sind einige weitere Schritte erforderlich und der Benutzer muss das Telefon entsperren, zum Anwendungsstarter wechseln, die App dort ausfindig machen, sie starten und schließlich die Schaltfläche LAUTLOSMODUS UMSCHALTEN antippen. Dann kann der Benutzer aber auch gleich die an den meisten Telefonen vorhandenen Tasten zur Lautstärkeregelung benutzen, um es in den Lautlosmodus zu versetzen. Wenn man die Lautstärke herunterregelt, wird das Telefon schließlich in diesen Modus umgeschaltet. Daher ist die Benutzerfreundlichkeit der App nicht gerade berauschend. Aber wie lässt sich diese App benutzerfreundlicher und für Endanwender attraktiver gestalten? Das ist einfach: Wandeln Sie sie in ein Widget für den Startbildschirm um.

In diesem Kapitel werde ich Ihnen zeigen, wie Sie ein Startbildschirm-Widget für Ihre Anwendung erstellen können. Bei App-Widgets handelt es sich normalerweise um kleine Symbole oder sehr kleine Steuerelemente, die sich auf Ihrem Startbildschirm befinden. Über dieses Widget können Benutzer Ihre App verwenden, indem sie einfach ein Symbol (das Startbildschirm-Widget) antippen. Dann werden die Kernfunktionen aufgerufen und der Lautlosmodus wird aktiviert oder deaktiviert. In diesem Kapitel werde ich Ihnen diese Klassen vorstellen:

Intent

BroadcastReceiver

AppWidgetProvider

IntentService

All diese Klassen spielen eine wichtige Rolle in Android und im App-Widget-Framework.

In Android mit App-Widgets arbeiten

In Android sind Widgets für den Startbildschirm (oder App-Widgets) spezielle Views, die auf dem Startbildschirm angezeigt werden können. Sie können Benutzereingaben wie Klick-Ereignisse entgegennehmen und sich selbst regelmäßig aktualisieren.

App-Widgets lassen sich bei älteren Android-Versionen zum Startbildschirm (Home-Bildschirm) hinzufügen, wenn Sie diesen an einer freien Stelle länger drücken und im dann angezeigten Menü die Option WIDGETS wählen. Daraufhin wird eine Liste der Widgets angezeigt, die Sie in ihren verschiedenen Varianten zu den Seiten des Startbildschirms hinzufügen können.

Bei den aktuellen Android-Versionen können Sie auf den Anwendungsseiten am oberen Bildschirmrand zwischen APPS (oder auch MENÜ, wie in Abbildung 7.1) und WIDGETS umschalten. Dann werden Ihnen die Seiten mit den verfügbaren Widgets angezeigt, die Sie auf die Seiten des Startbildschirms ziehen können.

Abbildung 7.1: Eine der Seiten mit Widgets, die Sie auf Ihrem Startbildschirm installieren können

Wenn Sie die Symboldarstellung eines Widgets antippen und halten, können Sie es auf den Startbildschirm ziehen, wobei die Seiten gewechselt werden, wenn Sie das gezogene Symbol zum jeweiligen Bildschirmrand ziehen. Abbildung 7.2 zeigt das Widget-Symbol noch während des Ziehvorgangs.

Abbildung 7.2: Hier wird das Widget der Analoguhr auf einen der Startbildschirme gezogen.

Abbildung 7.1 und Abbildung 7.2 deuten dabei noch vage auf eine Möglichkeit hin, die Widgets unter aktuellen Android-Versionen bereitstellen. Sie können Widgets nämlich in unterschiedlichen Größen und sogar in variabler Größe erstellen. Abbildung 7.3 zeigt so beispielsweise eine Bilderrahmen-App, die sich über die dargestellten Anfasser in die gewünschte Größe ziehen lässt.

Abbildung 7.3: Dieses Bilderrahmen-Widget kann über Anfasser in die gewünschte Größe gezogen werden.

So viel zunächst einmal über einige der Möglichkeiten im Zusammenhang mit Widgets.

Um die App LAUTLOSMODUS-UMSCHALTER benutzerfreundlicher zu machen, werden wir Ihnen zeigen, wie Sie prinzipiell ein Widget erstellen können, das Benutzer zu ihrem Startbildschirm hinzufügen können. Das Widget können Benutzer dann antippen, um den Klingelmodus umzuschalten, ohne die Anwendung dazu öffnen zu müssen. Das Widget ändert dabei auch sein Aussehen, um den Benutzer darüber zu informieren, in welchem Status sich das Telefon befindet (Abbildung 7.4).

Abbildung 7.4: Die beiden Zustände des zu erstellenden App-Widgets

Arbeiten mit RemoteView

Beim Umgang mit Android sollten Sie nicht vergessen, dass es auf dem Linux-Kernel 2.6 basiert. Linux hat seine eigenen, speziellen Vorstellungen von gewissen Sicherheitsaspekten, die auch für die Android-Plattform gelten. Beispielsweise konzentriert sich das Android-Sicherheitsmodell auf den Linux-Benutzer, Dateien und geschützte Prozesse.

Jeder Anwendung wird in Android (üblicherweise) eine eindeutige Benutzerkennung zugeordnet. Alle Prozesse werden für einen bestimmten Benutzer ausgeführt. Dadurch wird verhindert, dass eine Anwendung die Dateien einer anderen ändern kann, was dazu führen könnte, dass böswillige Entwickler Code in andere Apps einpflanzen.

Da es sich beim Startbildschirm eigentlich um eine Anwendung handelt, die von einem speziellen Benutzer (dem Android-System) gestartet wurde, wäre es aus Sicherheitsgründen nicht zulässig, wenn Sie als Entwickler direkt den ausgeführten Code des Startbildschirms ändern würden. Um aber trotzdem Möglichkeiten zum Zugriff auf den Startbildschirm und zur Änderung bestimmter Bereiche durch Anwendungen bereitzustellen, haben die Android-Entwickler die RemoteView-Architektur implementiert. Dabei kann Code innerhalb Ihrer Anwendung isoliert von der Startbildschirm-Anwendung ausgeführt werden, dem es allerdings erlaubt ist, das Aussehen der eigenen Steuerelemente zu aktualisieren. Letztlich können Sie zwar Widgets erstellen, innerhalb der Startbildschirm-Anwendung kann aber dennoch kein beliebiger Code ausgeführt werden. Der Code Ihres App-Widgets wird vollständig innerhalb Ihrer Anwendung ausgeführt.

Stellen Sie sich die folgende Situation vor: Der Benutzer tippt das App-Widget auf dem Startbildschirm an (hier ein auf dem Startbildschirm hinzugefügtes Symbol). Diese Aktion löst eine Anforderung zur Änderung des Klingelmodus aus, die sich an Ihre Anwendung richtet. Android leitet diese Anforderung an Ihre Anwendung weiter, die sie dann verarbeitet. Während der Verarbeitung dieser Anforderung fordert Ihre Anwendung die Android-Plattform zur Änderung des Klingelmodus und zur Aktualisierung des App-Widgets auf dem Startbildschirm auf, bei der ein neues Bild anzeigen soll, dass sich der Klingelmodus geändert hat. Nichts von diesem Code wird in der Startbildschirm-Anwendung, sondern entfernt (remote) in Ihrer App ausgeführt, wobei Android dafür sorgt, dass die Systemnachrichten an die richtige Anwendung weitergeleitet werden.

Bei diesen »ferngesteuerten Steuerelementen« sorgen innovative Techniken für so etwas wie Zaubertricks. Mit der Klasse RemoteView können Ihre Anwendungen auf der Android-Plattform in einem getrennten Prozess den Startbildschirm um von Programmen ferngesteuerte »Benutzeroberflächen« erweitern. Beim App-Widget-Code handelt es sich eigentlich nicht um Aktivitäten wie in den bisherigen Kapiteln, sondern um eine Implementierung von AppWidgetProvider. Wie im letzten Beispiel erwähnt wurde, sorgt Android dafür, dass Systemnachrichten an die entsprechenden Anwendungen weitergeleitet werden. Wenn Android eine Nachricht vom Startbildschirm an Ihre Anwendung weiterreicht, muss diese in Ihrer Implementierung der Klasse AppWidgetProvider verarbeitet werden.

AppWidgetProvider nutzen

Die Klasse AppWidgetProvider ermöglicht Entwicklern die programmgesteuerte Kommunikation mit dem App-Widget auf dem Startbildschirm. Wenn Benutzer über das App-Widget mit Ihrer Anwendung interagieren, werden dieser über Broadcast-Ereignisse Nachrichten vom Startbildschirm übermittelt. Auf diese Ereignisse kann Ihre App reagieren und das App-Widget aktualisieren, aktivieren, deaktivieren oder auch löschen. Durch Bereitstellung alternativer Views können Sie auch das Aussehen des App-Widgets auf dem Startbildschirm ändern. Da die entsprechenden Steuerelemente auf dem Startbildschirm und nicht innerhalb der gerade ausgeführten Anwendung angezeigt werden, müssen Sie RemoteView zur Aktualisierung des Layouts auf dem Startbildschirm verwenden. Für die logischen Abläufe der Geschehnisse sorgt eine Implementierung von AppWidgetProvider.

Sie können sich das App-Widget-Framework als einen Dolmetscher in einer Unterhaltung zwischen zwei Personen vorstellen. Sie wollen mit jemandem reden, der Italienisch spricht, beherrschen selbst diese Sprache aber nicht. Wie können Sie sich mit ihm verständigen? Sie müssen sich einen Dolmetscher suchen. Der Dolmetscher verarbeitet Ihre Eingaben, übersetzt sie in Italienisch und leitet sie an den italienischen Muttersprachler weiter. So arbeitet auch das App-Widget-Framework. Es arbeitet für Sie als Dolmetscher.

Wenn der italienische Muttersprachler (hier der Startbildschirm) Sie über gewisse Ereignisse informieren muss (etwa das Anklicken einer Schaltfläche), übersetzt der Dolmetscher (das App-Widget-Framework und das Android-System) dies in eine für Sie verständliche Nachricht (eine bestimmte Schaltfläche wurde angetippt). Dann können Sie reagieren und die erforderlichen Maßnahmen veranlassen (etwa die Hintergrundfarbe des App-Widgets in Lindgrün ändern). Der Dolmetscher (das App-Widget-Framework) informiert daraufhin den italienischen Muttersprachler über die zu ergreifenden Maßnahmen (den Startbildschirm über das Android-System). Letztlich sorgt die Startbildschirm-Anwendung dafür, dass Grün als Hintergrundfarbe für das Steuerelement verwendet wird.

App-Widgets können nur Fingerzeig-Ereignisse als Eingaben entgegennehmen. Andere einfache Eingabe-Widgets (wie Texteingabefelder, Dropdown-Listen) lassen sich bei der Arbeit mit App-Widgets nicht nutzen.

Mit PendingIntent arbeiten

Wenn Benutzer mit Ihrer Anwendung interagieren müssen, erfolgt die Kommunikation wie zuvor beschrieben mit Nachrichten über das Android-System. Daher erfährt Ihre App nicht sofort, wenn ein Benutzer das App-Widget antippt. Das bedeutet jedoch nicht, dass Sie nicht über die bei Ihrem App-Widget eintretenden Klick-Ereignisse informiert werden können. Die Kommunikation muss lediglich ein wenig anders abgewickelt werden.

Durch den Einsatz der Klasse PendingIntent im Android-Framework können Klick-Ereignisse bei App-Widgets Anweisungen über durchzuführende Aktionen enthalten. Bei diesen »noch unerledigten« oder »schwebenden« (pending) Intents handelt es sich um eine Implementierung der Klasse Intent in Android, die im folgenden Abschnitt ausführlicher erläutert wird.

Das Intent-System von Android verstehen

Ein Intent-Objekt ist unter Android eine Systemnachricht, die Android zu einer bestimmten Aktion veranlassen soll. Wenn Sie eine elektrische Lichtquelle über einen Schalter einschalten wollen, besteht die Aktion Ihres Intents im Einschalten des Lichts, wozu Sie den Schalter in die An-Position bringen. In Android entspricht diese Aktion dem Erzeugen einer Instanz der Intent-Klasse mit einer zugehörigen Aktion, die das Licht einschalten soll:

Intent turnLightOn = new Intent("LICHT_EINSCHALTEN");

Dieser Intent würde (wie in Kapitel 1 beschrieben) an das Android-Nachrichtensystem weitergeleitet und die entsprechende Aktivität würde den Intent verarbeiten. (Wenn mehrere Aktivitäten antworten, lässt Android den Benutzer auswählen, welche die Arbeit erledigen soll.) Im wahren Leben wird durch Umlegen des Schalters in die An-Position jedoch eine elektrische Verbindung hergestellt, was dazu führt, dass die Lichtquelle leuchtet. In Android müssen Sie Code für eine Aktivität schreiben, der die gewünschten Aktionen veranlasst. Diese Aktivität (nennen wir sie TurnLightOnActivity) reagiert auf den Intent turnLightOn. Wenn Sie mit einem App-Widget arbeiten, muss der Intent von einem BroadcastReceiver verarbeitet werden. In AppWidgetProvider gibt es eine Instanz von BroadcastReceiver mit ein paar zusätzlichen Extras, die das Einbinden des App-Widget-Frameworks weitgehend für Sie erledigt. Das Objekt BroadcastReceiver ist für den Empfang übertragener Nachrichten verantwortlich.

AppWidgetProvider verarbeitet den Intent vom Startbildschirm und sorgt für das in Ihrem eigenen Code in AppWidgetProvider festgelegte Ergebnis. AppWidgetProvider weiß jedoch nicht mit beliebigen Intents umzugehen. Um Eingabeereignisse von Ihrem App-Widget empfangen zu können, müssen Sie PendingIntent verwenden.

Um verstehen zu können, was ein PendingIntent ist, müssen Sie die Basisklasse Intent wirklich begriffen haben. PendingIntent enthält ein Kind-Objekt Intent. Auf hoher Ebene verhalten sich zur Verarbeitung anstehende (pending) Intents einfach wie normale Intents. Bei einem Intent handelt es sich um eine Nachricht, die höchst unterschiedliche Daten transportieren kann, die wiederum eine auszuführende Operation beschreiben. Intents können an eine bestimmte Aktivität oder an eine allgemeine Empfängerkategorie gerichtet sein, die für diese BroadcastReceiver (zu denen, wie Sie wissen, AppWidgetProvider zählt) eine Art Rundruf darstellen. Das aus Intent, Activity und BroadcastReceiver bestehende System ähnelt der Nachrichtenbus-Architektur, bei der eine Nachricht über einen Bus transportiert wird und ein (oder mehrere) Endpunkte am Bus nur genau dann darauf reagieren, wenn sie wissen, wie sie zu antworten haben. Wenn ein Endpunkt nicht weiß, wie er auf eine Nachricht reagieren soll oder die Nachricht nicht an ihn gerichtet ist, wird sie ignoriert.

Ein Intent lässt sich auf verschiedene Weise ins Nachrichtenbus-System übertragen:

Start einer weiteren Aktivität: Verwenden Sie dazu den Aufruf startActivity(), der ein Intent-Objekt als Parameter akzeptiert.

Nachrichten an interessierte BroadcastReceiver-Komponenten: Verwenden Sie den Aufruf sendBroadcast(), der ebenfalls einen Intent als Parameter übernimmt.

Kommunikation mit einem Hintergrunddienst: Nutzen Sie die Aufrufe startService() oder bindService(), die beide Intents als Parameter übernehmen.

Eine Aktivität können Sie sich als Verbindung zwischen verschiedenen Komponenten der Anwendung vorstellen, weil sie Mechanismen der späten Bindung zur Verfügung stellt, die die Kommunikation in Anwendungen und zwischen ihnen ermöglicht.

Intent-Daten verstehen

Die primären Daten eines Intents sind:

Aktion: Die durchzuführende allgemeine Aktion. Zu den gebräuchlicheren Aktionen zählen ACTION_VIEW, ACTION_EDIT und ACTION_MAIN. Sie können wahlweise auch selbst Aktionen definieren.

Daten: Die zu verarbeitenden Daten, wie zum Beispiel ein Datensatz aus einer Datenbank oder die Kennung einer zu öffnenden Ressource (etwa die URL einer Website).

Tabelle 7.1 führt einige Aktionen und Datenparameter für Intent-Objekte und deren einfache Datenstrukturen auf.

Intents können noch eine ganze Reihe anderer Daten übertragen, wie beispielsweise die folgenden:

category: Übergibt zusätzliche Informationen zur auszuführenden Aktion. Wenn beispielsweise CATEGORY_LAUNCHER angegeben wird, bedeutet dies, dass die Anwendung im Anwendungsstarter auf oberster Stufe angezeigt werden soll. Eine weitere Option ist CATEGORY_ALTERNATIVE, über die Benutzern bei bestimmten Daten alternative Aktionen angeboten werden können.

type: Legt einen bestimmten Typ (MIME-Typ) für Intent-Daten fest. Beispielsweise können Sie audio/mpeg als Typ angeben. Das Android-System weiß dann, dass Sie mit einer

Aktion | Daten | Ergebnis
ACTION_VIEW | tel:123 | Zeigt das Wählfeld mit der angegebenen Nummer (123) an
ACTION_DIAL | content://contacts/people/1 | Zeigt das Wählfeld mit der Nummer des Kontakts mit der Kennung 1 an
ACTION_EDIT | content://contacts/people/1 | Ermöglicht die Bearbeitung der Daten der Person mit der Kennung 1
ACTION_VIEW | http://www.beispiel.org | Zeigt die angegebene Webseite an
ACTION_VIEW | content://contacts/people | Zeigt eine Liste mit den Personen für alle gespeicherten Kontakte an

Tabelle 7.1:  Beispiele für Intent-Daten

MP3-Datei arbeiten. Normalerweise wird der Typ über die Daten selbst bestimmt. Mit der entsprechenden Angabe können Sie aber die Typermittlung übergehen und den Typ ausdrücklich im Intent vorgeben.

component: Legt ausdrücklich den Komponentennamen der Klasse fest, die vom Intent verarbeitet werden soll. Normalerweise wird durch Untersuchung anderer Informationen (action, data/type und category) im Intent ermittelt, welche Komponenten es gibt und welche vom Intent verarbeitet werden können. Wenn dieses Attribut gesetzt ist, werden die Prüfungen umgangen und die angegebene Komponente wird wie festgelegt verwendet. Wahrscheinlich handelt es sich hierbei um den häufigsten Anwendungsfall in Ihren Apps. Sie können eine andere Aktivität als Komponente angeben und so dafür sorgen, dass Android diese spezifische Klasse nutzt.

extras: Ein Paket zusätzlicher Daten unter Verwendung von Schlüsseln, das für die empfangende Komponente bereitgestellt werden kann. Wenn Sie beispielsweise eine E-Mail-Adresse übertragen müssen, können Sie das extras-Paket zur Übergabe des Textes, des Betreffs und anderer E-Mail-Bestandteile verwenden.

Intents auswerten

Es gibt zwei Möglichkeiten zur Auswertung von Intents durch das Android-System:

Explizit: Für den Intent wurde explizit eine Komponente oder die genaue Klasse angegeben, von denen die Daten im Intent verarbeitet werden. (Hierbei handelt es sich wahrscheinlich wieder um die häufigste Vorgehensweise im Umgang mit Intents.) Weil es andere Möglichkeiten zum Starten anderer Aktivitäten innerhalb einer Anwendung gibt, enthalten derartige Intents häufig keine anderen Daten. Weiter hinten in diesem Kapitel werde ich Ihnen zeigen, wie Sie explizite Intents nutzen können.

Implizit: Für den Intent wurde keine Komponente oder Klasse festgelegt. Der Intent muss für das Android-System über die vorhandenen Daten ausreichend Informationen zur durchzuführenden Aktion bereitstellen, damit es feststellen kann, welche verfügbaren Komponenten den Intent verarbeiten können. Dabei wird manchmal von Adressen und Nutzdaten gesprochen.

Ein Beispiel wäre die Einrichtung eines E-Mail-Intents mit entsprechenden Feldern (An, CC, Betreff und Text) und einem E-Mail-MIME-Typ. Android interpretiert die Daten dann als E-Mail und lässt den Benutzer des Geräts wählen, mit welcher Anwendung der Intent verarbeitet werden soll. Zu den Alternativen zählen hier Gmail, Exchange und/oder ein POP-E-Mail-Konto, die auf dem Gerät aktiviert sind. Dann kann der Benutzer festlegen, welche App für die E-Mail verwendet werden soll. Die Funktion, mit der Android möglicherweise zu den jeweiligen Intents passende Apps identifiziert, wird Intent-Resolution genannt.

PendingIntent nutzen

Im Kern handelt es sich bei PendingIntent zwar um einen Intent, allerdings mit einem funktionalen Paradigmenwechsel. Ein PendingIntent wird von Ihrer Anwendung erzeugt und einer völlig anderen Anwendung übergeben. Die andere Anwendung darf dann die angegebene Operation so ausführen, als wenn sie Ihre eigene Anwendung wäre. Gewissermaßen wird die andere Anwendung also im Auftrag Ihrer App tätig. Wenn die andere Anwendung mit der ihr übertragenen Arbeit beginnt, führt sie PendingIntent aus und beauftragt das Android-Nachrichtensystem damit, Ihre Anwendung darüber zu informieren, damit sie die notwendige Arbeit verrichten kann.

Für unsere Zwecke werde ich PendingIntent.getBroadcast() aufrufen, um eine PendingIntent-Instanz abzurufen. Der Aufruf gibt einen PendingIntent zurück, der für systemweite Rundrufe genutzt werden kann. Er hat vier Parameter:

Context: Der Kontext, in dem dieser PendingIntent den Broadcast ausführen soll.

RequestCode: Der private Anforderungscode für den Sender. Da dieser Parameter aktuell nicht genutzt wird, wird hier der Wert 0 übergeben.

Intent: Der zu übertragende Intent.

Flags: Eine Reihe von Werten zur Steuerung des Intents nach seinem Start. Da dieser Parameter aktuell nicht genutzt wird, wird hier der Wert 0 übergeben.

Moment, hier wird doch ein Intent und ein PendingIntent verwendet. Warum? Das Intent-Objekt wird in einen PendingIntent eingebettet, weil dieser für die Kommunikation zwischen Prozessen verwendet wird. Wird der PendingIntent abgesandt, wird die tatsächlich zu verrichtende Arbeit in das Intent-Kindobjekt eingebettet.

Hoppla, das sind eine Menge Informationen! Da Sie nun die Grundlagen des Android-Intent-Systems kennen, wird es Zeit, die zentralen Bestandteile der Anwendung im App-Widget zu implementieren.

Vermeiden Sie den gefürchteten ANR-Fehler

Da die komplette, in AppWidgetProvider verrichtete Arbeit im Haupt-Thread der Bedienschnittstelle stattfindet, müssen alle Aufgaben möglichst schnell verarbeitet werden. Wenn Ihr AppWidgetProvider für seine Reaktion zu viel Zeit benötigt, hält Ihr Code den Thread der Bedienoberfläche auf und sorgt dafür, dass ein Dialogfeld mit einem ANR-Fehler (Application Not Responding) angezeigt wird, weil das Android-System glaubt, die App sei abgestürzt und würde nicht mehr reagieren. Ein Beispiel wäre die Netzwerkkommunikation und das Herunterladen von Statusaktualisierungen von einem Dienst wie Twitter. Wenn der Download zu lange dauert, meldet Android einen ANR-Fehler und informiert den Benutzer darüber, dass das App-Widget nicht reagiert. Dann kann dieser das Schließen der Anwendung erzwingen.

Eine Möglichkeit zur Vermeidung von ANR-Fehlern besteht in der Implementierung eines Dienstes innerhalb von AppWidgetProvider, der seine Arbeit in einem Hintergrund-Task verrichtet. Der in den folgenden Abschnitten implementierte IntentService hilft Ihnen, ANR-Fehler zu vermeiden, und sorgt dafür, dass das Widget ausgesprochen schnell bleibt.

Das Widget für den Startbildschirm erstellen

Bei der Interaktion mit einem App-Widget passiert eine Menge. Das Android-Nachrichtensystem ist für den Prozess der Übertragung von Nachrichten zwischen dem App-Widget auf dem Startbildschirm und Ihrer Anwendung, die Klasse PendingIntent und den AppWidgetProvider zuständig. In diesem Abschnitt werden Sie die Komponenten erstellen, mit denen Sie Ihr erstes App-Widget auf dem Startbildschirm zum Laufen bekommen können.

AppWidgetProvider implementieren

AppWidgetProvider lässt sich recht leicht implementieren. Starten Sie Eclipse und öffnen Sie die Anwendung LAUTLOSMODUS-UMSCHALTER. Gehen Sie dann so vor:

1. Um zum Paket com.dummies.android.lautlosmodus eine neue Klasse hinzuzufügen, klicken Sie dieses im Ordner src/ mit der rechten Maustaste an und wählen im Kontextmenü NEW:CLASS.

Daraufhin wird das Dialogfeld NEW JAVA CLASS angezeigt (Abbildung 7.5).

2. Im Feld NAME tragen Sie AppWidget ein.

3. In das Feld SUPERCLASS geben Sie als Namen android.appWidget.AppWidgetProvider ein.

4. Klicken Sie abschließend FINISH an.

Damit wird eine neue Klasse zum markierten Paket hinzugefügt. Der Code mit dem angegebenen Namen in der Datei AppWidget.java sollte nun angezeigt werden.

Abbildung 7.5: Das Dialogfeld NEW JAVA CLASS

AppWidgetProvider übernimmt alle Reaktionen auf Ereignisse von RemoteView. Aber wie funktioniert das? Aus der Android-Dokumentation können Sie erfahren, dass es sich bei AppWidgetProvider um eine direkte Unterklasse von BroadcastReceiver handelt. Auf oberer Ebene handelt es sich bei BroadcastReceiver um eine Komponente, die Broadcast-Nachrichten vom Android-System empfangen kann. Wenn ein Benutzer ein Steuerelement der RemoteView (zum Beispiel eine Schaltfläche) auf dem Startbildschirm antippt, sendet das Android-System eine Nachricht, die den Empfänger darüber informiert, dass das Steuerelement angeklickt wurde. Die Nachricht wird zu einem bestimmten Ziel im Android-System übertragen. Danach kann AppWidgetProvider diese Nachricht verarbeiten.

Beachten Sie, dass diese Nachrichten systemweit übertragen werden. Wenn die Nutzdaten der Nachricht und die Zielangaben unbestimmt genug sind, könnten möglicherweise verschiedene BroadcastReceiver-Objekte die Nachricht verarbeiten. Der in diesem Abschnitt erstellte AppWidgetProvider wird aber nur an ein bestimmtes Ziel adressiert sein. Wenn Sie einen Raum mit Unternehmern betreten und laut fragen würden, ob sie für Sie arbeiten wollen, würden vielleicht alle antworten. Das ist aber bei einer derart unbestimmten Frage (vage Adresse und Nutzdaten) ja auch kein Wunder. Wenn Sie in derselben Gruppe aber nach einem Elektroniker namens Robert Schmitz fragen würden, sollte Ihnen nur dieser antworten (natürlich auch nur, sofern er überhaupt anwesend ist). Hier wurde die Nachricht gezielt adressiert und enthielt zudem noch ein paar spezifische Nutzdaten.

Kommunikation mit dem App-Widget

Anfangs enthält Ihre Klasse AppWidgetProvider noch keinen Code und ist nur eine leere Hülle. Damit sie etwas machen kann, müssen Sie ein wenig Code hinzufügen, der auf den zu Ihrem AppWidgetProvider übertragenen Intent (die Nachricht) reagiert. Geben Sie in der gerade erstellten Quelltextdatei den in Listing 7.1 dargestellten Code in den Editor ein. (Hinweis: Die Klasse wurde hier AppWidget genannt. Wenn Sie einen anderen Namen verwendet haben, müssen Sie die entsprechende Zeile ändern.)

public class AppWidget extends AppWidgetProvider { →1

@Override

public void onReceive(Context ctxt, Intent intent) { →4

if (intent.getAction()==null) { →5

// Etwas tun

} else {

super.onReceive(ctxt, intent); →10

}

}

@Override

public void onUpdate(Context context, AppWidgetManager

appWidgetManager, int[] appWidgetIds) { →15

// Etwas tun

}

}

Listing 7.1: Der anfängliche Code des App-Widgets

Es folgt eine kurze Erläuterung der einzelnen Zeilen:

→1: Diese Codezeile informiert Android darüber, dass die hier deklarierte Klasse von AppWidgetProvider abstammt.

→4: Diese Zeile überschreibt die Methode onReceive(), um erkennen zu können, wenn ein neuer Intent von RemoteView empfangen wird. Dieser könnte von einem Benutzer ausgelöst worden sein, der ein Steuerelement angetippt und damit eine Schaltfläche angeklickt hat. Das Intent-Objekt ist in PendingIntent enthalten, das die Anforderung ausgelöst hat.

→5: Intent-Objekte können unterschiedliche Datenelemente enthalten. Eines davon ist die Aktion. In dieser Zeile prüfe ich, ob der Intent eine Aktion enthält. Wenn das nicht der Fall ist, wird der Intent ausgelöst. Das mag zwar ein wenig verdreht klingen, Sie werden in den folgenden Abschnitten aber darüber noch ein wenig mehr erfahren.

→10: Da hier nichts weiter mit dem Intent gemacht werden muss, delegiert diese Zeile die Arbeit an die Superklasse. (Der Intent enthält eine Aktion, während hier ein Intent ohne Aktion erwartet wird.) Das würde geschehen, wenn das App-Widget sich selbst nach der in den Widget-Metadaten (diese werden im Abschnitt »Arbeiten mit den Metadaten des App-Widgets« weiter hinten im Kapitel erläutert) festgelegten Zeitspanne aktualisiert. Dann wird eine der vielen vordefinierten Methoden zur Aktivierung, Deaktivierung, zum Starten, Stoppen oder Aktualisieren des App-Widgets aufgerufen, wie es in Zeile 15 geschieht.

→15: Die Methode onUpdate() wird vom Android-Framework in regelmäßigen Intervallen aufgerufen, deren Dauer Sie in den Widget-Metadaten festlegen können. Wenn das vorgegebene Zeitintervall abgelaufen ist, kann das Android-Framework die View ohne Zutun des Benutzers vorausschauend aktualisieren. Ein gutes Beispiel wäre hier ein Nachrichten-Widget, das sich selbst alle 30 Minuten aktualisiert, um die jeweils letzten Schlagzeilen anzuzeigen. Da dies regelmäßig geschieht, wären keine weiteren Benutzereingriffe erforderlich. Diese Methode sorgt letztlich für die korrekte Einrichtung des Widgets.

Das Layout des App-Widgets erstellen

Damit Android weiß, wie es das App-Widget auf dem Startbildschirm anzeigen soll, benötigt es ein spezielles Layout, das über die Widget-Layoutdatei definiert wird. Eingangs des Kapitels wurden in Abbildung 7.4 zwei Bildschirmfotos vom fertigen App-Widget im Emulator dargestellt. Das Aussehen der Symbole auf dem Startbildschirm wurde über die Widget-Layoutdatei festgelegt. Wenn Sie die Hintergrundfarbe in der Layoutdatei auf Lindgrün setzen würden, wäre der Hintergrund des Widgets auf dem Startbildschirm nicht mehr transparent, sondern lindgrün.

Der lindgrüne Hintergrundbereich entspricht auch der für das App-Widget verfügbaren Bildschirmfläche. Ihr App-Widget kann eine oder mehrere Zellen auf dem Startbildschirm einnehmen. Das hier erstellte App-Widget nutzt nur eine Bildschirmzelle.

Um Ihr Widget-Layout zu erstellen, erzeugen Sie über das Kontextmenü eine neue Android-XML-Layoutdatei im Ordner res/layout. Nennen Sie sie widget.xml.

Listing 7.2 zeigt den Inhalt von widget.xml.

<?xml version="1.0" encoding="utf−8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/

android"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ImageView android:id="@+id/phoneState"

android:layout_height="wrap_content"

android:layout_width="wrap_content"

android:layout_centerInParent="true"

android:src="@drawable/ic_launcher"

android:clickable="true" /> →9

</RelativeLayout>

Listing 7.2:Der Inhalt von widget.xml

Dieses Layout ist Ihnen nicht wirklich neu. Es handelt sich um ein relatives Layout mit einer Kind-View. Damit diese ImageView auf Klicks reagiert, wurde in Listing 7.2 die Eigenschaft clickable in Zeile 9 auf true gesetzt.

Beachten Sie die src-Eigenschaft der ImageView. Sie wurde auf das Symbol der Anwendung gesetzt. Das wird Ihnen vielleicht ein wenig merkwürdig erscheinen. Das wird hier deshalb gemacht, weil Sie beim Erstellen des Layouts die Bilder für den lautlosen und normalen Modus noch nicht erstellt haben. Sie benötigen beim Erstellen des Layouts aber eine Vorschau, um zu wissen, wie die View aussehen soll. Daher wird hier, um bereits vorab einen gewissen Eindruck vom Aussehen erhalten zu können, @drawable/ic_launcher als Wert für ImageView verwendet. Machen Sie sich über die Verwendung des Anwendungssymbols weiter keine Gedanken. Der Wert für das Symbol wird beim Start des App-Widgets später durch ToggleService auf den des Symbols für den lautlosen oder normalen Modus umgeschaltet. Mehr dazu werden Sie noch weiter hinten in diesem Kapitel erfahren.

In Zeile 8 lauert mit der Anweisung android:src="@drawable/ic_launcher" eine mögliche Stolperfalle. Sie verweist auf das Anwendungssymbol in einer Datei namens ic_launcher.png. Ältere SDK-Versionen (und auch noch das für die amerikanische Auflage benutzte SDK) verwenden hier eine Datei namens icon.png. Dann muss diese Zeile aber auch entsprechend android:src="@drawable/icon" lauten. Quittiert wird die Verwendung des falschen Namens mit gut einem Dutzend Fehlermeldungen, von denen nur No resource found that matches the given name (at ‘src‘ with value ‘@drawable/ic_launcher‘ (»Ressource mit Dateinamen ic_launcher nicht gefunden«) wirklich von Interesse ist. Im Ordner res/drawable−mdpi können Sie beispielsweise nachsehen, welchen Dateinamen die von Ihnen eingesetzte SDK-Version für das App-Symbol verwendet.

Die Symbole sorgen dafür, dass Benutzer den aktuellen Anwendungsstatus erkennen können. Das Symbol phone_state_normal wird angezeigt, wenn sich das Telefon im normalen Klingelmodus befindet. Das Symbol phone_state_silent wird verwendet, wenn sich das Telefon im Lautlosmodus befindet.

Die Dateinamen für die beiden Symbole lauteten in den bisherigen Kapiteln phone_on.png und phone_silent.png. In diesem Kapitel werden etwas andere Symbole verwendet, die Sie zusätzlich in das Projekt einbinden müssen, damit die Beispiele wie dargestellt laufen.

Arbeit in AppWidgetProvider verrichten

Wenn Ihr AppWidgetProvider durch PendingIntent gestartet wurde, müssen im Auftrag der aufrufenden Anwendung (hier die Startbildschirm-Anwendung) gewisse Arbeiten erledigt werden. In den folgenden Abschnitten werden Sie sehen, wie diese zeitkritischen Arbeiten erledigt werden können.

Bevor Sie sich mit dem Code befassen, sollten Sie aber verstehen, wie AppWidgetProvider arbeitet. Aufgrund des Wesens der Remote-Prozesse und weil sie recht ressourcenlastig sein können, wird die Arbeit am besten von einem Hintergrunddienst verrichtet, was hier auch bei der Änderung des Klingelmodus geschehen soll.

IntentService verstehen

In diesem Abschnitt wird erläutert, warum hier ein Hintergrunddienst für eine derart triviale Aufgabe wie die Änderung des Klingelmodus verwendet wird.

Jeder Code, der zu lange ausgeführt wird, ohne dem Android-System zu antworten, kann zu ANR-Fehlern (Application Not Responding) führen. App-Widgets sind besonders anfällig für ANR-Fehler, weil sie Code in Remote-Prozessen ausführen. Dabei arbeiten sie prozessübergreifend und benötigen Zeit für die Einrichtung, Ausführung und Zerstörung der Prozesse. Das Ganze kostet viel Rechenzeit, Speicher und Batterieleistung. Das Android-System überwacht App-Widgets, um zu gewährleisten, dass sie nicht zu lange ausgeführt werden. Wenn sie längere Zeit für die Ausführung benötigen, hängt die aufrufende Anwendung (der Startbildschirm) und das Gerät lässt sich (vorübergehend) nicht mehr benutzen. Daher versucht die Android-Plattform, dafür zu sorgen, dass das Gerät nie länger als allenfalls ein paar Sekunden nicht reagiert.

Da App-Widgets sehr viel Rechenzeit und Speicher beanspruchen, lässt sich sehr schwer beurteilen, ob ein App-Widget einen ANR-Fehler verursachen wird. Sofern das Gerät nicht gerade andere aufwendige Aufgaben ausführt, dürfte unser App-Widget wohl tadellos funktionieren. Wenn es sich jedoch mitten in einer oder mehreren CPU-lastigen Operationen befindet, könnte das App-Widget zu lange brauchen, um zu antworten, und es kommt zu einem ANR-Fehler. Bei diesem unbekannten Prozessorzustand handelt es sich für Ihr App-Widget um eine gefährliche Situation. Um diese Gefahr zu vermeiden, wird die eigentliche Arbeit des App-Widget am besten in einen IntentService verschoben, der beliebig lange für die Erledigung seiner Aufgaben benötigen kann. Davon bleibt die Startbildschirm-Anwendung wiederum unbeeinflusst.

Anders als die meisten lange laufenden Hintergrunddienste nutzt ein IntentService eine Art Prozessorwarteschlange, bei der die Intents unter Verwendung eines Arbeits-Threads verarbeitet werden, und wird beendet, wenn er mit seiner Arbeit fertig ist. Vereinfacht ausgedrückt, erledigt der IntentService die ihm übertragene Arbeit als Hintergrunddienst und wird beendet, wenn es nichts mehr zu tun gibt.

AppWidgetProvider und IntentService implementieren

Fügen Sie im Quelltexteditor in die Klasse AppWidgetProvider den Code aus Listing 7.3 ein.

public class AppWidget extends AppWidgetProvider {

@Override

public void onReceive(Context context, Intent intent) {

if (intent.getAction()==null) {

context.startService(new Intent(context,

ToggleService.cla →6

} else {

super.onReceive(context, intent);

}

}

@Override

public void onUpdate(Context context, AppWidgetManager

appWidgetManager, int[] appWidgetIds) {

context.startService(new Intent(context,

ToggleService.class)); →16

}

public static class ToggleService extends IntentService { →19

public ToggleService() {

super(ToggleService.class.getName()); →22

}

@Override

protected void onHandleIntent(Intent intent) →26

ComponentName me=new ComponentName(this, AppWidget.class); →27

AppWidgetManager mgr=AppWidgetManager.getInstance(this); →28

mgr.updateAppWidget(me, buildUpdate(this)); →29

}

private RemoteViews buildUpdate(Context context) →30

RemoteViews updateViews=new

RemoteViews(context.getPackageName(),R.layout.widget) →32

AudioManager audioManager =

AudioManager)context.getSystemService(Activity.AUDIO_SERVICE);  →34

if(audioManager.getRingerMode() ==

AudioManager.RINGER_MODE_SILENT) {

updateViews.setImageViewResource(R.id.phoneState,

R.drawable.phone_state_normal); →40

audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);

} else {

updateViews.setImageViewResource(R.id.phoneState,

R.drawable.phone_state_silent); →45

audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);

}

Intent i=new Intent(this, AppWidget.class); →49

PendingIntent pi

= PendingIntent.getBroadcast(context, 0, i,0);

updateViews.setOnClickPendingIntent(R.id.phoneState,pi); →54

return updateViews; →56

}

}

}

Listing 7.3: Die vollständige AppWidget-Implementierung

Die folgende Liste erläutert kurz, was die jeweiligen Codeabschnitte machen:

→6: In dieser Zeile wird eine neue Instanz von ToggleService gestartet. Das Objekt context bezieht sich hier auf das Android-Objekt Context, bei dem es sich um eine Schnittstelle zu globalen Informationen über die Anwendung handelt. context wird an die Methodenaufrufe onReceive() und onUpdate() übergeben. Ein neuer Intent wird erzeugt, um dem Android-System mitzuteilen, was geschehen soll. Diese Methode wird vom Benutzer gestartet, wenn er das App-Widget auf dem Startbildschirm antippt.

→16: Diese Zeile führt dieselben Aktionen wie Zeile 6 aus.

→19: Hierbei handelt es sich um eine IntentService-Implementierung. Dieser IntentService führt im Zusammenhang mit der Umschaltung des Klingelmodus dieselben logischen Aufgaben wie Ihre MainActivity aus, allerdings im Rahmen der App-Widget-Struktur. Dabei handelt es sich um eine Implementierung eines Hintergrunddienstes auf der Android-Platform, wie sie gerade beschrieben wurde, und eine in das App-Widget eingebettete statische Klasse.

→22: Diese Methode ruft die Superklasse "com.dummies.android.lautlosmodus .AppWidget$ToggleService" auf. Der Methodenaufruf erfolgt, um Debuggingzwecke für den Thread-Namen zu unterstützen. Wenn Sie diese Zeile weglassen, erhalten Sie einen Compilerfehler, der Sie darüber informiert, dass Sie den Konstruktor der Superklasse explizit aufrufen müssen. Wenn Sie Ihr App-Widget nicht AppWidget genannt haben, sollten Sie den Namen dem von Ihnen verwendeten Klassennamen entsprechend anpassen.

→26: Die Methode onHandleIntent() ist für die Bedienung des dem Dienst übergebenen Intents zuständig. Hier würde es sich dabei um den in den Zeilen 6 und 16 erzeugten Intent handeln. Weil ein expliziter Intent erzeugt wurde (es wurde ein auszuführender Klassenname angegeben), wurden keine zusätzlichen Daten bereitgestellt. Wenn Zeile 26 erreicht wird, müssen Sie daher den Intent nicht weiter nutzen. Sie hätten dem Intent-Objekt aber zusätzliche Informationen übergeben und sie dann dem Parameter dieser Methode entnehmen können. Hier dient das Intent-Objekt lediglich als Bote, der ToggleService dazu auffordert, mit der Verarbeitung zu beginnen.

→27: Ein ComponentName-Objekt wird erzeugt. Es wird mit dem (nachfolgend erläuterten) AppWidgetManager benutzt, um die neuen Inhalte zur Verfügung zu stellen, die über die RemoteView-Instanz zum App-Widget übertragen werden.

→28: Der statische Aufruf AppWidgetManager.getInstance() liefert eine Instanz von AppWidgetManager. Die Klasse AppWidgetManager ist für die Aktualisierung des Zustands des App-Widgets zuständig und liefert weitere Informationen über das installierte App-Widget. Es wird verwendet, um den App-Widget-Status zu aktualisieren.

→29: In dieser Zeile wird das App-Widget durch einen Aufruf von updateAppWidget() aktualisiert. Dieser benötigt neben dem ComponentName-Objekt, das die Aktualisierung erledigt, das RemoteView-Objekt. Das ComponentName-Objekt wird in Zeile 27 erzeugt. Das zur Statusaktualisierung des App-Widgets auf dem Startbildschirm verwendete RemoteView-Objekt ist ein wenig komplizierter und wird als Nächstes erläutert.

→30: Die Definition der Methode buildUpdate(). Sie gibt ein neues RemoteView-Objekt zurück, das in Zeile 29 verwendet wird. Diese Methode enthält die Logik für die auszuführenden Aufgaben und die Aktionen, mit denen anschließend fortgefahren wird.

→32: Hier wird ein RemoteView-Objekt mit dem aktuellen Paketnamen erzeugt und das Layout erstellt, das von dieser Methode zurückgegeben wird. Das Layout (R.layout.widget) finden Sie in Listing 7.3.

→34: Hier wird eine AudioManager-Instanz übernommen und direkt anschließend der Status der Klingel geprüft. Befindet sich das Telefon aktuell im Lautlosmodus, bedeutet dies, dass der Benutzer die Klingel nun in den Normalmodus umschalten will.

→40: Diese Zeile aktualisiert das vorhandene RemoteView-Objekt. Das RemoteView-Objekt ändert das ImageView-Steuerelent R.id.phoneState, für das anschließend das Symbol R.drawable.phone_state_normal (rechts in Abbildung 7.4) verwendet wird.

→45: Die else-Anweisung vor dieser Zeile wird durchlaufen, um das ImageView-Steuerelement so zu aktualisieren, dass es das Symbol R.drawable.phone_state_silent enthält. Das Telefon befand sich hier zuvor nicht im Lautlosmodus, in den der Benutzer jetzt wechseln will.

→49: Hier wird ein Intent-Objekt erzeugt, das bei seiner Initialisierung die Klasse AppWidget starten wird.

Leider können App-Widgets nicht mit einfachen Intents kommunizieren. Zu diesem Zweck muss ein PendingIntent genutzt werden, weil App-Widgets prozessübergreifend kommunizieren müssen. In dieser Zeile wird der PendingIntent erzeugt, der dem App-Widget über den in Zeile 49 erzeugten Kind-Intent seine nächste Aktion übermittelt.

→52: Erzeugt das PendingIntent-Objekt, das dem App-Widget mitteilt, was getan werden soll, wenn ein Benutzer die View anklickt oder antippt.

→54: Richtet den Klick-Listener für die ImageView im App-Widget ein. Da Sie mit einer RemoteView arbeiten, müssen Sie die gesamte Ereignishierarchie in der View neu erstellen. Das liegt daran, dass das App-Widget-Framework die komplette RemoteView durch eine neue ersetzt, die von dieser Methode geliefert wird. Daher müssen Sie der RemoteView noch mitteilen, was geschehen soll, wenn sie auf dem Startbildschirm angetippt/angeklickt wird. Der in Zeile 52 erzeugte PendingIntent teilt dem App-Widget mit, was geschehen soll, wenn jemand die View antippt. Für die entsprechende Einrichtung ist setOnClickPendingIntent() zuständig. Diese Methode nimmt als Parameter die Kennung der angeklickten View (hier ein Symbol) und das Argument pi entgegen, bei dem es sich um den in Zeile 52 erzeugten PendingIntent handelt.

→56: Rückgabe des neu erzeugten RemoteView-Objekts, damit der Aufruf von update AppWidget() in Zeile 29 das App-Widget aktualisieren kann.

Arbeiten mit den Metadaten des App-Widgets

Da Sie nun den Code geschrieben haben, der sich um die Aktualisierung des App-Widgets kümmert, werden Sie sich vielleicht fragen, wie Sie es schaffen, dass das App-Widget nach längerem Drücken des Startbildschirms im Widgets-Menü angezeigt wird. Da Sie dazu nur eine einzige XML-Datei zu Ihrem Projekt hinzufügen müssen, lässt sich das recht einfach erreichen. Diese XML-Datei enthält einige grundlegende Metadaten für das App-Widget, die der Android-Plattform mitteilen, wie das Layout der App-Widget auf dem Startbildschirm aussehen soll. Gehen Sie dazu so vor:

1. Klicken Sie in Ihrem Projekt den Ordner res mit der rechten Maustaste an und wählen Sie im Kontextmenü NEW:FOLDER.

2. Tragen Sie xml in das Feld für den Ordnernamen (FOLDER NAME) ein und klicken Sie FINISH an.

3. Klicken Sie den neuen Ordner res/xml mit der rechten Maustaste an und wählen Sie die Option ANDROID XML FILE.

4. Im Assistenten NEW ANDROID XML FILE tragen Sie im Feld FILE als Dateinamen widget_ provider.xml ein.

5. Unter RESOURCE TYPE wählen Sie im Listenfeld die Option APPWIDGET PROVIDER aus und klicken FINISH an.

6. Nachdem die Datei erstellt wurde, aktivieren Sie den XML-Editor und geben die folgenden Zeilen in die Datei widget_provider.xml ein:

<?xml version="1.0" encoding="utf−8"?>

<appwidget−provider
xmlns:android="http://schemas.android.com/apk/res/android"

android:minWidth="79px"

android:minHeight="79px"

android:updatePeriodMillis="1800000"

android:initialLayout="@layout/widget"

/>

Über die Eigenschaften minWidth und minHeight werden die Minimalabmessungen der View auf dem Startbildschirm festgelegt. Wenn Sie wollen, können Sie auch höhere Werte verwenden, allerdings benötigen Sie für eine Telefon-App wirklich nicht mehr als 79x79, um das jeweils vom Widget angezeigte Symbol unterbringen zu können.

Über die Eigenschaft updatePeriodMillis lässt sich festlegen, wie häufig sich das App-Widget selbst aktualisieren soll. Im Fall der App LAUTLOSMODUS-UMSCHALTER muss das (wenn überhaupt) nur selten geschehen. Daher wird dieser Wert auf 180.000 Millisekunden (30 Minuten) gesetzt. Alle 30 Minuten versucht sich die App selbst dadurch zu aktualisieren, dass sie ein Intent sendet, das die Methode onUpdate()in AppWidgetProvider aufruft.

Die Eigenschaft initialLayout legt fest, wie das App-Widget aussieht, wenn es erstmals zum Startbildschirm hinzugefügt wird und seine eigentliche Arbeit noch nicht aufgenommen hat. Beachten Sie, dass es einige Sekunden (oder länger) dauern kann, bis das App-Widget initialisiert und das RemoteView-Objekt durch Aufruf der Methode onReceive() aktualisiert worden ist.

Zu längeren Verzögerungen kann es beispielsweise kommen, wenn ein App-Widget den Twitter-Status zu aktualisieren versucht. Bei einem langsamen Netzwerk würde initialLayout angezeigt werden, bis Aktualisierungen von Twitter empfangen werden. Wenn Sie vorab bereits wissen, dass das problematisch werden könnte, sollten Sie den Benutzer in initialLayout darüber informieren, dass Daten geladen werden. Dann weiß er beim Start des App-Widgets für den Startbildschirm, was im Hintergrund geschieht. Zu diesem Zweck könnten Sie eine TextView mit der Meldung "Daten werden geladen..." anzeigen, während AppWidgetProvider seine Arbeit verrichtet.

Die neuen Komponenten mit dem Manifest registrieren

Immer wenn Sie eine Aktivität, einen Dienst oder einen BroadcastReceiver (und einige andere Elemente) zu Ihrer Anwendung hinzufügen, müssen Sie sie in der Manifest-Datei der Anwendung registrieren. Das Manifest der Anwendung enthält wesentliche Angaben zur Android-Plattform und insbesondere den Anwendungskomponenten. Die im Manifest der Anwendung nicht registrierten Objekte Activity, Service und BroadcastReceiver werden vom System nicht erkannt und können nicht ausgeführt werden. Wenn Sie das App-Widget jetzt zu Ihrem Startbildschirm hinzufügen, würde es daher abstürzen, weil es sich bei AppWidgetProvider um einen BroadcastReceiver handelt, in dessen Code ein Dienst benutzt wird, der ebenfalls nicht im Manifest registriert ist.

Um AppWidgetProvider und IntentService zur Manifest-Datei Ihrer Anwendung hinzuzufügen, öffnen Sie die Datei AndroidManifest.xml und geben dort den in Listing 7.5 dargestellten Code in die bereits existierende Datei ein. Die fett dargestellten Zeilen werden für die neuen Komponenten neu eingefügt.

<?xml version="1.0" encoding="utf−8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.dummies.android.lautlosmodus"

android:versionCode="1"

android:versionName="1.0">

<application android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:debuggable="true">

<activity android:name=".MainActivity"

android:label="@string/app_name">

<intent−filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent−filter>

</activity>

<receiver android:name=".AppWidget"

android:label="@string/app_name"

android:icon="@drawable/ic_launcher"> →18

<intent−filter>

<action

android:name="android.appwidget.action.APPWIDGET_UPDATE" />  →21

</intent−filter>

<meta−data

android:name="android.appwidget.provider"

android:resource="@xml/widget_provider" /> →25

</receiver>

<service android:name=".AppWidget$ToggleService" /> →27

</application>

<uses−sdk android:minSdkVersion="4" />

</manifest>

Listing 7.5: Die aktualisierte Datei AndroidManifest.xml mit neu registrierten Komponenten

Nachfolgend werden die verschiedenen Abschnitte kurz erläutert:

→18: Bei dieser Zeile handelt es sich um das einleitende Element, das einen BroadcastReceiver als Teil dieser Anwendung registriert. Die Eigenschaft name gibt den Namen des Empfängers an, der hier AppWidget lautet und damit der Datei AppWidget.java in der Anwendung entspricht. Über diese Angaben lassen sich Empfänger identifizieren.

→21: Gibt an, auf welche Art von Intent (basierend auf der Aktion des Intents im Intent-Filter) das App-Widget automatisch reagiert, wenn der spezielle Intent übertragen wird. Über Intent-Filter kann das Android-System erkennen, über welche Arten von Ereignissen Ihre App benachrichtigt werden sollte. Hier ist die Aktion APPWIDGET_UPDATE des Broadcast-Intents für Ihre Anwendung von Bedeutung. Dieses Ereignis wird nach Ablauf der über die Eigenschaft updatePeriodMillis festgelegten Zeitspanne ausgelöst, die in der Datei widget_provider.xml definiert wurde. Zu den weiteren Aktionen zählen enabled, deleted, disabled und weitere.

→25: Gibt den Speicherort der aktuell in Ihre Anwendung integrierten Metadaten an. Android nutzt die Metadaten zur Ermittlung von Vorgaben und Layoutparametern für Ihr App-Widget.

→27: Das <service>-Element registriert AppWidget$ToggleService IntentService für Ihre App. Dabei handelt es sich um den Hintergrunddienst, der die meiste Arbeit für Ihr Widget erledigt.

Noch fehlende Import-Anweisungen ergänzen

Nun müssen Sie noch dafür sorgen, dass die import-Anweisungen am Anfang der Datei AppWidget.java vollständig sind. Sie sollten so aussehen:

import android.app.Activity;
import android.app.IntentService;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.widget.RemoteViews;

Listing 7.4:Die import-Anweisungen in der Datei AppWidget.java>

Jetzt können Sie Ihre Anwendung installieren und testen. Wählen Sie dazu RUN:RUN oder drücken Sie [Strg]+[F11]. Ihre Anwendung sollte nun im Emulator angezeigt werden. Kehren Sie durch Anklicken der Home-Taste zum Startbildschirm zurück. Dort können Sie nun das gerade erstellte App-Widget hinzufügen.

Wenn hier Fehlermeldungen angezeigt werden sollten, die auf @drawable/ic_ launcher hinweisen, finden Sie entsprechende Hinweise im Abschnitt weiter vorn in diesem Kapitel. Ein paar allgemeine Hinweise zu besonderen Eclipse-Macken finden Sie zudem auch am Ende von Kapitel 3.

Ihr App-Widget zum Startbildschirm hinzufügen

Die für die Benutzeroberfläche verantwortlichen Experten im Android-Team haben hervorragende Arbeit geleistet, als sie dafür gesorgt haben, dass App-Widgets leicht zum Startbildschirm hinzugefügt werden können. Führen Sie dazu diese folgenden einfachen Schritte aus:

1. Öffnen Sie die Anwendungsliste über den Home-Bildschirm des Emulators.

2. Wenn die Liste der Anwendungen angezeigt wird, schalten Sie am oberen Bildschirmrand von der Anzeige der Apps zur Anzeige der Widgets um.

3. Tippen Sie hier das in Abbildung 7.6 dargestellte App-Widget mit dem Titel LAUTLOS SCHALTEN an und ziehen Sie es auf eine der Seiten des Startbildschirms.

Die an dieser Stelle erforderliche Vorgehensweise wurde eingangs dieses Kapitels auch für ältere Android-Versionen ausführlicher und hübsch bebildert dargestellt.

Abbildung 7.6: Das Symbol LAUTLOS SCHALTEN in der Widget-Liste eines physischen Android-Testgeräts

Damit haben Sie das Widget LAUTLOSMODUS-UMSCHALTER zu Ihrem Startbildschirm hinzugefügt (Abbildung 7.6). Sie können das Symbol antippen, um den Klingelmodus zu ändern und das Symbol ändert sich daraufhin entsprechend (siehe Abbildung 7.7).

Abbildung 7.7: Das App-Widget auf einem Startbildschirm

8

Verteilung Ihrer App über Google
Play Store

In diesem Kapitel

Eine APK-Datei erstellen

Ein Entwicklerprofil im Google Play Store erstellen

Einen Preis für Ihre App festlegen

Ihre App mit Bildschirmfotos präsentieren

Ihre Anwendung hochladen und veröffentlichen

Downloads überwachen

Google Play Store ist die offizielle Verteilungszentrale für Android-Anwendungen. Wenn Sie Ihre App hier veröffentlichen, kann sie von Millionen Anwendern weltweit heruntergeladen, installiert und genutzt werden. Die Benutzer können Apps dort auch bewerten und kommentieren, um so auf gängige Trends und mögliche problematische Aspekte hinzuweisen.

Google Play Store bietet auch eine Reihe wertvoller Statistiken, über die Sie laufend Aufschluss über den Erfolg Ihrer Anwendung erhalten können.

In diesem Kapitel werden Sie Ihre Anwendung über Google Play Store veröffentlichen. Sie werden auch erfahren, wie Sie Ihrer App ein paar Bildschirmfotos, ein Werbebildschirmfoto und eine Kurzbeschreibung beifügen können.

Eine verteilbare Datei erstellen

Sie hatten eine tolle Idee und haben die nächste kommende Erfolgsanwendung oder das nächste Topspiel für die Android-Plattform entwickelt und wollen sie nun für Endanwender verfügbar machen. Dazu müssen Sie Ihre App erst einmal so verpacken, dass sie auf das Gerät der Endanwender gelangen kann. Dazu müssen Sie eine Android-Paketdatei erstellen, die auch APK-Datei genannt wird.

In den folgenden Abschnitten werden Sie eine APK-Datei erstellen.

Noch einmal zurück zur Manifest-Datei

Bevor Sie ins kalte Wasser springen und die verteilbare APK-Datei erstellen, sollten Sie dafür sorgen, dass Ihre App für möglichst viele Anwender verfügbar gemacht wird, und sich mit dem Element uses−sdk in der Datei AndroidManifest.xml vertraut machen. Ihre AndroidManifest.xml (siehe Kapitel 4) enthält momentan diesen uses−sdk-Eintrag:

<uses−sdk android:minSdkVersion="4" />

Die Eigenschaft minSdkVersion gibt an, unter welchen Versionen der Android-Plattform diese Anwendung installiert werden kann. In diesem Beispiel wurde Version 4 ausgewählt. Bei der Entwicklung der App LAUTLOSMODUS-UMSCHALTER wurde API Level 17 (Android 4.2) als Ziel-SDK eingestellt.

Die Android-Plattform ist weitgehend aufwärtskompatibel. Beinahe alle Funktionen der Version 3 existieren auch noch in Version 4. Es gibt zwar in allen neuen Versionen kleinere Änderungen und manchmal auch neue umfangreichere Komponenten, aber ansonsten bleiben nahezu alle Funktionen der Plattform aufwärtskompatibel. Wenn daher angegeben wird, dass eine Anwendung mindestens SDK-Version 4 benötigt, kann sie unter Android in allen Versionen ab 4 ausgeführt werden.

Über die minSdkVersion-Angabe kann Google Play Store feststellen, welche Anwendungen den Benutzern der jeweiligen Geräte angeboten werden. Wenn Sie die Anwendung jetzt mit dem eingestellten Wert 4 für minSdkVersion veröffentlichen und Google Play Store auf einem Android-Gerät nutzen, auf dem Version 3 (Android 1.5) oder eine noch ältere Version läuft, könnten Sie Ihre App dort nicht finden. Das liegt daran, dass Google Play Store das Angebot entsprechend filtert, wenn Entwickler angeben, dass ihre Apps erst ab dem API Level 4 ausgeführt werden können. Wenn Sie den Google Play Store mit einem Gerät aufrufen, auf dem API Level 4 oder eine spätere Version läuft, wird Ihre App auf diesem Gerät angezeigt und kann installiert werden.

Wenn Sie im Element uses−sdk des Manifests der Anwendung für minSdkVersion keinen Wert vorgeben, wird standardmäßig der Wert 0 als Vorgabe verwendet, der bedeutet, dass die Anwendung mit allen Android-Versionen kompatibel ist. Nutzt Ihre App dann Komponenten, die unter den älteren Plattformversionen noch nicht bereitstehen (beispielsweise Bluetooth unter Android 2.0), und wird von einem Anwender installiert, tritt während der Programmausführung ein Ausnahmefehler auf und es wird die Meldung angezeigt, dass die App nicht weiter ausgeführt werden kann.

Setzen Sie minSdkVersion immer auf die niedrigste und targetSdkVersion auf die höchste SDK-Version, unter der Sie Ihre App erfolgreich getestet haben.

Auswahl der Werkzeuge

Eine Android-APK-Datei können Sie auf verschiedene Weise erstellen:

Über die ADT (Android Development Tools) in Eclipse

Über einen automatischen Build-Vorgang und einen Integrationsserver

Über die Befehlszeile mit Ant

Über das Maven- oder Gradle-Build-System

Sie werden hier die ADT in Eclipse zum Erstellen der APK-Datei benutzen. Die ADT stellen eine Reihe von Werkzeugen zur Verfügung, mit denen Sie Ihre Android-Anwendung kompilieren, digital signieren und als Paket in eine APK-Datei packen können. Dabei findet die digitale Signierung statt. Der entsprechende Vorgang wird im nächsten Abschnitt vorgestellt.

Die anderen Optionen lassen sich zwar ebenfalls nutzen, werden aber üblicherweise erst im anspruchsvolleren Einsatz genutzt. Mehr zur Einrichtung eines Ant-Build-Prozesses für derartige Build-Prozeduren finden Sie in der Android-Dokumentation unter http://d.android.com/guide/publishing/app−signing.html.

Anwendungen digital signieren

Das Android-System verlangt, dass alle installierten Anwendungen mit einem Zertifikat digital signiert werden müssen, das einen öffentlichen und einen privaten Schlüssel enthält. Der private Schlüssel befindet sich im Besitz des Entwicklers. Das bei der digitalen Signierung der Anwendung verwendete Zertifikat dient als Kennung für die Anwendung und den Entwickler und wird zum Aufbau von vertrauenswürdigen Beziehungen zwischen Anwendungen verwendet.

Über die Signierung von Android-Anwendungen müssen Sie einige Dinge wissen:

Alle Android-Anwendungen müssen signiert werden. Das System verweigert die Installation unsignierter Anwendungen.

Zur Signierung Ihrer Anwendung können Sie selbstsignierte Zertifikate verwenden. Eine Zertifizierungsstelle wird dazu nicht benötigt.

Wenn Sie Ihre Anwendung über Google Play Store verteilen wollen, müssen Sie sie mit einem privaten Schlüssel signieren. Sie können die Anwendung nicht mit dem Debug-Schlüssel veröffentlichen, mit dem die APK-Datei für die Fehlersuche in ihrer Anwendung signiert wird.

Das Zertifikat hat ein Ablaufdatum, das nur bei der Installation geprüft wird. Wenn ein Zertifikat nach der Installation der betreffenden Anwendung abläuft, arbeitet sie dennoch normal weiter.

Wenn Sie nicht die ADT zum Erzeugen des Zertifikats und zum Signieren Ihrer APK-Dateien benutzen wollen, können Sie auch Standardwerkzeuge wie Keytool oder Jarsigner zu diesem Zweck verwenden.

Sie können modulare Anwendungen erstellen, die miteinander kommunizieren können, wenn die Anwendungen mit demselben Zertifikat signiert werden. Dann können die Anwendungen in demselben Prozess ausgeführt werden und vom System auf Anforderung wie eine einzelne Anwendung behandelt werden. Bei dieser Vorgehensweise können Sie Anwendungen in Modulen erstellen und Anwender können die einzelnen Module je nach Bedarf aktualisieren. Ein Beispiel dafür wäre die Entwicklung eines Spiels mit anschließender Veröffentlichung von »Update-Packs« zu dessen Aktualisierung. Anwender können dann wählen, welche Updates sie beziehen wollen.

Der Zertifizierungsprozess wird in der Android-Dokumentation unter http://d. android.com/tools/publishing/app−signing.html ausführlich beschrieben.

Erstellen eines Keystores

Bei einem Keystore handelt es sich in Android (und auch in Java) um einen Container, in dem Ihre persönlichen Zertifikate aufbewahrt werden. Eine Keystore-Datei können Sie in Android mit verschiedenen Werkzeugen erstellen:

ADT-Export-Assistent: Dieses Werkzeug wird zusammen mit den ADT installiert und ermöglicht Ihnen über einen Assistenten den Export einer selbstsignierten APK-Datei zur digitalen Signierung der Anwendung und die Erzeugung des Zertifikats und (bei Bedarf) auch des Keystores. Im folgenden Abschnitt werden Sie beim Erstellen Ihrer APK-Datei einen Keystore erstellen.

Keytool-Anwendung: Mit der Keytool-Anwendung können Sie einen selbstsignierten Keystore über die Befehlszeile erzeugen. Dieses Werkzeug mit seinen vielen Befehlszeilenoptionen finden Sie im Ordner tools des Android-SDKs.

Schutz Ihres Keystores

Die Keystore-Datei enthält Ihr privates Zertifikat, das Android zur Identifizierung Ihrer Anwendung im Google Play Store verwendet. Sie sollten eine Sicherung Ihres Keystores an einem sicheren Ort anlegen, da Sie im Falle des Verlusts Ihres Keystores die Anwendung nicht mehr mit demselben privaten Schlüssel signieren können. Da Google Play Store erkennt, dass die Anwendung nicht mit demselben Schlüssel signiert wurde, können Sie Ihre Anwendung nicht mehr aktualisieren. Dann handelt es sich für Google Play Store in einem solchen Fall um eine neue Android-Anwendung und nicht um eine neue Version einer Anwendung. Dasselbe geschieht, wenn Sie den Paketnamen der App ändern. Wenn Paketname und/oder Zertifikat nicht mehr übereinstimmen, hält Android die neue App-Version dann nicht mehr für ein gültiges Update.

Erstellen der APK-Datei

Führen Sie zum Erstellen Ihrer ersten APK-Datei die folgenden Schritte aus:

1. Starten Sie Eclipse, wenn es nicht bereits läuft.

2. Klicken Sie die App LAUTLOSMODUS-UMSCHALTER mit der rechten Maustaste an, wählen Sie im Kontextmenü ANDROID TOOLS und dann EXPORT SIGNED APPLICATION PACKAGE.

Daraufhin wird das Dialogfeld EXPORT ANDROID APPLICATION angezeigt (Abbildung 8.1), in dem der Name des aktuellen Projekts bereits als Vorgabe eingetragen wurde.

3. Klicken Sie die Schaltfläche NEXT an.

Das Dialogfeld KEYSTORE SELECTION wird angezeigt (Abbildung 8.2).

Abbildung 8.1: Das Dialogfeld EXPORT ANDROID APPLICATION

Abbildung 8.2: Das Dialogfeld KEYSTORE SELECTION

4.Bisher haben Sie noch keinen Keystore erstellt, weshalb Sie hier die Option CREATE NEW KEYSTORE aktivieren.

Wenn Sie bereits einen Keystore erstellt haben, wählen Sie hier stattdessen die Option USE EXISTING KEYSTORE.

5.Legen Sie den Speicherort für Ihren Keystore fest.

Erstellen Sie innerhalb des Ordners Android (im Stammverzeichnis oder im persönlichen Ordner) eine Datei für den Keystore. Für deren Namen sollten Sie die Erweiterung .keystore benutzen. Der vollständige Pfad könnte also beispielsweise so lauten:

C:\Users\Benutzername\Android\dummies.keystore

6.Wählen und merken Sie sich ein Passwort, das Sie in das Feld PASSWORD eingeben und im Feld CONFIRM bestätigen müssen.

Wenn Sie einen bereits vorhandenen Keystore verwenden, müssen Sie Ihr Passwort nicht bestätigen.

7.Klicken Sie die Schaltfläche NEXT an.

Daraufhin wird das Dialogfeld KEY CREATION angezeigt (Abbildung 8.3).

8.Füllen Sie die folgenden Felder aus:

• ALIAS: Dieses werden Sie zur Identifizierung des Schlüssels verwenden.

• PASSWORD und CONFIRM: Hier tragen Sie das Passwort ein, das für den Schlüssel verwendet wird, und bestätigen es.

• VALIDITY: Hier geben Sie an, wie lange der Schlüssel gültig bleiben soll. Er muss nach dem 22. Oktober 2033 ablaufen.

9.Im unteren Bereich des Dialogfelds müssen Sie Angaben zum Herausgeber vornehmen und mindestens eines dieser Felder ausfüllen:

• FIRST AND LAST NAME (Vor- und Nachname)

• ORGANIZATIONAL UNIT (Abteilung)

• ORGANIZATION (Organisation)

• CITY OR LOCALITY (Stadt oder Ort)

• STATE OR PROVINCE (Staat oder Provinz)

• COUNTRY CODE (XX) (als Ländercode wird die internationale Telefonvorwahl verwendet, die für Deutschland 49, für die Schweiz 41 und für Österreich 43 lautet)

Ich habe hier im Beispiel meinen Namen als Herausgeber angegeben.

10. Klicken Sie die Schaltfläche NEXT an.

Zuletzt wird das Dialogfeld DESTINATION AND KEY/CERTIFICATE CHECKS angezeigt (Abbildung 8.4).

Abbildung 8.3: Das Dialogfeld KEY CREATION

Abbildung 8.4: Einen Namen und einen Speicherort für Ihre erste APK-Datei angeben

11. Geben Sie einen Namen und den Speicherort für die APK-Datei mit Ihrer signierten App ein.

12. Klicken Sie die Schaltfläche FINISH an.

Damit werden die .apk- und eine Keystore-Datei an dem in Schritt 5 festgelegten Ort erstellt. Wenn Sie den entsprechenden Ordner öffnen, finden Sie dort die .keystore- und eine .apk-Datei (Abbildung 8.5).

Abbildung 8.5: Die erstellte APK-Datei im angegebenen Ordner

Damit haben Sie eine verteilbare APK-Datei und einen wiederverwendbaren Keystore für künftige Updates erstellt.

Ein Google-Play-Entwicklerprofil erstellen

Da Sie nun Ihre APK-Datei erstellt haben, können Sie die Anwendung über Google Play Store veröffentlichen. Dazu erstellen Sie ein Google-Play-Entwicklerprofil. Dafür benötigen Sie erst einmal ein Google-Konto. Für diesen Zweck eignen sich alle Google-Konten, wie zum Beispiel ein Gmail-Konto. Sollten Sie noch kein Google-Konto besitzen, können Sie unter www.google.com/accounts kostenlos ein solches anlegen.

Um ein Konto für den Google Play Store zu erstellen, führen Sie diese Schritte aus:

1. Starten Sie Ihren Webbrowser und rufen Sie die Seite http://market.android.com/publish auf.

2. Melden Sie sich auf der rechten Seite des Bildschirms über Ihr Google-Konto an.

Wenn Sie bereits angemeldet sind, fahren Sie direkt mit Schritt 3 fort, um Ihr Entwicklerprofil auszufüllen.

3. Um Ihr Entwicklerprofil zu erstellen, füllen Sie die in Abbildung 8.6 dargestellten Felder aus:

• NAME DES ENTWICKLERS: Der hier angegebene Name wird für den Entwickler der veröffentlichten Anwendungen angezeigt. Dabei kann es sich um den Namen Ihres Unternehmens oder Ihren eigenen Namen handeln. Sie können diese Angabe nach dem Erstellen Ihres Entwicklerprofils noch ändern.

• E-MAIL-ADRESSE: An die hier angegebene Adresse können Benutzer E-Mails schicken. Normalerweise wird es sich dabei um Fragen oder Kommentare zu der Anwendung handeln, für die sie sich interessieren.

• WEBSITE-URL: Die URL Ihrer Website. Wenn Sie keine eigene Website unterhalten, können Sie beispielsweise ein kostenloses Blogger-Konto über www.blogger.com einrichten und dort einen Blog erstellen, der für eine solche Website ausreicht. Hierbei handelt es sich um die einzige Angabe, die Sie nicht zwingend vornehmen müssen.

• TELEFONNUMMER: Hier geben Sie eine gültige Telefonnummer an, über die bei Problemen mit den veröffentlichten Inhalten zu Ihnen Kontakt aufgenommen werden kann.

Abbildung 8.6: Detailangaben zum Entwickler

4. Klicken Sie die Schaltfläche WEITER an.

Die Seite mit den »Vereinbarungen für den Entwicklervertrieb« wird angezeigt.

5. Lesen Sie sich die Bedingungen durch, aktivieren Sie das Kontrollkästchen und klicken Sie dann ICH STIMME ZU. WEITER an, um sich als Entwickler zu registrieren und Ihre Registrierungsgebühr zu entrichten.

Wenn Sie die Registrierungsgebühr in Höhe von momentan 25 US-Dollar nicht bezahlen, können Sie keine Apps über Google Play Store veröffentlichen.

6. Klicken Sie die Schaltfläche WEITER an, um Ihre Bestellung zu prüfen, und nehmen Sie die weiteren für die Zahlungsabwicklung erforderlichen Angaben vor.

Die nun angezeigten Angaben sind von den jeweils angebotenen Zahlungsoptionen und davon abhängig, ob Sie bereits ein Konto bei Google Wallet eingerichtet und Ihre Kreditkartendaten dort hinterlegt haben. Eine Kreditkarte ist dabei aber momentan zwingend erforderlich.

7. Abschließend wird Ihnen eine Seite mit einer Auftragsbestätigung angezeigt, über die Sie Ihren Auftrag schließlich über die entsprechende Schaltfläche wirklich abschließen können.

In Abhängigkeit von der Geschwindigkeit Ihrer Internet-Verbindung und der Verarbeitung Ihres Auftrags geht es mehr oder weniger schnell, bis Ihnen eine Bestätigungsmeldung angezeigt wird. Abbildung 8.7 zeigt die Auftragsbestätigung für einen der Buchautoren. Nun können Sie als Android-Entwickler Ihre Apps über Google Play Store verteilen.

Abbildung 8.7: Bestätigung der Registrierung

8. Klicken Sie den Link PLAY STORE ENTWICKLERSEITE an.

Nachdem Sie die hier möglicherweise angezeigten Nutzungsbedingungen bestätigt haben, können Sie über diese Seite Ihre Apps hochladen.

Wenn Sie kostenpflichtige Apps über Google Play Store verteilen wollen, müssen Sie hier ein Händlerkonto (Merchant Account) einrichten. Ein wenig mehr dazu erfahren Sie im Kasten »Google-Händlerkonto«.

Google-Händlerkonto

Wenn Sie kostenpflichtige Anwendungen über Google Play Store verteilen wollen, müssen Sie ein Google-Händlerkonto (Google Checkout merchant accounts) einrichten. Dies können Sie über die Play-Store-Entwicklerseiten erledigen. Hierbei müssen Sie einige weitere Angaben vornehmen:

Persönliche und Unternehmensdaten

Steueridentifikationsdaten (Ihre persönlichen oder die Ihres Unternehmens)

Erwartete monatliche Einkünfte (so etwa eine Milliarde, oder?)

Wenn Sie ein Händlerkonto eingerichtet haben, können Sie Anwendungen über Google Play Store verkaufen.

Preisgestaltung für Ihre Anwendungen

Erst haben Sie Ihre APK-Datei erstellt und dann haben Sie sich als Android-Entwickler registriert. Nun können Sie Ihre App schließlich an die Anwender verteilen. Abschließend müssen Sie sich selbst aber noch die Frage beantworten, ob Sie Ihre App kostenlos oder kostenpflichtig verteilen wollen.

Diese Entscheidung sollten Sie vor der Veröffentlichung Ihrer App treffen, weil sie psychologische Folgen bei potenziellen Kunden/Benutzern hat und sich auf Ihr Einkommen auswirken wird. Bei kostenpflichtigen Anwendungen müssen Sie deren Preis festlegen. Diese Entscheidung können Sie zwar nur selber treffen, aber immerhin kann ich Ihnen raten, sich einmal nach ähnlichen Apps auf dem Marktplatz umzusehen, um sich bei der Festlegung Ihrer Preisstrategie an diesen orientieren zu können. Die meisten Apps befinden sich im Preisbereich zwischen 99 Cents und 9,99 Dollar oder Euro. Apps, für die mehr als zehn Dollar oder Euro verlangt werden, sind rar. Bei der konkurrenzfähigen Preisgestaltung geht es um knallharte wirtschaftliche Aspekte, mit denen Sie sich auseinandersetzen müssen, wenn Sie feststellen wollen, was Sie für Ihre Anwendung verlangen können.

Bei der immerwährenden Debatte um kostenpflichtige und kostenlose Apps behaupten beide Gegner, dass die jeweilige Alternative profitabel sein kann. Letztlich müssen Sie aber selbst herausfinden, welche Alternative sich für Ihre Anwendung in der jeweiligen Situation am besten eignet.

Argumente für das kostenpflichtige Modell

Wenn Sie sich für das kostenpflichtige Modell entscheiden, bekommen Sie innerhalb von 24 Stunden nach dem ersten Verkauf (abgesehen von Sonn- und Feiertagen) Geld in Ihre Tasche. Das Geld wird am folgenden Werktag überwiesen. Kostenpflichtige Anwendungen werden oft aber nicht gerade häufig installiert.

Allen Benutzern von Google Play Store wird nach dem Kauf ein kurzer Testzeitraum von 15 Minuten eingeräumt. Während dieser Zeit können potenzielle Käufer die voll funktionsfähige Anwendung ausprobieren und sie bei Nichtgefallen unter voller Rückbuchung des Kaufpreises wieder deinstallieren. Dieser Testzeitraum ist höchst praktisch, da potenzielle Nutzer so keine Nachteile haben, wenn sie Apps kurz antesten. Allerdings sind 15 Minuten für viele Apps auch deutlich zu kurz.

Argumente für das kostenlose Modell

Wenn Sie sich für den kostenlosen Weg entscheiden, können potenzielle Benutzer die Anwendung frei installieren. Erfahrungsgemäß behalten 50 bis 80 Prozent der Benutzer, die Ihre kostenlose App installieren, diese auf dem Gerät, während sie von den übrigen deinstalliert wird. Die große Frage lautet an dieser Stelle aber, wie sich Geld mit kostenlosen Apps verdienen lässt.

»Nichts im Leben ist umsonst«, lautet ein uraltes Sprichwort. Ähnlich verhält es sich auch mit kostenlosen Apps. Letztlich haben Sie zwei Optionen:

Verkäufe über die App: Sie können den Nutzern Ihrer App verschiedene kostenpflichtige »Upgrades« anbieten, die dann über Google Play Store abgewickelt werden.

Werbung: Verschiedene Werbetreibende bieten Drittbibliotheken an, die bei der Nutzung Ihrer Anwendungen auf Mobilgeräten Werbung einblenden.

Die führenden Unternehmen in diesem Bereich sind momentan Google AdSense und AdMob. Bei diesen Unternehmen können Sie recht einfach kostenlose Konten einrichten. Sie bieten praktische SDKs an und erläutern Ihnen schrittweise, wie Sie Werbeanzeigen in Ihre eigenen Android-Anwendungen integrieren können. Da die meisten dieser Unternehmen ihre Zahlungen im 60-Tage-Rhythmus leisten, kann es ein paar Monate dauern, bis Sie erste Zahlungen erhalten.

Bildschirmfotos Ihrer Anwendung

Bildschirmfotos sind ein äußerst wichtiger Bestandteil von Google Play Store. Potenzielle Benutzer können sich darüber noch vor der Installation einer App einen ersten Eindruck verschaffen. Wenn Sie Benutzern eine Reihe von Bildschirmfotos von Ihrer Anwendung präsentieren, kann dies den Ausschlag darüber geben, ob sie sie installieren oder nicht. Stellen Sie sich vor, dass Sie ein Spiel programmiert haben und Benutzer dazu motivieren wollen, es zu spielen. Wenn Sie Wochen (oder auch Monate) mit dem Erstellen detaillierter Grafiken verbracht haben, werden Sie potenziellen Benutzern/Käufern des Spiels bestimmt zeigen wollen, wie toll Ihre App aussieht.

Um Bildschirmfotos von Ihrer laufenden Anwendung machen zu können, benötigen Sie einen Emulator oder ein physisches Android-Gerät. Um Bildschirmfotos von Ihrer Anwendung im Emulator zu machen, gehen Sie so vor:

1. Starten Sie den Emulator und legen Sie das Widget auf dem Startbildschirm ab.

2. Öffnen Sie in Eclipse die DDMS-Perspektive.

In Kapitel 5 können Sie Ihre diesbezüglichen Kenntnisse noch einmal auffrischen.

3. Markieren Sie den Emulator auf der Registerkarte DEVICES (Abbildung 8.8).

Abbildung 8.8: Die DDMS-Perspektive mit einem aufgenommenen Bildschirmfoto

4. Klicken Sie die Schaltfläche SCREEN SHOT an, um den gewünschten Bildschirm aufzunehmen.

5. Speichern Sie Ihr Bildschirmfoto über die Schaltfläche SAVE oder kopieren Sie es über die Schaltfläche COPY in die Zwischenablage, um es in ein Bildbearbeitungsprogramm zu übernehmen (zum Beispiel über BEARBEITEN:ALS NEUES BILD EINFÜGEN in Paint Shop Pro).

Sie können Änderungen im Emulator vornehmen und neue oder weitere Bildschirmfotos aufnehmen. Nach der Aufnahme des Bildschirmfotos können Sie es bei Google Play Store veröffentlichen.

Bei physischen Geräten können Sie Bildschirmfotos wie beim Emulator machen. Dazu müssen Sie die App nur auf dem physischen Gerät installieren und dieses anstelle des Emulators unter DEVICES auswählen. Bei einigen Geräten (insbesondere mit neueren Android-Versionen) können Sie auch direkt Bildschirmfotos bei Betätigung bestimmter Tasten erstellen. Beispielsweise könnten sich Optionen wie SCREENSHOT STATT SUSPEND unter EINSTELLUNGEN:ANWENDUNGEN:ENTWICKLUNG verbergen. Die so erstellten Bildschirmfotos können Sie dann auf den Computer übertragen.

Ihre Anwendung auf Google Play Store hochladen

Schließlich haben Sie den Höhepunkt der Android-Anwendungsentwicklung erreicht, nämlich den Punkt, an dem Sie Ihre Anwendung veröffentlichen können. Dazu benötigen Sie die folgenden Angaben und Daten:

Die signierte APK-Datei Ihrer App

Bildschirmfotos Ihrer Anwendung

Eine Beschreibung und einen Werbetext für Ihre App

Ein optionales Werbebild für den Fall, dass Ihre App im Google Play Store herausgestellt wird

Für die eigentliche Veröffentlichung führen Sie die folgenden einfachen Schritte aus:

1. Auf der Homepage für Android-Entwickler klicken Sie die Schaltfläche UPLOAD APPLICATION (APP HOCHLADEN) an.

Die Seite UPLOAD AN APPLICATION wird angezeigt. (Abbildung 8.9 zeigt diese Seite für die zuletzt in diesem Buch vorgestellte App.)

2. Wählen Sie unter APPLICATION .APK FILE die zuvor in diesem Kapitel erstellte .apk-Datei aus und klicken Sie dann UPLOAD an.

Google Play Store verwendet den Java-Paketnamen zu Identifikationszwecken. Es kann keine zwei Anwendungen mit demselben Paketnamen geben. Wenn Sie die Anwendung daher jetzt hochzuladen versuchen, werden Sie eine Fehlermeldung erhalten, die Ihnen mitteilt, dass der Paketname Ihrer APK-Datei mit dem bereits vorhandenen Paket eines anderen Entwicklers identisch ist, und dazu aufgefordert, einen anderen Paketnamen zu verwenden. Bei der Entwicklung eigener Anwendungen für Google Play Store sollten Sie deshalb vielleicht entweder Ihren oder den Namen Ihres Unternehmens als Teil des Paketnamens verwenden, um keine entsprechenden Meldungen zu erhalten.

3. Fügen Sie im Bereich SCREENSHOTS zwei Bildschirmfotos Ihrer App hinzu.

Anwendungen mit Bildschirmfotos erreichen höhere Installationsraten als Anwendungen ohne. Diese Bildschirmfotos zeigen potenziellen Benutzern Ihre Anwendung im laufenden Zustand, ohne dass sie sie dazu installieren müssten.

4. Fügen Sie (optional) ein Werbebild (Promotional Graphic) hinzu.

Dieses Werbebild muss nicht aus Ihrer App stammen, sondern kann frei erstellt werden und dient reinen Werbezwecken. Es wird für die zufälligen Google-Werbevorstellungen während des Durchsuchens des Play Stores genutzt. Ein solches Bild ist nicht erforderlich, um Apps veröffentlichen zu können.

Abbildung 8.9: Die Seite UPLOAD AN APPLICATION

5. Legen Sie einen Namen für Ihre Anwendung fest.

Wählen Sie einen Titel, der sich für Ihre App eignet. Dieser wird für die Suche in Google Play Store indiziert.

6. Geben Sie eine Beschreibung Ihrer Anwendung ein.

Hierbei handelt es sich um die Beschreibung, die Benutzer zu sehen bekommen, wenn sie sich für Ihre Anwendung interessieren und sie möglicherweise installieren wollen. Der gesamte Text der Beschreibung wird für die Suche über Google Play Store indiziert.

7. Geben Sie den Werbetext für Ihre Anwendung ein.

Der Promo-Text wird verwendet, wenn Ihre Anwendung auf dem Markt besonders vorgestellt oder beworben wird. Die Kriterien dafür, welche Anwendungen herausgehoben werden, sind nicht wirklich klar, basieren aber wohl vorrangig auf deren Beliebtheit. Wird Ihre Anwendung ausgewählt und im Werbebereich des Marktplatzes vorgestellt (beispielsweise oben oder auch links auf dem Bildschirm bei den jeweiligen Kategorien), wird dieser Text begleitend angezeigt.

8.Legen Sie den Anwendungstyp fest.

Wählen Sie einen geeigneten Typ für Ihre App aus. Wenn es sich um ein Spiel handelt, wählen Sie also beispielsweise »Spiele«.

9.Legen Sie die Kategorie für Ihre App fest.

Die Kategorie sollten Sie passend zur App und zum Anwendungstyp wählen.

10. Deaktivieren Sie den Kopierschutz.

Mit dieser Einstellung können Sie dafür sorgen, dass Ihre App nicht illegal auf andere Geräte kopiert werden kann. Wenn Sie das aber wirklich verhindern wollen, sollten Sie besser den Lizenzierungsdienst von Google Play benutzen. Mehr dazu erfahren Sie über http://d.android.com/guide/google/play/ licensing/index.html.

11. Wählen Sie eine Liste der Standorte aus, an denen Ihre Anwendung sichtbar sein soll.

Wenn Sie beispielsweise eine deutsche Anwendung erstellt haben, deaktivieren Sie alle Standorte und aktivieren Deutschland (Germany) und vielleicht noch Schweiz (Switzerland) und Österreich (Austria) als Zielmärkte. Dann wird die Anwendung auf dem Marktplatz nur für Geräte angezeigt, deren Standort sich in diesen Ländern befindet. Wenn Sie alle Standorte aktiviert lassen, wird Ihre App weltweit angezeigt.

12. Füllen Sie die Felder für die Website und die E-Mail-Adresse aus. (Und wenn Sie es nicht lassen können, auch das Feld für die Telefonnummer.)

Über diese Felder können Sie angeben, wie Benutzer Ihrer App Kontakt zu Ihnen aufnehmen können. Wundern Sie sich aber nicht, dass angegebene Telefonnummern auch wirklich genutzt werden. Meist geht es dabei um erwünschte zusätzliche Funktionen und Fehlerberichte. Wenn Sie eine App für ein anderes Unternehmen schreiben, sie aber unter Ihrem Entwicklerkonto veröffentlichen, können Sie die Angaben zur Website, E-MailAdresse und Telefonnummer ändern, damit sich die Benutzer nicht an Sie wenden.

13. Bestätigen Sie, dass Ihre Anwendung den Android-Inhaltsrichtlinien und gesetzlichen Vorschriften entspricht, indem Sie die entsprechenden Kontrollkästchen aktivieren.

14. Wählen Sie eine der folgenden Optionen:

• Publish/Veröffentlichen: Speichert und veröffentlicht die App direkt auf dem Marktplatz.

• Speichern: Speichert vorgenommene Änderungen, ohne die App zu veröffentlichen. Auf der Entwickler-Homepage wird Ihre App entsprechend gekennzeichnet. Zur Veröffentlichung markieren Sie den Titel und klicken die für das Hochladen zuständige Option an.

Sie können hier auch Löschen/Delete auswählen, was Sie aber nicht tun sollten, da damit alle bisherigen Schritte und Arbeiten gelöscht werden würden.

Klicken Sie für diese Übung die Schaltfläche SAVE an. Damit wird Ihre Anwendung gespeichert und Sie kehren zu Ihrer Android-Entwickler-Homepage zurück, auf der Sie unten ein Symbol darauf hinweist, dass eine Anwendung für Google Play Store gespeichert wurde (Abbildung 8.10). Sie können diese Möglichkeit zum Sammeln Ihrer Apps vor deren Veröffentlichung nutzen.

Abbildung 8.10: Eine gespeicherte App auf der Android-Entwickler-Homepage

15. Blättern Sie auf der Seite nach unten und klicken Sie die Schaltfläche PUBLISH/VERÖFFENTLICHEN an.

Damit wird Ihre Anwendung über Google Play Store veröffentlicht.

Abbildung 8.11 zeigt die gerade erstellte Anwendung auf einem Nexus One. Um sie anzuzeigen, habe ich den MARKET gestartet, bin erst zu APPS und dann zu PRODUCTIVITY gewechselt und habe dort die Registerkarte JUST IN aktiviert, auf der die ganz neu hochgeladenen Apps angezeigt werden.

Wahrscheinlich ist Ihnen ein Vorteil dieser Vorgehensweise aufgefallen, der darin besteht, dass Apps hier nicht wie bei anderen Mobilplattformen erst genehmigt werden müssen! Sie können sofort eine App entwickeln und veröffentlichen und schon können potenzielle Benutzer sie installieren. Dadurch können Sie kurz nacheinander neue Versionen veröffentlichen und neue Funktionen unmittelbar nach Fertigstellung veröffentlichen, was wirklich toll ist.

Wenn Sie jemals eine App aus dem Google Play Store entfernen wollen, markieren Sie den Anwendungsnamen auf Ihrer Entwickler-Homepage und entfernen sie über die entsprechende Option (UNPUBLISH).

Installationshäufigkeit überwachen

Sie haben Ihre erste Anwendung veröffentlicht. Dann wollen Sie doch bestimmt auch sehen, wie die Millionen bei Ihnen eintrudeln. Oder so ähnlich. Vielleicht sind Sie ja ein selbstständiger Entwickler, der den nächsten erfolgreichen Ego-Shooter veröffentlicht, oder ein Programmierer, der die Android-Anwendungen seines Unternehmens publiziert. Wie dem auch sei, Sie müssen über die Erfahrungen der Benutzer mit Ihren Apps auf den verschiedenen Geräten auf dem Laufenden bleiben.

Abbildung 8.11: Die veröffentlichte Anwendung im Google Play Store

Ihnen stehen mehrere Möglichkeiten offen, um sich über den Erfolg Ihrer Anwendung zu informieren:

Fünf-Sterne-Bewertungssystem: Je höher die durchschnittliche Bewertung, desto besser!

Kommentare: Benutzer nehmen sich die Zeit, Kommentare zu verfassen, erweisen Sie ihnen also die Höflichkeit und lesen Sie sie. Sie werden überrascht sein, wie viele tolle Ideen Ihnen hier kostenlos angeboten werden. Oft sind Benutzer von neu implementierten Funktionen derart begeistert, dass sie ihre Kommentare und Bewertungen aktualisieren und sehr viel positiver gestalten.

Fehlerberichte: Manchmal übermitteln Ihnen Benutzer Berichte, um Sie darüber zu informieren, dass bei Ihrer App aus unbekannten Gründen Ausnahmefehler aufgetreten sind. Sehen Sie sich diese Berichte und die Fehler an, beobachten Sie die Entwicklung des Stapels und versuchen Sie, den Fehler zu beheben. Wenn eine Anwendung häufiger abstürzt, führt dies recht schnell zu einer Menge wirklich schlechter Bewertungen. Die Rückverfolgung des Stapels ist nur bei Geräten möglich, auf denen mindestens Android 2.2 läuft.

Installationen versus aktive Installationen: Dabei handelt es sich zwar nicht um den besten Maßstab für die Benutzerzufriedenheit, aber doch um eine unwissenschaftliche Möglichkeit, mit der sich feststellen lässt, ob Benutzer Ihre App nach der Installation auch weiterhin auf ihren Geräten lassen. Wenn Benutzer Ihre App installiert lassen, muss sie ihnen gefallen!

Direkte E-Mails: Benutzer können über Google Play Store Ihre E-Mail-Adresse und/oder die Adresse Ihrer Website erfahren. Sie werden Ihnen E-Mails schicken, um Ihnen Fragen über Funktionen zu stellen oder über eigene Erfahrungen zu berichten. Vielleicht schicken sie Ihnen auch Ideen zur Verbesserung Ihrer App oder bitten Sie, eine App mit Funktionen zu entwickeln, wie es sie bei Google Play noch nicht gibt. Antworten Sie, wenn es Ihnen Ihre Zeit erlaubt. Bei einer Million aktiver Benutzer lässt sich zwar kaum mehr verwirklichen, aber Benutzer sind oft wirklich glücklich, wenn sie wissen, dass Sie erreichbar sind, wenn Probleme mit Ihren Apps auftreten.

Den Kontakt zur Benutzerbasis aufrechtzuerhalten, ist an und für sich bereits eine riesige Aufgabe. Wenn Sie das aber schaffen, werden Sie vielleicht mit engagierten und zufriedenen Kunden belohnt, die Ihre Anwendung Freunden und Familienangehörigen weiterempfehlen.

Neben dem Google Play Store bietet auch der Amazon App Store für Android (eines der größten App-Angebote für Android-Geräte, das nicht von Google selbst bereitgestellt wird) Anwendungen an, die Benutzer kaufen und installieren können. Entwickler können hier ihre Apps gegen adäquate Vergütung verkaufen oder sie kostenlos bereitstellen. Und auch Amazon stellt hervorragende Analyseinstrumente bereit. Mehr dazu erfahren Sie über http://developer.amazon.com. Wie Sie Ihre App für den Amazon App Store aufbereiten müssen, erfahren Sie zudem in Kapitel 18.

Teil III

Eine umfassendere App erstellen

In diesem Teil …

Teil III baut auf Ihre Kenntnisse aus Teil II auf und zeigt Ihnen, wie Sie eine funktionsreichere App erstellen können. Dabei werden wir auch einige fortgeschrittenere Themen ansprechen, die Ihnen den Übergang vom Einsteiger zum fortgeschrittenen Android-Entwickler erleichtern sollen.

In diesem Teil werden Sie bestimmte Funktionen erstellen, die die Nutzererfahrungen beim Umgang mit Ihren Apps verbessern sollen. Am Ende von Teil III werden Sie eine voll funktionsfähige anspruchsvollere Anwendung vorliegen haben, die auf eine lokale Datenbank zurückgreift und Benutzereinstellungen speichert.

9

Entwurf einer App zur Terminplanung

In diesem Kapitel

Anforderungen der App zusammenstellen

Mehrere Bildschirmlayouts entwerfen

Eine Liste als Aktivität erstellen

Mit Intents arbeiten

Das Erstellen von Android-Anwendungen macht zwar Spaß, wirklich interessant wird es aber erst, wenn wirklich umfassende Anwendungen programmiert werden. Erst damit dringt man zu den wirklichen Interna der Android-Plattform vor. In diesem Kapitel wird Ihnen eine App zur Terminplanung vorgestellt, die Sie in den nächsten paar Kapiteln erstellen sollen.

Mit der Terminplaner-Anwendung können Benutzer eine Liste von Einträgen erstellen, mit denen jeweils ein Erinnerungstermin einhergeht.

Überblick über die Basisanforderungen

Die Terminplaner-Anwendung muss einige Grundanforderungen erfüllen:

Sie muss Benutzereingaben entgegennehmen können, denn ohne solche wäre eine derartige App ziemlich sinnlos!

Die Termine müssen sich leicht verwalten lassen.

Zu den einzelnen Terminen muss es ein Erinnerungsdatum mit einer Uhrzeit geben, zu der Benutzer an sie erinnert werden.

Der Benutzer muss zum angegebenen Zeitpunkt an den jeweiligen Termin erinnert werden.

Benutzer müssen Termine löschen können.

Benutzer müssen Termine nicht nur hinzufügen, sondern auch bearbeiten können.

Es kommt bei dieser Anwendung also zu recht umfassenden Interaktionen zwischen ihren Benutzern und dem Android-System. Die folgenden Abschnitte befassen sich mit den Funktionen, die Sie in die App integrieren, um den Benutzern die benötigten Funktionen bieten zu können.

Erinnerungstermine erstellen

Damit die Terminplaner-Anwendung wirklich funktioniert, muss sie den Benutzer irgendwie an die Termine erinnern oder aufmerksam machen können. Damit sind wir direkt bei den Erinnerungsterminen. Unter dem Windows-Betriebssystem können Entwickler Aufgaben (Tasks) und damit Code, Programme oder Skripts mit dem Programm AUFGABENPLANUNG zu vorgegebenen Zeiten geplant ausführen lassen. Im Unix/Linux-Bereich verwenden Entwickler zu diesem Zweck cron (für chronos – Griechisch für Zeit).

Da Android auf dem Linux-Kernel 2.6 basiert, könnte man daher annehmen, dass es auch hier eine crontab-Datei gibt, die bearbeitet werden kann. Leider fehlt cron aber bei Android. Dafür gibt es jedoch die Klasse AlarmManager, mit der Sie dasselbe erreichen können. Mit ihr können Sie angeben, wann Ihre Anwendung gestartet werden soll. Weckereignisse können einmalig oder wiederholt ausgelöst werden. Die Terminplaner-Anwendung nutzt die Klasse AlarmManager, um Benutzer an unerledigte Termine zu erinnern.

Daten speichern

Alle Aktivitäten, Daten der verschiedenen Einträge und Alarmtermine werden an den folgenden Orten gespeichert:

Aktivitäten und BroadcastReceiver: Diese werden in einem einzelnen Java-Paket gespeichert.

Termindaten: Diese werden in einem ContentProvider gespeichert, der sich auf eine SQLite-Datenbank stützt.

Alarmtermine: Diese werden über das Intent-System aus dem ContentProvider übernommen und im AlarmManager gespeichert.

Den Benutzer (höflich) aufmerksam machen

Bei einem eintretenden Termin muss der Benutzer durch ein entsprechendes Alarmsignal darauf aufmerksam gemacht werden. Die Android-Plattform stellt Mechanismen zur Verfügung, die Ihre Aktivität dann in den Vordergrund holen, aber das ist kein optimales Benachrichtigungsverfahren, weil es den Anwendungsfokus von den aktuellen Benutzertätigkeiten abzieht. Stellen Sie sich vor, dass der Benutzer gerade eine Telefonnummer eingibt oder einen Anruf beantwortet, wenn der Alarm ausgelöst und eine andere Aktivität in den Vordergrund geholt wird. Das dürfte den Benutzer sehr wahrscheinlich erheblich irritieren, denn schließlich wurde hier ohne dessen eigenes (unmittelbares) Zutun eine Aktivität gestartet.

Es existieren aber noch weitere Möglichkeiten, um einen Benutzer auf etwas aufmerksam machen zu können, ohne dabei seine aktuelle Tätigkeit in den Hintergrund zu verbannen. Dazu zählen:

Toasts: Bei einem Toast handelt es sich um ein kleines Steuerelement, das eine Kurzmitteilung für den Benutzer enthält. Die Mitteilung wird nicht dauerhaft, sondern allenfalls einige Sekunden lang angezeigt. Ein Toast erhält nie den Fokus. Der Terminplaner verwendet Toasts zwar nicht, um Benutzer auf etwas aufmerksam zu machen, nutzt sie aber für Hinweise beim Speichern von Aktivitäten.

NotificationManager: Diese Klasse wird dazu verwendet, um Benutzer über stattfindende Ereignisse zu informieren. Die entsprechenden Meldungen können über die Status- und Benachrichtigungsleiste am Bildschirmrand erfolgen. Benachrichtigungen können mehrere Steuerelemente umfassen und lassen sich über von Ihnen vorgegebene Symbole identifizieren. Um sich eingehender über die Benachrichtigung zu informieren, kann der Benutzer die Statusleiste aufziehen. Die Terminplaner-App nutzt die Klasse NotificationManager zur Verarbeitung von Alarmereignissen. (In Kapitel 1 wurde die Funktionsweise des Benachrichtigungsbereichs näher beschrieben.)

Sie können Benutzer auch über Dialogfelder auf bestimmte Ereignisse aufmerksam machen. Dabei wird aber sofort der Fokus von aktuell laufenden Apps abgezogen. Auf diese Weise lässt sich zwar wirklich die Aufmerksamkeit des Benutzers gewinnen, es wird ihn aber möglicherweise irritieren, wenn der Fokus (bei vielen Alarmereignissen möglicherweise recht häufig) von seinen aktuellen Apps abgezogen und von anderen Anwendungen übernommen wird.

Das Layout der App-Bildschirme entwerfen

Die Terminplaner-Anwendung soll aus zwei verschiedenen Bildschirmen bestehen, über die alle Basisfunktionen (Erstellen, Lesen, Ändern und Entfernen von Terminen) abgewickelt werden können:

Bei der ersten View handelt es sich um eine Listenansicht aller aktuellen Termine in der Anwendung, die nach deren Namen sortiert werden soll. Beim Lang-Druck auf einem Eintrag sollen einzelne Listenelemente auch entfernt werden können.

Über die zweite View sollen Benutzer Termine lesen, hinzufügen und bearbeiten können.

Beide Bildschirme bestehen dabei aus jeweils einem Codefragment, das die Schnittstelle für den Bildschirm weitgehend enthält und in einer Aktivität enthalten ist.

Sie können Fragmente wiederverwenden, wenn Sie Tablets in Ihrer App unterstützen wollen. Teil IV befasst sich eingehender mit den speziellen Aspekten der Entwicklung für Tablets.

Das neue Projekt erstellen

Starten Sie zunächst Eclipse und erstellen Sie eine neue Android-App als Projekt über das Kontextmenü im Package Explorer. Verwenden Sie für den Terminplaner die in Tabelle 9.1 zusammengestellten Werte. (Wenn Sie nicht mehr genau wissen, wie Android-Projekte erstellt werden, kehren Sie noch einmal zu Kapitel 3 zurück.)

Wenn Sie die Quelltexte für das Beispiel aus dem Internet herunterladen, können Sie auch auf die entsprechenden Android-Quelltexte zurückgreifen.

Beachten Sie den Wert ReminderListActivity für die zu erzeugende Aktivität unter ACTIVITY NAME. Meist wird der ersten Aktivität in einer Anwendung der Name MainActivity gegeben, weil dieser auch vorgegeben wird. Der erste Bildschirm, den der Benutzer zu sehen bekommt, zeigt jedoch eine Liste der gespeicherten Termine an. Daher handelt es sich bei dieser Aktivität eigentlich um eine Instanz von ListActivity, weshalb hier auch der Name ReminderListActivity verwendet wird.

Eigenschaft | Wert
APPLICATION NAME | Terminplaner
PROJECT NAME | Terminplaner
PACKAGE NAME | com.dummies.android.terminplaner
MINIMUM REQUIRED SDK | API 4: ANDROID 1.6 (DONUT)
TARGET SDK | API 16: ANDROID 4.1 (JELLY BEAN)
COMPILE WITH | API 17: ANDROID 4.2 (JELLY BEAN)
CREATE ACTIVITY | YES
ACTIVITY NAME | ReminderListActivity

Tabelle 9.1: Einstellungen für das neue Projekt

Die Terminplaner-App nutzt Funktionen aus der Android Support Library, um auch noch Geräte unterstützen zu können, auf denen ältere Versionen wie Android 2.x oder sogar 1.6 laufen. Fügen Sie die Bibliothek mit den folgenden Schritten zu Ihrem Eclipse-Projekt hinzu:

1. Kopieren Sie android−support−v13.jar in das Verzeichnis libs Ihres Projekts.

Sie finden diese Bibliothek im Ordner ANDROID_SDK/extras/android/support/v13.

Wenn Sie die Datei android−support−v13.jar nicht im Ordner mit dem Android-SDK finden können, haben Sie die Support Library bisher vielleicht noch nicht installiert. Starten Sie den Android SDK Manager und klicken Sie EXTRAS an, um das bei Bedarf nachzuholen.

2. Wählen Sie im Eclipse-Menü PROJECT:CLEAN.

Wenn das Projekt erst einmal neu aufgebaut wurde, sollten die Angaben unter ANDROID DEPENDENCIES wie in Abbildung 9.1 aussehen.

ReminderListActivity erstellen

Die von Eclipse erstellte Klasse ReminderListActivity ist praktisch leer, weshalb Sie einige Änderungen daran vornehmen müssen. Nehmen Sie folgende Änderungen vor:

Startdatei umbenennen. Benennen Sie die vorhandene Datei im Ordner res/layout Ihres Projekts in reminder_list.xml um. (Je nach installierter Entwicklungsumgebung werden hier möglicherweise unterschiedliche Namen automatisch vergeben.) Klicken Sie die vorhandene Datei dazu mit der rechten Maustaste an und wählen Sie im Kontextmenü REFACTOR:RENAME oder markieren Sie die Datei und betätigen Sie die Tastenkombination [Strg]+[Alt]+[R].

Abbildung 9.1: Die Datei android−support−v13.jar in Ihrem Projekt

Java-Datei aktualisieren. Wenn Sie den Dateinamen geändert haben, müssen Sie diesen auch im Aufruf setContentView() in der Datei ReminderListActivity.java aktualisieren, damit er auf den geänderten Dateinamen verweist. Öffnen Sie diese Datei und ändern Sie diese Zeile in:

setContentView(R.layout.reminder_list);

Vererbungsregel ändern. Da ReminderListActivity Fragmente enthält, muss sie von der Klasse FragmentActivity und nicht von der normalen Basisklasse Activity abstammen. Nehmen Sie auch diese Änderung vor.

Anschließend sollte die Klasse ReminderListActivity wie in Listing 9.1 aussehen, wobei die vorzunehmenden Änderungen fett hervorgehoben wurden.

public class ReminderListActivity extends ListActivity {

/** Wird beim Start der App zuerst aufgerufen */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.reminder_list);

}

}

Listing 9.1: Die Klasse ReminderListActivity

Vergessen Sie nicht, jeweils zusätzlich benötigte import-Anweisungen hinzuzufügen. Zudem können Sie in Eclipse möglicherweise vorhandenen, automatisch zusätzlich eingefügten Code einfach ignorieren. (Lassen Sie ihn einfach zusätzlich stehen.)

setContentView() verweist auf die Layoutdatei reminder_list.xml. Diese enthält aktuell aber noch den Standardcode, der beim Erstellen des Projekts erzeugt wurde. Öffnen Sie die Datei res/layout/reminder_list.xml und aktualisieren Sie ihn so, dass er wie in Listing 9.2 aussieht.

<?xml version="1.0" encoding="utf−8"?>

<fragment

xmlns:android="http://schemas.android.com/apk/res/android"

android:name="com.dummies.android.terminplaner

.ReminderListFragment"

android:layout_width="fill_parent"

android:layout_height="fill_parent" />

Listing 9.2: Der Inhalt der Datei reminder_list.xml

In der Layoutdatei Ihrer Activity gibt es mit ReminderListFragment nur ein einziges Element, das die gesamte Höhe und Breite des Bildschirms einnimmt. ReminderListFragment ist dafür zuständig, Benutzern die Aufgabenliste anzuzeigen.

ReminderListFragment erstellen

Fragmente sind jene Bestandteile Ihrer Aktivitäten, die wiederholt innerhalb Ihrer App verwendet werden sollen. Die meisten Aktivitäten bestehen aus ein oder zwei Fragmenten. Die Activity list benötigt ein Fragment zur Anzeige der Aufgabenliste. Erstellen Sie also eine neue Datei namens ReminderListFragment.java mit dem Code aus Listing 9.3.

package com.dummies.android.terminplaner;

import android.os.Bundle;
import android.support.v4.app.ListFragment; →4
import android.view.View;

public class ReminderListFragment extends ListFragment {

@Override

public void onActivityCreated(Bundle savedInstanceState) { →10

super.onActivityCreated(savedInstanceState);

setEmptyText(getResources().getString(R.string.no_reminders));  →12

}

}

Listing 9.3: Die öffentliche Klasse ReminderListFragment

Der Code aus Listing 9.3 lässt sich wie folgt kurz erläutern:

→4: Diese Zeile sorgt dafür, dass android.support.v4.app.* und nicht die Entsprechungen aus android.app importiert werden.

Importieren Sie android.support.v4.app.Fragment und android.support. v4.app.FragmentTransaction anstelle der Entsprechungen aus android. app. Wenn Sie die Klassen android.support.v4.* aus der Android Support Library verwenden, sorgen Sie dafür, dass Ihre Apps auch auf Geräten mit älteren Android-Versionen bis hinab zur Version 4 (Android 1.6) funktionieren. Wenn Sie die Versionen vor Android 3.x nicht mehr unterstützen wollen, können Sie auf die Nutzung dieser Support-Bibliothek natürlich verzichten.

→10: Die Methode onCreate() der Activity (nicht des Fragments) kehrt zurück und ruft die Callback-Funktion onActivityCreated() auf.

Wenn Sie etwas mit den Views Ihres Fragments machen müssen, eignet sich onActivityCreated() dafür hervorragend, weil Ihre Fragmente an dieser Stelle bereits garantiert komplett erstellt wurden.

→12: ListFragment unterstützt die Anzeige einer Mitteilung bei leerer Aufgabenliste. Der Aufruf verwendet den Wert setEmptyText() und die Mitteilung den Wert von R.string.no_reminders.

Fügen Sie <string name="no_reminders">Es liegen keine Termine vor</string> zur Datei res/values/strings.xml hinzu. Mehr Informationen zum Hinzufügen von Strings zur Datei strings.xml finden Sie in Kapitel 6.

Die Verwendung von Activities und Fragmenten

Aktivitäten und Fragmente sind beide zentrale Bestandteile des Codes der Bedienschnittstelle Ihrer App. Wie lässt sich dann aber entscheiden, welche Funktionen Sie in einem Fragment und welche Sie in einer Activity definieren?

Sie können den Code Ihrer Bedienschnittstelle zwar direkt in Aktivitäten packen, dieser ließe sich dann aber nur vergleichsweise schlecht wiederholt verwenden. Packen Sie Ihren Code stattdessen in ein Fragment, können Sie ihn viel leichter wiederholt nutzen.

Wenn Sie sich völlig sicher sind, dass Sie Code schreiben, der für eine bestimmte Aktivität spezifisch ist, sollten Sie ihn direkt in eine Activity packen. Wenn Sie sich dessen hingegen nicht sicher sind, verwenden Sie für den Code Ihrer Bedienschnittstelle besser Fragmente. In den meisten Apps enthalten Fragmente den gesamten Bedienschnittstellencode, während Aktivitäten nur Code enthalten, der die Fragmente miteinander verbindet.

Termine mit einer Activity erstellen und bearbeiten

Für die Terminplaner-Anwendung wird ein weiterer Bildschirm benötigt, über den Benutzer ihre Termine und die zugehörigen Daten bearbeiten können. Mit der neuen Aktivität und dem Fragment sollen Benutzer Termine erstellen, lesen und bearbeiten können.

Führen Sie die folgenden Schritte in Eclipse durch:

1. Erstellen Sie eine neue Aktivität für das Erstellen, Lesen und Ändern von Terminen.

Klicken Sie den Paketnamen im Ordner src mit der rechten Maustaste an und wählen Sie im Kontextmenü NEW:CLASS oder betätigen Sie [Shift]+[Alt]+[N] und wählen Sie CLASS.

2. Im Dialogfeld NEW JAVA CLASS tragen Sie im Feld NAME den Wert ReminderEditActivity und im Feld SUPERCLASS den Wert android.support.v4.app.FragmentActivity ein und klicken FINISH an.

Daraufhin wird die Datei ReminderEditActivity.java mit einer leeren Klasse erstellt und angezeigt.

3. Ersetzen Sie den Inhalt dieser Datei durch den Code aus Listing 9.4.

Dieser Code erzeugt die Activity, verbindet das Fragment mit einem Layout und richtet dann das Fragment für die Activity ein.

package com.dummies.android.terminplaner;

import android.os.Bundle;
import android.support.v4.app.Fragment; →3
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;

public class ReminderEditActivity extends FragmentActivity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.reminder_edit_activity); →11

Fragment fragment = getSupportFragmentManager().findFragmentByTag

(ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG); →15

if (fragment == null) { →17

fragment = new ReminderEditFragment();

Bundle args = new Bundle();

args.putLong(ReminderProvider.COLUMN_ROWID, getIntent()

.getLongExtra(ReminderProvider.COLUMN_ROWID, 0L)); →21

fragment.setArguments(args); →22

FragmentTransaction transaction = getSupportFragmentManager()

.beginTransaction(); →25

transaction.add(R.id.edit_container, fragment,

ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG); →27

transaction.commit(); →28

}

}

}

Listing 9.4: ReminderEditActivity

Der Code aus Listing 9.4 lässt sich wie folgt erläutern:

→3: Diese Zeile sorgt dafür, dass android.support.v4.app.* und nicht die Entsprechungen aus android.app importiert werden.

→11: Diese Zeile legt das Layout für dieses Fragment fest. Den Code für R.layout. reminder_edit_activity finden Sie in Listing 9.5.

→15: Bevor ein Fragment erstmals hinzugefügt wird, prüft diese Zeile, ob es nicht bereits vorhanden ist.

Wenn eine Activity (beispielsweise beim Rotieren des Bildschirms) aus einer anderen neu erzeugt wird, müsste auch das vorherige Fragment neu erzeugt werden. Dann wird das vorherige verwendet, anstatt ein völlig neues zu erstellen.

→17: Wenn die Activity kein zuvor verwendetes Fragment finden kann, wird in dieser Zeile ein neues Fragment erzeugt.

→21: Intents können Zusatzfunktionen enthalten, über die Aktivitäten einander Daten übergeben können. In Zeile 21 nutzt der Intent getLongExtra(), um den Wert einer Variablen vom Typ long namens COLUMN_ROWID zu übernehmen, sofern es einen solchen gibt. Ist das nicht der Fall, verwendet der Intent den Wert 0L (0 als long).

→22: Fragmente benötigen Argumente. Im Unterschied zu normalen Java-Klassen können Sie Fragmenten keine Argumente über einen Konstruktor übergeben. Stattdessen wird in Zeile 22 zu diesem Zweck ein Bundle namens Fragment.setArguments() genutzt.

→25: Für alle Interaktionen mit einem Fragment müssen Sie eine FragmentTransaction verwenden. In dieser Zeile wird FragmentActivity.getSupportFragmentManager() aufgerufen, um den FragmentManager zu ermitteln und dann eine Transaktion mit FragmentManager.beginTransaction() eingeleitet. Alle Fragmente operieren zwischen den Aufrufen von FragmentTransaction.beginTransaction() und FragmentTransaction.commit().

→27: In dieser Zeile wird das Fragment zur Activity hinzugefügt. Sie legt das Fragment in einem FrameLayout-Platzhalter namens R.id.edit_container ab, den Sie in Listing 9.5 erst noch definieren werden, und nennt es DEFAULT_EDIT_FRAGMENT_TAG, damit die App es unter diesem Namen wiederfinden kann.

→28: Diese Zeile beendet die Transaktion.

Vielleicht ist Ihnen aufgefallen, dass das Fragment in dieser Klasse anders als in der Klasse ReminderListActivity eingerichtet wurde. In ReminderListActivity kann das <fragment>-Element direkt zum XML-Code hinzugefügt werden, da es ohne Parameter auskommt. Der Activity ReminderEditFragment muss das Fragment manuell über Java-Code hinzugefügt werden, weil ihr die ID von Terminen übergeben werden muss.

Die in Listing 9.5 wiedergegebene und über NEW:ANDROID XML FILE im Ordner res/layout zu erstellende Layoutdatei reminder_edit_activity.xml besteht aus einem einzigen Platzhalterelement, das den gesamten Bildschirm für sich beansprucht und den Namen edit_ container trägt. Diesen verwendet der Java-Code in Listing 9.1, um das Element mit ReminderListFragment zu verbinden.

<?xml version="1.0" encoding="utf−8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/   android"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<FrameLayout

android:id="@+id/edit_container"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

/>

</LinearLayout>

Listing 9.5: R.layout.reminder_edit_activity

ReminderEditActivity nutzt eine Konstante der Klasse ReminderProvider, die bisher noch nicht existiert und die Sie daher jetzt erstellen sollten. Bearbeiten Sie den Code der Klasse so, dass die Datei ReminderProvider.java den folgenden Code enthält:

package com.dummies.android.terminplaner;

public class ReminderProvider {

public static final String COLUMN_ROWID = "_id";

}

Sie müssen der Android-Plattform auch noch mitteilen, dass es ReminderEditActivity gibt, und diese Aktivität zum Android-Manifest hinzufügen. Dazu fügen Sie die in Listing 9.6 fett dargestellten Zeilen zum Application-Element der Datei AndroidManifest.xml hinzu:

<application android:icon="@drawable/ic_launcher"

android:label="@string/app_name">

<activity android:name=".ReminderListActivity"

android:label="@string/app_name">

<intent−filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category. LAUNCHER" />

</intent−filter>

</activity>

<activity android:name=".ReminderEditActivity"

android:label="@string/app_name" />

</application>

Listing 9.6: Der neue Code für die Datei AndroidManifest.xml

Wenn Sie die Aktivität nicht zur Datei AndroidManifest.xml hinzufügen, tritt während der Laufzeit ein Ausnahmefehler auf, der Sie darüber benachrichtigt, dass Android die Klasse (die Activity) nicht finden kann.

Ein Fragment zur Activity hinzufügen

Nachdem Sie eine Aktivität für ReminderEditFragment erstellt haben, wird es Zeit, das Fragment zu erstellen. Erstellen Sie eine neue Java-Klasse namens ReminderEditFragment und übertragen Sie den folgenden Code in die Datei:

package com.dummies.android.terminplaner;

import android.os.Bundle;
import android.support.v4.app.Fragment; →4
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;

public class ReminderEditFragment extends Fragment {

public static final String DEFAULT_EDIT_FRAGMENT_TAG =  "editFragmentTag" →12

private EditText mTitleText;

private EditText mBodyText;

private Button mDateButton;

private Button mTimeButton;

private Button mConfirmButton;

private long mRowId; →19

@Override

public void onCreate(Bundle savedInstanceState) { →22

super.onCreate(savedInstanceState);

Bundle arguments = getArguments(); →25

if (arguments != null) {

mRowId = arguments.getLong(ReminderProvider.COLUMN_ROWID);

}

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) { →33

View v = inflater.inflate(R.layout.reminder_edit, container, false);  →35

mTitleText = (EditText) v.findViewById(R.id.title);

mBodyText = (EditText) v.findViewById(R.id.body);

mDateButton = (Button) v.findViewById(R.id.reminder_date);

mTimeButton = (Button) v.findViewById(R.id.reminder_time);

mConfirmButton = (Button) v.findViewById(R.id.confirm);

return v;

}

}

Listing 9.7: ReminderEditFragment.java

Der Code aus Listing 9.7 funktioniert wie folgt:

→4: Diese Zeile sorgt dafür, dass android.support.v4.app.* und nicht die Entsprechungen aus android.app importiert werden.

→12: Aktivitäten benötigen ein Tag oder eine ID, über die sie bei der Arbeit mit Fragmenten angesprochen werden können. Diese Zeile ordnet ReminderEditFragment eine ID zu, über die sie später wiederholt angesprochen werden kann.

→19: Jede Instanz von ReminderEditFragment besitzt eine ID für den jeweiligen Termin. RowID entspricht einer Zeile in der Datenbank. Wenn vorhandene Termine bearbeitet werden, entspricht mRowId der ID der Zeile in der Datenbank dieses Termins. Bei neuen Terminen wird mRowId der Wert 0 zugeordnet.

→22: Fragmente besitzen wie Activities eine onCreate()-Methode. onCreate() wird aufgerufen, wenn ein Fragment erzeugt wird, und in dieser Methode sollten Sie Fragmente auch generell möglichst weitgehend initialisieren.

Im Unterschied zu Activities werden in onCreate jedoch keinerlei Initialisierungen von Views vorgenommen. Das übernimmt erst onCreateView() in Zeile 33.

→25: Durch einen Aufruf von getArguments() ermittelt das Fragment, welchen Termin der Benutzer bearbeitet oder erzeugt.

Die Argumente liefert das von getArguments() zurückgegebene Bundle und nicht das Bundle, das an onCreate() übergeben wird (ein Irrtum, dem man leicht verfallen kann).

→33: Anders als bei Activities werden Ihre XML-Layouts in Fragment.onCreateView() und nicht in Activity.setContentView() erstellt. Diese Zeile erzeugt das Layout R.layout.reminder_edit und ruft dann findViewById() auf, um die View-Objekte einzurichten, ähnlich wie dies auch bei der Initialisierung von Activities der Fall ist.

Sie könnten die View-Objekte zwar auch in onActivityCreated() einrichten; da die Views nicht weiter verändert werden, ist es hier aber praktischer, das onCreateView() zu überlassen. Mehr Informationen über onActivityCreated() und onCreateView() erfahren Sie im Kasten »Der Lebenszyklus von Fragmenten«.

→35: In dieser Zeile wird inflate() aufgerufen, wobei attachToRoot auf false gesetzt wird, weil das Fragment die View anhängt.

Der Lebenszyklus von Fragmenten

Wie Activities (siehe Kapitel 5) besitzen auch Fragmente ihren eigenen Lebenszyklus.

Fragmente können wie Activities in drei Zuständen existieren:

Fortgesetzt (Resumed): Das Fragment ist in der laufenden Activity sichtbar.

Pausiert (Paused): Eine andere Activity befindet sich im Vordergrund und besitzt den Fokus. Die Activity, in die das Fragment eingebettet ist, ist dabei aber weiterhin sichtbar. (Die Vordergrund-Activity ist teilweise transparent oder beansprucht nicht den gesamten Bildschirm.)

Angehalten (Stopped): Das Fragment ist nicht sichtbar. Entweder wurde die HostActivity gestoppt oder das Fragment wurde zwar aus der Activity entfernt, aber in den Hintergrund verlagert. Angehaltene Fragmente bestehen weiter (alle Zustände und Mitgliedsdaten werden vom System gespeichert). Für Benutzer sind sie aber unsichtbar und werden zusammen mit der Activity beendet.

Die meisten Callback-Funktionen von Fragmenten ähneln stark denen der Activities. Es gibt jedoch einige wichtige Unterschiede. Die drei meistverwendeten Callback-Funktionen von Fragmenten sind:

onCreate: Im Unterschied zu Activities besitzen Fragmente keine setContentView()-Methode. Und Fragmente werden auch in keiner Weise in ihrer onCreate()Methode erzeugt, weshalb es keine Möglichkeiten gibt, hier Views zu manipulieren.

onCreateView: Um eine View in einem Fragment zu erzeugen, überschreiben Sie die Methode onCreateView(), erstellen die View selbst und geben sie dann am Ende der Funktion zurück. Listing 9.1 enthält ein entsprechendes Beispiel. Wichtig dabei ist, dass die Views zwar erzeugt, aber noch nicht vollständig fertiggestellt werden. Wenn gespeicherte Zustände der View wiederhergestellt werden sollen (wenn die Activity beispielsweise wegen einer Drehung des Bildschirms zerstört und neu erzeugt wurde), stehen diese erst nach dem nächsten Schritt bereit.

onActivityCreated: onActivityCreated() wird als letzter Schritt aufgerufen, bevor Ihr Fragment komplett erzeugt ist. Daher ist es üblicherweise am besten, den meisten für Views oder gespeicherte Zustände benötigten Code hier unterzubringen.

Das Layout für das Hinzufügen/Bearbeiten von Terminen erstellen

Das Layout, über das Termine hinzugefügt und bearbeitet werden können, ist recht einfach und enthält nur die folgenden Felder:

title: Der für einen Termin gewählte Titel, der in der Listenansicht angezeigt wird.

body: Ein längerer Beschreibungstext zum Termin. Hier können Benutzer Einzelheiten zu den jeweiligen Terminen eintragen.

reminder_date: Das Datum, an dem der Benutzer an den Termin erinnert werden soll.

reminder_time: Die Uhrzeit, zu der der Benutzer am gespeicherten Datum an den Termin erinnert werden soll.

In der fertigen Anwendung sieht der Bildschirm wie in Abbildung 9.2 aus.

Abbildung 9.2: Der Bildschirm TERMINPLANER – BEARBEITEN

Um dieses Layout zu erstellen, erzeugen Sie im Ordner res/layout eine Layoutdatei namens reminder_edit.xml. Führen Sie dazu diese Schritte aus:

1. Klicken Sie den Ordner res/layout mit der rechten Maustaste an und wählen Sie im Kontextmenü NEW:ANDROID XML FILE.

2. Tragen Sie den Dateinamen (reminder_edit.xml) in das Feld FILE ein.

3. Behalten Sie oben im Feld RESOURCE TYPE die Vorgabe LAYOUT als Angabe bei.

4. Wählen Sie im Feld ROOT ELEMENT die Option ScrollView aus.

5. Klicken Sie die Schaltfläche FINISH an.

Um den in Abbildung 9.2 dargestellten Bildschirm zu erstellen, müssen Sie alle zugehörigen View-Definitionen erstellen. Geben Sie dazu in die Datei reminder_edit.xml den Code aus Listing 9.8 ein.

<?xml version="1.0" encoding="utf−8"?>

<ScrollView

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"> →5

<LinearLayout →6

android:orientation="vertical" →7

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/title" /> →12

<EditText android:id="@+id/title"

android:layout_width="fill_parent"

android:layout_height="wrap_content" /> →15

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/body" /> →18

<EditText android:id="@+id/body"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:minLines="5"

android:scrollbars="vertical"

android:gravity="top" /> →24

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/date" /> →27

<Button

android:id="@+id/reminder_date"

android:layout_height="wrap_content"

android:layout_width="wrap_content"/> →31

<TextView android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/time" /> →34

<Button

android:id="@+id/reminder_time"

android:layout_height="wrap_content"

android:layout_width="wrap_content" /> →38

<Button   android:id="@+id/confirm"

android:text="@string/confirm"

android:layout_width="wrap_content"

android:layout_height="wrap_content" /> →42

</LinearLayout>

</ScrollView>

Listing 9.8: Die Datei reminder_edit.xml

Der Code aus Listing 9.8 wird nachfolgend kurz erläutert:

→5: Die View stammt von einer ScrollView ab, die einen Rollbalken erzeugt und bei der durch die angezeigte Liste geblättert werden kann, wenn deren Inhalte nicht mehr auf eine einzige Bildschirmseite passen. Der Bildschirm befindet sich in Abbildung 9.2 im Hochkantmodus. Wenn das Gerät aber um 90 Grad gedreht wird, kippt die Darstellung und mehr als die Hälfte der Anzeige wird abgeschnitten. Bei ScrollView kann auch diese Darstellung auf dem Bildschirm verschoben werden. Daher können Benutzer mit dem Finger auf dem Bildschirm nach oben streichen, um die angezeigten Inhalte nach oben zu verschieben und die übrigen Elemente der View zu betrachten.

→6: Eine ScrollView kann nur ein Kind haben, bei dem es sich hier um das Hauptlayout LinearLayout handelt, das die übrigen Layoutelemente enthält.

→7: Die Ausrichtung von LinearLayout wird auf vertical gesetzt, damit die Views innerhalb dieses Layouts übereinander angeordnet werden sollen.

→12: Die Beschriftung des title-Feldes

→15: Über EditText kann der Benutzer dem Termin einen Titel geben. Fügen Sie dafür die Anweisung <string name="title">Titel</string> zur Datei strings.xml hinzu.

→18: Die Beschriftung des body-Feldes. Fügen Sie die Anweisung <string name= "body">Beschreibung</string> zu strings.xml hinzu.

→24: Das EditText-Element zur Eingabe von ausführlicheren Daten zum Termin. Bei dieser View wurden die Eigenschaften minLines auf den Wert 5 und gravity auf den Wert top gesetzt. Das sorgt dafür, dass die EditText-View mindestens fünf Zeilen hoch ist und dass der eingegebene Text am oberen Rand der View ausgerichtet wird (gravity).

→27: Die Beschriftung des reminder_date-Feldes. Dafür wird eine String-Ressource verwendet, die Sie mit dem Namen "date" und dem Wert Erinnerungsdatum zur Datei strings.xml hinzufügen müssen.

→31: Die Schaltfläche reminder_date. Wenn sie angeklickt wird, wird ein DatePickerDialog angezeigt, über den der Benutzer ein Datum in einem integrierten Android-Steuerelement auswählen kann. Nach Auswahl des Datums im DatePicker wird dieses als Schaltflächentext übernommen.

→34: Die Beschriftung des reminder_time-Feldes. Dafür wird eine String-Ressource verwendet, die Sie mit dem Namen "time" und dem Wert Uhrzeit in die Datei strings.xml einfügen müssen.

→38: Wenn die Schaltfläche time_reminder angeklickt wird, wird ein TimePicker angezeigt, über den der Benutzer eine Uhrzeit in einem integrierten Android-Steuerelement auswählen kann. Nach Auswahl der Uhrzeit im DatePicker wird sie als Schaltflächentext übernommen.

→42: Über diese Bestätigungsschaltfläche werden die Formularwerte gespeichert, wenn der Benutzer sie anklickt. Für den Schaltflächentext fügen Sie die Anweisung <string name="confirm">Speichern</string> in die Datei strings.xml ein.

Ihr erstes ListFragment fertigstellen

Die Klasse ListFragment zeigt eine Liste von Elementen an, die einer Datenquelle wie einem Array oder Cursor entstammen, und stellt Callback-Methoden bereit, wenn der Benutzer ein Element auswählt. Um eine Liste von Elementen anzeigen zu können, müssen Sie jedoch ein Layout erstellen, um darin das Aussehen der einzelnen Zeilen zu definieren.

Ein Cursor ermöglicht wahlfreie Lese- und Schreibzugriffe auf eine von einer Datenbankabfrage zurückgegebene Ergebnismenge.

Fügen Sie über NEW:OTHER:ANDROID XML FILE ein neues Layout zum Ordner res/layout hinzu. Wählen Sie im Dialogfeld NEW ANDROID XML FILE als ROOT ELEMENT die Option TextView und geben Sie der Layoutdatei den Namen reminder_row.xml. Fügen Sie in diese Datei den Code aus Listing 9.9 ein.

<?xml version="1.0" encoding="utf−8"?>

<TextView

xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/text1" →4

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:padding="10dp"/>

Listing 9.9: Die Datei reminder_row.xml

Dieser Code definiert einfach eine Zeile, in der Textwerte mit einem Zwischenraum von zehn geräteunabhängigen Pixeln (dp – density-independent pixels) angezeigt werden können. In Zeile 4 wird die ID der View definiert, auf die beim Füllen der Liste mit Daten verwiesen wird.

Die eben hinzugefügte View ist interner Bestandteil des Android-Systems. In der Android-Dokumentation finden Sie bei Android.R.layout unter simple_ list_item_1 praktisch dieselbe XML-Definition. Den entsprechenden Quelltext finden Sie unter der Adresse https://github.com/android/platform_frameworks_base/blob/master/core/res/res/layout/simple_list_item_1.xml.

ListFragment muss über eine Schnittstelle (adapter) mit Daten versorgt werden. Hier stehen verschiedene Alternativen zur Auswahl. Da aber bisher noch keine Datenbasis erstellt wurde (das geschieht erst in Kapitel 12 über eine SQLite-Datenbank), können Sie hier erst einmal vorübergehend ein paar Testdaten anlegen, die in der Liste angezeigt werden können. Im folgenden Abschnitt werden ein paar beliebige Testdaten erzeugt, um den Adapter von ListFragment mit einem Aufruf von setListAdapter() festlegen zu können.

Ein Rumpfgerüst mit falschen Daten

Fügen Sie das folgende Feld und die Methode in die Klasse ReminderListFragment ein:

private ListAdapter mAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

String[] items = new String[] { "Foo", "Bar", "Fizz", "Bin" };  →7

mAdapter = new ArrayAdapter<String>(getActivity(), →9

R.layout.reminder_row, R.id.text1, items);

setListAdapter(mAdapter); →11

}

Listing 9.10:Die Testdaten in der Datei ReminderListActivity.java

Der Code lässt sich wie folgt kurz erläutern:

→7: Es wird ein Array mit String-Elementen erstellt, die später zu Testzwecken in der Liste angezeigt werden sollen.

→9: Hier wird ein neuer ArrayAdapter mit String-Typen erstellt. Dieser verwaltet in einer ListView eine unbestimmte Anzahl beliebiger Objekte, bei denen es sich hier um einen einfachen String-Array handelt. Der Code verwendet die Java-Schreibweise, bei der Entwickler angeben können, mit welchem Typ von Objekten der ArrayAdapter arbeiten soll. Der Konstruktor von ArrayAdapter enthält diese Elemente:

• getActivity(): Der aktuelle Kontext. (Da es sich bei dieser Aktivität um eine Implementierung der Context-Klasse handelt, können Sie die aktuelle Instanz als Kontext verwenden.)

• R.layout.reminder_row: Das Layout, das für die Zeilen der ListView verwendet werden soll.

• R.id.text1: Die ID der TextView in R.layout.reminder_row, in die die Werte aus dem Array übernommen werden sollen.

• items: Der Array mit den Strings, die in ListView übernommen werden sollen.

→11: Der Aufruf von setListAdapter() teilt ListFragment mit, wie die ListView mit Daten gefüllt werden soll. Hier verwende ich zu diesem Zweck den in Zeile 9 erzeugten ArrayAdapter.

Sofern Sie alle bis hierher benötigten und im Text erwähnten String-Werte in der Datei strings.xml definiert und bei den Fehlermeldungen die erforderlichen import-Anweisungen eingefügt haben, sollten Sie die Anwendung nun starten können.

Wenn die Eclipse-IDE hier irgendwelche Fehlermeldungen anzeigt, sollten sich diese eigentlich auf fehlende import-Anweisungen oder Definitionen in der Datei strings.xml beziehen, deren bisheriger Inhalt in Listing 9.11 dargestellt wird.

<?xml version="1.0" encoding="utf−8"?>

<resources>

<string name="app_name">Terminplaner</string>

<string name="menu_settings">Einstellungen</string>

<string name="no_reminders">Es liegen keine Termine vor</string>

<string name="title">Titel</string>

<string name="body">Beschreibung</string>

<string name="date">Erinnerungsdatum</string>

<string name="time">Uhrzeit</string>

<string name="confirm">Speichern</string>

</resources>

Listing 9.11: Die bisher benötigten String-Ressourcen in der Datei strings.xml

Die Zeile mit dem String menu_settings wurde automatisch von der IDE erstellt und hier bisher noch nicht behandelt.

Wählen Sie dazu RUN:RUN oder drücken Sie [Strg]+[F11]. Warten Sie ein wenig, bis Ihre App im Emulator läuft. Das Aussehen der App sollte momentan Abbildung 9.3 entsprechen.

Abbildung 9.3: Der mit den Testdaten laufende Terminplaner

Der eben vorgestellte Code und das Beispiel zeigen, wie Sie eine statische Datenquelle für ListFragment verwenden können. In Kapitel 12 werde ich diesen Code entfernen und Daten aus einer SQLite-Datenbank übernehmen.

Verarbeitung von Klick-Ereignissen

Die Listenelemente können vom Benutzer angeklickt werden und liefern entsprechende Klick-Ereignisse, die verarbeitet werden können. Android-View-Objekte reagieren im Wesentlichen auf zwei Klick-Ereignisse:

Klick (Kurz-Druck): Der Benutzer tippt ein Steuerelement an (zum Beispiel eine Schaltfläche).

Lang-Klick (Lang-Druck): Der Benutzer drückt seinen Finger länger auf ein Steuerelement.

Alle Views und/oder Aktivitäten können diese Ereignisse über verschiedene Methoden auswerten. Im folgenden Abschnitt werde ich Ihnen zeigen, wie Sie auf die verschiedenen Ereignisse in einer ListFragment reagieren können. Wie Klick-Ereignisse bei Schaltflächen verarbeitet werden können, erfahren Sie in Kapitel 11.

Kurze Klicks

ListFragment nimmt Ihnen in Android eine Menge Schwerarbeit ab. Fügen Sie zur Verarbeitung kurzer Klicks zur Klasse ReminderListFragment hinter onCreate() die folgende Methode hinzu:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {

super.onListItemClick(l, v, position, id);

}

Dieser Code überschreibt die von ListFragment bereitgestellte Standardimplementierung von onListItemClick(). Diese Methode wird beim Anklicken eines Listenelements aufgerufen. Dabei werden ihr die folgenden Parameter übergeben:

l: Die ListView, bei der das Ereignis aufgetreten ist
v: Das in der ListView angeklickte Element
position: Die Position des angeklickten Elements in der Liste
id: Die ID der Zeile des angeklickten Elements

Über diese Variablen können Sie ermitteln, welches Element angeklickt wurde, und auf dieser Basis entsprechende Maßnahmen ergreifen. Wird ein Element in der Liste angeklickt, startet ein Intent ReminderEditActivity, damit das Element bearbeitet werden kann. Damit befasst sich der Abschnitt »Neue Aktivitäten mit Intents starten« weiter hinten in diesem Kapitel.

Lange Klicks

Ein langer Klick oder ein Lang-Druck tritt auf, wenn der Benutzer längere Zeit auf ein Steuerelement drückt. Um das Ereignis des Lang-Drucks auf Listenelemente in einer ListFragment zu verarbeiten, fügen Sie in der Klasse ReminderListFragment die folgende Codezeile am Ende der Methode onActivityCreated() hinzu:

registerForContextMenu(getListView());

Die äußere Methode registerForContextMenu() ist für die Registrierung eines Kontextmenüs zuständig, das für eine bestimmte View angezeigt wird. Mehrere Views können Kontextmenüs anzeigen. Damit lassen sich auch für alle Listenelemente jeweils eigene Kontextmenüs erzeugen. Der Aufruf registerForContextMenu() übernimmt ein View-Objekt als Parameter, über das die ListFragment registriert wird, um Kontextmenüs erzeugen zu können. Die innere Methode getListView() gibt ein ListView-Objekt zurück, das bei der Registrierung benutzt wird. Der Aufruf getListView() ist ein Mitglied der Klasse ListFragment.

Nach dem Registrieren der ListView darf sie ein Kontextmenü erzeugen und Sie müssen dafür sorgen, dass sie jederzeit auf lange Klicks reagiert. Wenn ein solches Ereignis eintritt, erkennt registerForContextMenu() dies und ruft zum Erzeugen des Kontextmenüs die Methode onCreateContextMenu() auf. In dieser Methode können Sie Ihr Kontextmenü konfigurieren.

Fügen Sie am Ende der Klassendatei ReminderListFragment die folgende Methode hinzu:

@Override
public void onCreateContextMenu(ContextMenu menu, View v,  ContextMenuInfo menuInfo) {

super.onCreateContextMenu(menu, v, menuInfo);

}

Die Methode onCreateContextMenu() wird mit den folgenden Parametern aufgerufen:

menu: Das zu erstellende Kontextmenü.

v: Das Steuerelement, auf das länger gedrückt wird und für das das Kontextmenü erstellt wird.

menuInfo: Zusätzliche Informationen zum Element, für das das Kontextmenü angezeigt werden soll. Diese können je nach Art der im Parameter v angegebenen View variieren.

Innerhalb dieser Methode können Sie das dem Benutzer angezeigte Kontextmenü ändern. Wenn Benutzer beispielsweise länger auf ein Element in der Terminliste drücken, sollen sie es entfernen können. Daher muss ihnen die Option ENTFERNEN im Kontextmenü angeboten werden. Dafür wird in Kapitel 10 gesorgt werden.

Intents identifizieren

Selbst wenn Apps nur aus zwei Bildschirmen bestehen (wie auch die Terminplaner-Anwendung), passiert doch eine Menge im Hintergrund. Erwähnenswert sind dabei insbesondere variierende Bildschirme, die dem Benutzer angezeigt werden, wenn er verschiedene Funktionen der Anwendung nutzt. Bei allen umfangreicheren Apps können Benutzer die verschiedenen Bildschirme unabhängig voneinander nutzen. Die entscheidende Frage lautet daher, wie Benutzer andere Bildschirme aktivieren können.

Die Interaktionen mit dem Benutzer werden über das Intent-System von Android verarbeitet. Das wurde zwar bereits in Kapitel 7 ausführlicher behandelt, dabei habe ich aber nicht dargestellt, wie unter Verwendung eines Intents von einem zu einem anderen Bildschirm gewechselt werden kann. Glücklicherweise handelt es sich dabei um einen recht einfachen Prozess. (Mehr über das Intent-System erfahren Sie in Kapitel 7.)

Neue Aktivitäten mit Intents starten

Aktivitäten werden über das Intent-Framework von Android gestartet. Ein Intent ist eine Klasse, die einer Nachricht entspricht, die an das Intent-System von Android übergeben wird. Die ganze Arbeitsweise ähnelt einer Architektur mit einem Nachrichtenbus. Wenn eine Aktivität einen Intent verarbeiten kann, informiert sie die Android-Plattform darüber, was entweder dazu führt, dass sie direkt gestartet wird oder dass dem Benutzer eine Liste mit Anwendungen angezeigt wird, aus der er eine auswählen kann. (Die entsprechende Auswahlliste wird Chooser genannt und gleich eingehender beschrieben.) Einen Intent können Sie sich recht gut als abstrakte Beschreibung einer Operation vorstellen.

Bestimmte Aktivitäten lassen sich leicht starten. Fügen Sie in ReminderListFragment den nachfolgend fett dargestellten Code zur Methode onListItemClick() hinzu:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {

super.onListItemClick(l, v, position, id);

Intent i = new Intent(getActivity(), ReminderEditActivity.class);  →4

i.putExtra(ReminderProvider.COLUMN_ROWID, id); →5

startActivity(i); →6

}

Nachfolgend werden die einzelnen Zeilen kurz erläutert:

→4: Hier wird über den Konstruktor Intent ein neuer Intent erzeugt, der neben dem aktuellen Kontext, der sich in this (der aktuell laufenden Aktivität) befindet, eine Klasse (hier ReminderEditActivity) übernimmt, die das Intent-System starten soll.

→5: Diese Zeile fügt einige zusätzliche Daten in das Intent-Objekt ein. Hier handelt es sich dabei um ein Schlüssel/Wert-Paar. Der Schlüssel ist RowId und beim Wert handelt es sich um die ID der angeklickten View. Der Wert wird in den Intent eingefügt, damit die Empfängeraktivität (ReminderEditActivity) die Daten übernehmen und weiter nutzen kann. Da ich momentan nur Testdaten verwende, wird noch nichts angezeigt. Nach Kapitel 12 werden Sie jedoch sehen können, wie Daten in ReminderEditFragment übernommen werden.

→6: Diese Zeile startet die Aktivität aus der aktuellen Aktivität heraus. Dieser Aufruf übergibt die Intent-Nachricht an das Intent-System von Android und sorgt dafür, dass Android feststellen kann, wie dieser Bildschirm dem Benutzer angezeigt werden soll.

Einen Chooser erstellen

Früher oder später werden Sie als Entwickler dem Benutzer eine Liste von Anwendungen präsentieren müssen, die einen bestimmten Intent verarbeiten können. Ein gängiges Beispiel wäre die Weitergabe von Daten an einen Freund über Netzwerkanwendungen wie etwa E-Mail, SMS, Twitter, Facebook oder Google Latitude.

Das Intent-System von Android wurde für den Umgang mit derartigen Situationen entwickelt. Auch wenn diese Funktion in der Terminplaner-Anwendung nicht benutzt wird, behandele ich sie hier, weil sie häufiger sehr praktisch sein kann. Listing 9.12 enthält den Code zur Anzeige mehrerer verfügbarer Optionen für den Benutzer, den Sie in die Methode onCreateContextMenu() einfügen können.

Intent i = new Intent(Intent.ACTION_SEND); →1
i.setType("text/plain"); →2
i.putExtra(Intent.EXTRA_TEXT, "Hallo, wie geht‘s!"); →3
i.putExtra(Intent.EXTRA_SUBJECT, "Mein Thema "); →4

Intent chooser = Intent.createChooser(i, "Wer soll das verarbeiten?");  →5
startActivity(chooser); →6

Listing 9.12: Einen Intent-Chooser erzeugen

Die verschiedenen Zeilen in Listing 9.12 lassen sich kurz wie folgt beschreiben:

→1: Hier wird ein neuer Intent erzeugt, der das Intent-System darüber informiert, dass der Benutzer etwas versenden oder verschicken will.

→2: Der Content-Typ der Mitteilung. Dieser kann auf einen bestimmten MIME-Typ gesetzt werden. Bei MIME-Typen müssen Sie im Unterschied zu RFC-MIME-Typen die genaue Schreibweise beachten und sie sollten immer in Kleinbuchstaben angegeben werden. Damit legen Sie den Typ des Intents fest. Nur Anwendungen, die diese Art von Intent verarbeiten können, werden im Auswahlmenü (dem Chooser) angezeigt.

→3: Zusätzliche Daten in den Intent einfügen. EXTRA_TEXT wird beispielsweise als Mitteilungstext verwendet, wenn der Intent von einer E-Mail-Anwendung weiterverarbeitet wird. Wenn Twitter ausgewählt wird, handelt es sich dabei um den Text des Tweets. Alle Anwendungen, die auf den Intent reagieren, können die zusätzlichen Daten (im Beispiel EXTRA_TEXT und EXTRA_SUBJECT) auf die ihnen eigene Weise verarbeiten. Sie sollten nicht davon ausgehen, dass die Zielanwendung die zusätzlichen Daten wie erwartet behandelt. Welche wofür verwendet werden, bestimmen letztlich die Entwickler der jeweiligen Zielanwendungen.

→4: Ähnlich wie Zeile 3, nur dass hier EXTRA_SUBJECT zusätzlich übertragen wird. Wenn die Daten von einem E-Mail-Client übernommen werden, wird EXTRA_SUBJECT typischerweise für die Betreffzeile der E-Mail verwendet.

→5: Der Chooser wird erzeugt. Das Intent-Objekt besitzt eine statische Hilfsmethode, die sich um das Erzeugen des Choosers kümmert. Der Chooser ist selbst wieder ein Intent. Sie müssen lediglich den Ziel-Intent (was geschehen soll) und einen Titel für den anzuzeigenden Chooser angeben.

→6: Der Intent wird gestartet. Damit wird der Chooser angezeigt, in dem eine verfügbare Anwendung ausgewählt werden kann.

Das über Listing 9.12 erzeugte Auswahlmenü wird in Abbildung 9.4 dargestellt. Welche Apps darin zur Auswahl stehen, hängt davon ab, welche Anwendungen auf dem jeweiligen Emulator oder Gerät installiert (und gegebenenfalls konfiguriert!) sind.

Abbildung 9.4: Ein neuer Chooser wurde erzeugt.

Wenn das Intent-System keine Anwendungen finden kann, die den Intent zu verarbeiten wissen, wird der Chooser mit einer Meldung angezeigt, die den Benutzer darüber informiert, dass es keine passende Anwendung gibt, die die angeforderte Aktion ausführen kann (Abbildung 9.5).

Abbildung 9.5: Ein Chooser informiert den Benutzer darüber, dass Android keine passende Anwendung zur Verarbeitung des Intents finden kann.

Gibt es nur eine einzige Anwendung, die die angeforderte Aktion durchführen kann, wird diese sofort gestartet, ohne dass ein Auswahlmenü angezeigt wird. (Das könnte Ihnen im Emulator passieren.)

Über Chooser können Sie Ihren Anwendungen Möglichkeiten der Zusammenarbeit mit anderen Apps erschließen. Wenn Sie allerdings einfach nur startActivity() aufrufen würden, ohne einen Chooser zu erzeugen, könnte Ihre Anwendung abstürzen, da Android Ihnen alle Freiheiten lässt und annimmt, dass Sie wissen, was Sie tun. Wenn Sie keinen Chooser einbeziehen, gehen Sie davon aus, dass es auf dem Zielgerät wirklich mindestens eine Anwendung gibt, die mit dem Intent umzugehen weiß. Sollte das nicht der Fall sein, kommt es zu einem Ausnahmefehler (der in der DDMS-Perspektive angezeigt wird), der darüber informiert, dass es keine Klasse gibt, die etwas mit dem Intent anzufangen weiß. Für den Benutzer ist Ihre App damit abgestürzt.

Um Benutzer nicht unnötig zu vergraulen, sollten Sie immer für einen Intent-Chooser sorgen, wenn Sie Intents starten, die mit anderen Anwendungen zusammenarbeiten sollen. Damit vermeiden Sie mögliche Probleme und bleiben mit dem von Android bereits verwendeten Anwendungsmodell konsistent.

10

Menüs gestalten

In diesem Kapitel

Gute von schlechten Menüs unterscheiden

Ein Menü mit Optionen erstellen

Ein Kontextmenü erstellen

Alle guten Android-Apps umfassen Menüs. Wenn Sie ein Android-Gerät besitzen und einige Anwendungen aus dem Google Play Store heruntergeladen haben, werden Ihnen bestimmt schon einige schlechte Menüimplementierungen aufgefallen sein.

Schlecht implementierte Menüs liefern wenig oder gar keinen hilfreichen Text in ihrer Beschreibung und keine Symbole. Zu den häufigeren Fehlern bei Menüs zählen:

Schlechte Menütitel

Menüs ohne Symbole

Fehlende Menüs

Gute Menüs sollten für Endbenutzer visuell und textlich ansprechend gestaltet sein. Das Aussehen des Menüsymbols liefert Hinweise darauf, wie intensiv sich der Entwickler Gedanken bei der Erstellung des Menüs und der Auswahl der am besten zu seiner Anwendung passenden Symbole gemacht hat.

Sowohl Aktivitäten als auch Fragmente können Menüs umfassen, die dann zu einem einzigen zusammengefasst werden. In diesem Kapitel werden Sie die Fragmente der Terminplaner-App um Auswahl- und Kontextmenüs erweitern. Sie könnten sie aber genauso gut auch zu einer Activity hinzufügen.

Grundlagen von Auswahl- und Kontextmenüs

Android stellt einfache Mechanismen bereit, mit denen Sie Ihren Apps Menüs hinzufügen können. Folgende Arten von Menüs werden unterstützt:

Auswahlmenü: Mit Auswahlmenüs werden Sie am häufigsten arbeiten, weil es sich dabei um die primären Menüs von Aktivitäten und Fragmenten handelt.

Unter Android ab Version 3.x befindet sich das Auswahlmenü in der Aktionsleiste am oberen Bildschirmrand (mehr dazu erfahren Sie in Kapitel 1). Unter Android 2.x und älteren Versionen wird Benutzern das Auswahlmenü beim Drücken der Menütaste oder des Menüsymbols des Geräts angezeigt. Abbildung 10.1 und Abbildung 10.2 zeigen die beiden Varianten unter Android 2.x und Android 4.x auf zwei verschiedenen Geräten.

Abbildung 10.1: Das Auswahlmenü am unteren Bildschirmrand im Google Play Store unter Android 2.x

Abbildung 10.2: Das aufgeschlagene Auswahlmenü rechts oben in der Aktionsleiste im Google Play Store unter Android 4.x

Auswahlmenüs fallen in zwei Gruppen:

• Symbolmenü: Dabei handelt es sich um die direkt in der ersten Ebene angebotenen Menüoptionen. Wie Sie in Abbildung 10.1 anhand des Auswahlmenüs am unteren Bildschirmrand sehen können, bietet das Gerät hier maximal Platz für sechs Menüelemente. Nur für diese können Symbole verwendet werden. Kontrollkästchen oder Optionsfelder werden darin nicht unterstützt. (Das Auswahlmenü in der Aktionsleiste ab Android 3.0 können Sie flexibler gestalten.)

• Erweitertes Menü: Werden mehr als sechs Menüelemente verwendet, wird ein erweitertes Menü erzeugt, in dem das sechste und alle weiteren Menüelemente untergebracht werden, für die der Platz im Symbolmenü fehlt. Für dieses Menü wird dann automatisch das Menüsymbol MEHR (MORE) eingeblendet.

Kontextmenü: Eine schwebende Liste mit Menüelementen, die bei einem Lang-Druck auf ein Steuerelement angezeigt wird

Untermenü: Eine schwebende Liste mit Menüelementen, die dem Benutzer angezeigt wird, wenn er ein Element in einem Auswahl- oder Kontextmenü anklickt. Verschachtelte Untermenüs werden nicht unterstützt.

In diesem Kapitel werden Sie ein Auswahlmenü und ein Kontextmenü erstellen.

Sollten Sie ein wenig die Orientierung verlieren, können Sie sich die vollständigen Quelltexte von der Website des Buches herunterladen.

Ihr erstes Menü erstellen

Sie können Menüs im Code oder über XML-Dateien erstellen, die Sie im Ordner res/menu ablegen. Dabei sollten Sie der Vorgehensweise mit XML-Dateien den Vorzug geben und die Menüs dann in ein Programmobjekt übernehmen, mit dem der Benutzer interagieren kann. Dadurch lässt sich die Menüdefinition besser vom eigentlichen Anwendungscode trennen.

Die XML-Datei erstellen

Führen Sie die folgenden Schritte aus, um ein XML-Menü zu definieren:

1. Erstellen und/oder aktivieren Sie im Ordner res das Unterverzeichnis menu.

2. Fügen Sie eine Datei namens list_menu.xml zu diesem Verzeichnis hinzu.

Wählen Sie dazu NEW:(OTHER):ANDROID XML FILE und tragen Sie im Feld FILE den Dateinamen list_menu.xml ein. Lassen Sie MENU als Typ der zu erstellenden Ressource markiert und klicken Sie FINISH an.

3. Geben Sie den folgenden Code in die Datei list_menu.xml ein.

<?xml version="1.0" encoding="utf−8"?>

<menu

xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/menu_insert"

android:icon="@android:drawable/ic_menu_add"

android:title="@string/menu_insert" />

</menu>

Beachten Sie, dass eine neue String-Ressource hinzugekommen ist, die Sie noch erstellen müssen. (Das wird in Schritt 4 erledigt.) Beim Wert von android:icon handelt es sich um ein internes Android-Symbol. Die zugehörige Bitmap müssen Sie nicht selbst als Bildressource erstellen. Die Android-Plattform stellt das Symbol für die verschiedenen Auflösungen (hdpi, ldpi, mdpi und xhdpi) bereit. Wenn Sie sich über die anderen verfügbaren Ressourcen informieren wollen, finden Sie die Dokumentation zu android.R.drawable unter der Adresse http://developer.android.com/ reference/android/R.drawable.html.

Alle Ressourcen der Klasse android.R können Sie in Ihren Anwendungen nutzen. Sie sollten sie zudem bevorzugt verwenden, weil sie Ihrer Anwendung damit ein dem Benutzer bereits vertrautes Aussehen verleihen, das mit der übrigen Android-Benutzeroberfläche konsistent ist.

4. Erstellen Sie eine neue String-Ressource namens menu_insert mit dem Wert Termin  hinzufügen in der Ressourcendatei strings.xml.

5. Erweitern Sie in der Klasse ReminderListFragment die Methode onActivityCreated() wie folgt:

@Override
public void onActivityCreated(Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

setEmptyText(getResources().getString(string.no_reminders));

registerForContextMenu(getListView());

setHasOptionsMenu(true);

}

registerForContextMenu() teilt Android mit, dass ListView Einträge zum Kontextmenü (das beim Lang-Druck auf ein Bildschirmelement angezeigt wird) hinzufügen will. setHasOptionsMenu() sorgt dafür, dass Android 2.x das Menü beim Antippen der Menü-Schaltfläche anzeigt, während es unter Android ab 3.0 in der Aktionsleiste angezeigt wird. In Kapitel 9 finden Sie ausführlichere Informationen zu register ForContextMenu() und setHasOptionsMenu().

6. Fügen Sie die Methode onCreateOptionsMenu() zu Ihrer Klasse hinzu:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {

super.onCreateOptionsMenu(menu, inflater);

inflater.inflate(R.menu.list_menu, menu);

}

MenuInflater übernimmt das vorhin erstellte XML-Menülayout und fügt es zu dem Menü hinzu, das als Argument im Methodenaufruf übergeben wurde.

Fügen Sie bei Bedarf jeweils die zusätzlich benötigten import-Anweisungen ein. Um sich die Arbeit zu erleichtern, können Sie dazu im Kontextmenü Ihres Projekts auch der Einfachheit halber SOURCE:ORGANIZE IMPORTS wählen.

7. Installieren Sie die Anwendung im Emulator und klicken Sie die Schaltfläche MENU an.

Auf einem Gerät wird damit ein Menü wie in Abbildung 10.3 erstellt. (Wundern Sie sich nicht über den von der verwendeten Entwicklungsumgebung automatisch erzeugten zweiten Menüeintrag.)

Abbildung 10.3: Das Menüsymbol in der Terminplaner-App unter Android 2.x

Umgang mit Benutzeraktionen

Das Menü wurde erstellt und nun soll auch etwas geschehen, wenn es angeklickt wird. Fügen Sie dazu den folgenden Code am Ende der Klassendatei ReminderListFragment an:

@Override
public boolean onOptionsItemSelected(MenuItem item) { →2

switch (item.getItemId()) { →3

case R.id.menu_insert: →4

editReminder(0); →5

return true; →6

}

return super.onOptionsItemSelected(item); →9

}

Die einzelnen Codezeilen werden nachfolgend erläutert:

→2: Diese Methode wird bei der Auswahl eines Menüelements aufgerufen. Der Parameter item gibt an, welches Menüelement vom Benutzer angetippt wurde.

→3: Um festzustellen, mit welchem Element Sie arbeiten, vergleichen Sie die ID der Menüelemente mit den bekannten vorhandenen Menüelementen. Dabei wird eine switchAnweisung verwendet, um alle möglichen Fälle zu prüfen. Die Menü-ID erhalten Sie über die MenuItem-Methode getItemId().

→4: Über die ID des Menüelements wird geprüft, ob Benutzer dieses ausgewählt haben.

→5: Wenn das Menüelement TERMIN HINZUFÜGEN angeklickt wurde, soll die Anwendung über die Methode editReminder() (wird im folgenden Abschnitt definiert) einen Termin erstellen. Dabei erzeugt die App laut Konvention beim Aufruf von editReminder() mit dem ID-Wert 0 einen neuen Erinnerungstermin.

→6: Diese Zeile gibt true zurück, um der Methode onOptionsItemSelected() mitzuteilen, dass eine Menüauswahl verarbeitet wurde.

→9: Wenn die Menüauswahl bisher noch nicht verarbeitet und der Rückgabewert noch nicht gesetzt wurde, versucht die Elternklasse, das Menüelement zu verarbeiten.

Momentan erhalten Sie noch Fehler beim Kompilieren, was Sie aber nicht weiter stören sollte! Diese werden im nächsten Abschnitt behoben.

Einen Erinnerungstermin erstellen

Über die Methode editReminder() können Benutzer zu ReminderEditActivity navigieren, um einen neuen Task für einen Erinnerungstermin zu erstellen.

Fügen Sie die folgende Methode am Ende Ihrer Klassendatei ReminderListFragment hinzu:

public void editReminder(long id) {

Intent i = new Intent(getActivity(), ReminderEditActivity.class);

i.putExtra(ReminderProvider.COLUMN_ROWID, id);

startActivity(i);

}

Dieser Code erzeugt einen neuen Intent, der ReminderEditActivity startet. Diese Methode ruft dann startActivity() auf, um … die Activity zu starten.

Ein Kontextmenü erstellen

Kontextmenüs werden beim Lang-Druck auf ein Steuerelement angezeigt. Kontextmenüs schweben über der aktuellen Aktivität und ermöglichen Benutzern die Auswahl einer von mehreren Optionen.

Glücklicherweise ähnelt die Erstellung von Kontextmenüs der von Auswahlmenüs. Das Menü lässt sich über eine XML-Datei definieren und dann mit denselben Verfahren übernehmen, die auch bei der Erstellung von Auswahlmenüs zum Einsatz kommen. Daher können wir gleich loslegen. Um ein Kontextmenü zu erstellen, müssen Sie nur registerForContextMenu() mit einer View als Ziel aufrufen. Anschließend müssen Sie den Aufruf onCreateContextMenu() überschreiben. Beides wurde bereits in Kapitel 9 dargestellt.

Im Terminplaner müssen nicht mehr benötigte Termine entfernt werden können. Wenn Benutzer länger auf einen in der Liste angezeigten Termin drücken, wird ein Kontextmenü angezeigt, in dem sie eine entsprechende Option auswählen und einzelne Termine entfernen können.

Die XML-Datei für das Menü erstellen

Für dieses Menü erstellen Sie im Ordner res/menu eine neue XML-Datei. Nennen Sie sie list_menu_item_longpress.xml. Geben Sie den folgenden Code in die XML-Datei ein:

<?xml version="1.0" encoding="utf−8"?>

<menu
xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/menu_delete"

android:title="@string/menu_delete" />

</menu>

Beachten Sie, dass die Eigenschaft title die neue String-Ressource menu_delete verwendet. Sie müssen daher in der Datei strings.xml eine neue String-Ressource mit dem Namen menu_delete und dem Wert Termin entfernen erstellen. Beachten Sie auch, dass Sie diesem Menü kein Symbol zuordnen müssen.

Kontextmenüs unterstützen keine Symbole, da es sich bei ihnen einfach nur um eine Liste von Menüoptionen handelt, die über der aktuellen Aktivität schwebend angezeigt werden.

Das Menü laden

Um das Menü zu laden und dem Benutzer anzeigen zu können, fügen Sie in ReminderListFragment.java den folgenden Code für die Methode onCreateContextMenu() ein:

@Override
public void onCreateContextMenu(ContextMenu menu, View v,

ContextMenuInfo menuInfo) {

super.onCreateContextMenu(menu, v, menuInfo);

MenuInflater mi = getActivity().getMenuInflater();

mi.inflate(R.menu.list_menu_item_longpress, menu);

}

Dieser Code erfüllt dieselbe Funktion wie der Aufruf von onCreateOptionsMenu(), allerdings binden Sie hier kein Auswahlmenü, sondern das definierte Kontextmenü ein. Wenn Sie nun länger auf ein Element in der Liste drücken, wird das in Abbildung 10.4 dargestellte Kontextmenü angezeigt.

Abbildung 10.4: Das Kontextmenü in der Terminplaner-App

Verarbeitung der Benutzerauswahl

Die Verarbeitung der Auswahl dieser Menüelemente ähnelt der bei einem Auswahlmenü. Fügen Sie dazu den folgenden Code am Ende Ihrer Klassendatei ReminderListFragement hinzu:

@Override
public boolean onContextItemSelected(MenuItem item) →2

switch(item.getItemId()) { →3

case R.id.menu_delete: →4

// Termin entfernen

return true;

}

return super.onContextItemSelected(item);

}

Die Codezeilen werden nachfolgend erläutert:

→2: Diese Methode wird aufgerufen, wenn ein Kontextmenüelement ausgewählt wird. Beim Parameter item handelt es sich um das im Kontextmenü ausgewählte Element.

→3: Über die in der Datei list_menu_item_longpress.xml festgelegte ID ermittelt eine switch-Anweisung, welches Element angeklickt wurde.

→4: Hierbei handelt es sich um die ID der Schaltfläche menu_delete in der Datei list_menu_item_longpress.xml. Wenn diese Menüoption ausgewählt wird, würde der nachfolgende Code die entsprechenden Aktionen durchführen. Vorläufig passiert in diesem Codeblock noch weiter nichts, aber das wird sich in Kapitel 12 ändern, denn dort wird der markierte Termin aus der SQLite-Datenbank gelöscht.

Sie können viele verschiedene Kontextmenüelemente zur Datei list_menu_ item_longpress.xml hinzufügen und zwischen ihnen im Methodenaufruf onContextMenuItemSelected() umschalten, um jeweils andere Aktionen zu veranlassen.

11

Verarbeitung von Benutzereingaben

In diesem Kapitel

Mit EditText-Widgets arbeiten

DatePicker und TimePicker erzeugen

Alarm-Dialogfelder einrichten

Benutzereingaben prüfen

Es gibt nur wenige Anwendungen, mit denen Benutzer nicht über Eingabeschnittstellen interagieren können. Nutzer müssen auf die eine oder andere Weise Daten in Ihrer Anwendung eingeben können und verwenden dazu beispielsweise Text, Felder zur Datum- oder Zeitauswahl, Optionsschaltflächen oder Kontrollkästchen. Die Verallgemeinerung der Eingabe erstreckt sich auch auf Schaltflächen, das Streichen über den Bildschirm, das Drücken oder Antippen von Optionen, Menüs und verschiedene andere Optionen. Dieses Kapitel konzentriert sich einzig auf Benutzereingaben über Termineingaben, formatfreie Texte und die Datum- und Zeitauswahl.

Schnittstellen für Benutzereingaben erstellen

Bei der meistverwendeten Eingabevariante handelt es sich um die EditText-View, über die formatfreie Texte eingegeben werden können. Wenn eine EditText-View den Fokus besitzt, können Benutzer Texte über die eingeblendete Bildschirmtastatur oder eine möglicherweise angeschlossene Tastatur eingeben.

Das EditText-Element wird auch Texteingabefeld oder Textbox genannt.

Ein EditText-Widget erstellen

In Kapitel 9 wurde eine XML-Datei für das Layout einer View namens reminder_edit.xml erstellt, die den folgenden Code enthielt:

<EditText android:id="@+id/title"

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

Dieser Codeschnipsel definiert ein Texteingabefeld für den Titel des Termins. Darin können Benutzer Daten eingeben. Die EditText-View nimmt die gesamte Breite des Bildschirms in Anspruch und beschränkt sich auf die wirklich benötigte Höhe. Wird sie ausgewählt, zeigt Android automatisch die Bildschirmtastatur an, über die der Benutzer Daten eingeben kann.

Im Unterschied zum folgenden EditText-Beispiel, das ebenfalls in der Layoutdatei reminder_edit.xml erstellt wurde, handelt es sich beim letzten Beispiel um eine Minimalvariante. Das folgende Beispiel ist da schon ein wenig umfangreicher:

<EditText android:id="@+id/body" android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:minLines="5"

android:scrollbars="vertical"

android:gravity="top" />

Dieses Textfeld dient der Eingabe der ausführlicheren Terminbeschreibung. Breite (layout_ width) und Höhe (layout_height) wurden wie bei der vorherigen EditText-View gewählt, weshalb es ebenfalls die gesamte verfügbare Breite des Bildschirms einnimmt. Der Unterschied besteht bei dieser EditText-Definition in den nachfolgend kurz beschriebenen drei weiteren Eigenschaften:

minLines: Über diese Eigenschaft wird festgelegt, wie hoch die EditText-View sein soll. EditText ist eine Unterklasse des TextView-Objekts und verfügt daher über dieselbe gemeinsame Eigenschaft. Hier lege ich fest, dass das EditText-Objekt mindestens fünf Zeilen hoch sein soll. Schließlich soll das Feld ja längere Texteingaben aufnehmen, über die Termine ausführlicher beschrieben werden können.

Sie können dieses Feld mit dem für die Eingabe einer E-Mail-Nachricht vergleichen, die ja meist auch deutlich umfangreicher als deren Betreffangabe ist.

scrollbars: Über diese Eigenschaft wird festgelegt, welche Rollbalken angezeigt werden sollen, wenn die vorhandene Textmenge den im Texteingabefeld real verfügbaren Platz übersteigt. Hier gebe ich vertical an, damit dann seitlich in der EditText-View ein Rollbalken angezeigt wird.

gravity: Wenn der Benutzer ein EditText-Feld aktiviert, wird der Text standardmäßig vertikal mittig angezeigt (Abbildung 11.1). Das entspricht aber nicht den Erwartungen der Benutzer bei mehrzeiligen Eingaben. Normalerweise wird er erwarten, dass der Cursor oben in der EditText-View angezeigt wird. Dazu müssen Sie die Eigenschaft gravity der EditText-View auf "top" setzen.

Wenn beispielsweise eine japanische Bildschirmtastatur angezeigt wird, können Sie dies über die EINSTELLUNGEN FÜR SPRACHE UND TASTATUR ändern. Zudem finden Sie hier auch eine Option wie ANDROID-TASTATUR-EINSTELLUNGEN, über die Sie Optionen wie AUTOM. VERVOLLSTÄNDIGEN und/oder VORSCHLÄGE ANZEIGEN deaktivieren und Einstellungen für externe Tastaturen (hier geht es um die Unterstützung von Umlauten) vornehmen können.

Abbildung 11.1: EditText-View ohne gesetzte gravity-Eigenschaft zeigt den Text vertikal mittig im Eingabefeld an.

Anzeige einer Bildschirmtastatur

Die EditText-View ist höchst vielseitig und lässt sich auf vielerlei Weise konfigurieren. Sie ist dafür verantwortlich, wie die Bildschirmtastatur angezeigt wird. Da viele Geräte mit Touchscreen keine physische Tastatur besitzen, müssen Eingaben bei ihnen über eine Bildschirmtastatur erfolgen. Über eine der Eigenschaften der EditText-View lassen sich verschiedene Aspekte der Bildschirmtastatur anpassen.

Und warum sollte man das Aussehen der Bildschirmtastatur anpassen? Einfach deshalb, weil bei Eingaben in eine EditText-View für unterschiedliche Datentypen andere Tasten benötigt werden. Wenn in EditText beispielsweise eine Telefonnummer angezeigt wird, sollte die Bildschirmtastatur nur Ziffern anzeigen. Wenn es sich beim EditText-Wert um eine E-Mail-Adresse handelt, sollte die Bildschirmtastatur die darin gebräuchlichen Zeichen (wie das at-Symbol @) anzeigen.

Merken Sie sich hier einfach, dass es verschiedene Möglichkeiten zur Anpassung des Aussehens der Bildschirmtastatur gibt. Wenn Sie die Bildschirmtastatur geschickt anpassen, können Sie Ihre Anwendungen damit benutzerfreundlicher gestalten. Einige Hersteller nehmen allerdings eigene Anpassungen der Bildschirmtastatur vor, weshalb dabei auch ein wenig Vorsicht geboten ist. So finden Sie in der in Abbildung 11.1 eingeblendeten Bildschirmtastatur eines Samsung-Tablets beispielsweise gleichzeitig Buchstaben, Zahlen und unten links eine Taste, über die sich die Tastatureinstellungen direkt aufrufen lassen.

Programmgesteuert lassen sich die Merkmale der Bildschirmtastatur über die Eigenschaft inputType von EditText beeinflussen. Wenn Sie beispielsweise beim EditText-Element namens body die Zeile android:inputType="number" zusätzlich einfügen würden, wird eine für die Eingabe numerischer Zeichen angepasste Bildschirmtastatur eingeblendet. Diese kann beispielsweise wie in Abbildung 11.2 aussehen.

Abbildung 11.2: Für die Eingabe numerischer Zeichen umgeschaltete Bildschirmtastatur (ein physisches Tablet unter Android 2.2)

Die Vielzahl der verfügbaren Optionen würde hier den Rahmen dieses Buches deutlich sprengen. Sie finden aber eine vollständige Liste unter http://developer.android.com/ reference/android/widget/TextView.html#attr_android:inputType.

Auswahl von Datum und Uhrzeit

Eine Terminplaner-Anwendung, ohne Datum und Uhrzeit für die verschiedenen Erinnerungstermine festlegen zu können, wäre armselig. Ohne entsprechende Möglichkeiten ließe sich eine Terminplaner-App letztlich gar nicht nutzen! Es würde sich einfach nur um eine langweilige Aufgabenliste handeln.

Wenn Sie schon einmal in anderen Programmiersprachen mit Datum und Uhrzeit gearbeitet haben, wissen Sie vielleicht, dass es recht mühsam werden kann, wenn man derartige Eingabekomponenten erstellen will, ohne dabei auf vordefinierte Komponenten zurückgreifen zu können. Glücklicherweise haben Ihnen die Android-Programmierer diese Arbeit bereits weitgehend abgenommen. Die Android-Plattform stellt mit DatePicker und TimePicker zwei Klassen bereit, die Sie bei derartigen Aufgaben unterstützen. Und diese enthalten darüber hinaus auch Klassen, mit deren Hilfe Sie Dialogfelder zur Auswahl von Datum und/oder Uhrzeit einblenden können. Daher können Sie entweder DatePicker und/oder TimePicker in die Views Ihrer Anwendung einbinden oder DialogFragment-Klassen verwenden.

Auswahl-Schaltflächen erstellen

In reminder_edit.xml existieren bereits einige Zeilen, die letztlich der Anzeige von DatePicker und TimePicker dienen. Diese befinden sich hinter den bereits erläuterten EditText-Definitionen. Dabei handelt es sich um zwei Schaltflächen mit darüber angezeigten Titeln.

Den entsprechenden Code zeigt Listing 11.1.

<TextView android:layout_width="wrap_content" →1

android:layout_height="wrap_content"

android:text="@string/date" />

<Button →4

android:id="@+id/reminder_date"

android:layout_height="wrap_content"

android:layout_width="wrap_content"

/>

<TextView android:layout_width="wrap_content" →9

android:layout_height="wrap_content"

android:text="@string/time" />

<Button →12

android:id="@+id/reminder_time"

android:layout_height="wrap_content"

android:layout_width="wrap_content"

/>

Listing 11.1: Die Schaltflächen date und time mit den zugehörigen TextView-Beschriftungselementen

Der Code wird nachfolgend erläutert:

→1: Hierbei handelt es sich um das TextView-Label für die Datum-Schaltfläche. Entsprechend den Angaben in der String-Ressource wird hier der Wert Erinnerungsdatum angezeigt.

→4: In dieser Zeile wird eine Schaltfläche definiert, die der Benutzer anklicken kann, um ein DatePickerDialogFragment anzeigen zu lassen. Damit befasst sich der nachfolgende Abschnitt.

→9: Hierbei handelt es sich um das TextView-Label für die Uhrzeit-Schaltfläche. Entsprechend den Angaben in der String-Ressource wird hier der Wert Uhrzeit angezeigt.

→12: In dieser Zeile wird eine Schaltfläche definiert, die der Benutzer anklicken kann, um den TimePickerDialogFragment anzeigen zu lassen. Damit befasst sich der Abschnitt »TimePicker einbinden«.

DatePicker einbinden

Wenn Benutzer die date-Schaltfläche anklicken, sollen sie das Datum ändern können. Wie das geht, werde ich in den folgenden Abschnitten beschreiben.

Klick-Listener für die date-Schaltfläche einrichten

Um diese Überwachungsfunktion zu implementieren, öffnen Sie die Aktivität, in die Ihr Code für die Terminplaner-Anwendung eingefügt werden soll, also die Datei ReminderEditFragment.java.

Fügen Sie dort den in Listing 11.2 fett dargestellten Code zur Methode onCreateView() hinzu.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View v = inflater.inflate(R.layout.reminder_edit, container, false);

mTitleText = (EditText) v.findViewById(R.id.title);

mBodyText = (EditText) v.findViewById(R.id.body);

mDateButton = (Button) v.findViewById(R.id.reminder_date);

mTimeButton = (Button) v.findViewById(R.id.reminder_time);

mConfirmButton = (Button) v.findViewById(R.id.confirm);

mDateButton.setOnClickListener(new View.OnClickListener() { →13

@Override

public void onClick(View v) { →15

showDatePicker(); →16

}

});

return v;

}

Listing 11.2: Den DateButton-Klick-Listener implementieren

Die Codezeilen lassen sich wie folgt beschreiben:

→13: Setzt onClickListener() für die Schaltfläche mDateButton. Diese Methode wird beim Anklicken der Schaltfläche ausgeführt und führt die in Zeile 16 aufgeführte Aktion aus.

→15: Diese Zeile überschreibt das Standardverhalten der Schaltfläche bei einem Klick, um eigene Aktionen durchführen zu können. Der Parameter View v gibt an, welches Steuerelement angeklickt wurde.

→16: Hier wird festgelegt, was beim Anklicken der Schaltfläche geschehen soll. Es wird eine Methode des Fragments showDatePicker() aufgerufen, die weiter hinten im Abschnitt »Die Methode showDatePicker() erstellen« noch eingehender erläutert wird.

DatePickerDialogFragment erstellen

Das Android-Betriebssystem enthält ein DatePickerDialog-Element, über das Benutzer ein Datum auswählen können, ohne es eintippen zu müssen. Es kommt allerdings nicht bereits als Fragment verpackt daher, weshalb Sie diese Aufgabe selbst übernehmen müssen.

Die alten Dialogobjekte waren für den Aufruf von Aktivitäten und nicht von Fragmenten aus gedacht. Damit sich das Dialogfeld korrekt verhält, müssen Sie beim Öffnen eines Dialogfelds von einem Fragment aus eine Subklasse der Klasse DialogFragment verwenden.

Erstellen Sie eine neue Datei, nennen Sie sie DatePickerDialogFragment.java und fügen Sie den folgenden Code darin ein:

package com.dummies.android.terminplaner;

import android.app.DatePickerDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment; →6

public class DatePickerDialogFragment extends DialogFragment {

@Override

public Dialog onCreateDialog(Bundle savedInstanceState) { →10

Bundle args = getArguments(); →11

Fragment editFragment = getFragmentManager() →12

.findFragmentByTag(

ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG);

OnDateSetListener listener = (OnDateSetListener) editFragment;  →15

return new DatePickerDialog(getActivity(), listener, →17

args.getInt(ReminderEditFragment.YEAR),

args.getInt(ReminderEditFragment.MONTH),

args.getInt(ReminderEditFragment.DAY));

}

}

Der Code funktioniert wie folgt:

→6: Hier müssen Sie dafür sorgen, dass android.support.v4.app.* und nicht die Entsprechungen aus android.app importiert werden.

→10: Diese Methode wird aufgerufen, wenn Android das DatePicker-Dialogfeld anzeigen will. Diese Methode erzeugt es und gibt es zurück.

Zuweilen werden Sie savedInstanceState benutzen müssen, um den Status einer vorherigen Instanz wiederherzustellen. Hier nimmt Ihnen das Dialogfeld diese Aufgabe aber bereits automatisch ab und Sie können savedInstanceState in dieser Methode gefahrlos ignorieren.

→11: Konstruktor-ähnliche Argumente für ein Fragment werden über setArguments() und nicht über den Fragment-Konstruktor übergeben. Daher werden hier diese in Zeile 17 benötigten Argumente mit getArguments() übernommen.

→12: Fordern FragmentManager auf, nach einem Fragment namens DEFAULT_EDIT_ FRAGMENT_TAG zu suchen, bei dem es sich um ReminderEditFragment handelt.

→15: Wandelt editFragment in ein OnDateSetListener um. Das von onCreateDialog erzeugte Dialogfeld benötigt das OnDateSetListener-Objekt, um ReminderEditFragment darüber zu informieren, wenn Benutzer ein Datum ausgewählt haben.

→17: In dieser Zeile wird der DatePickerDialog-Konstruktor aufgerufen und es wird die in Zeile 12 übernommene Überwachungsfunktion getActivity() für den aktuellen Kontext und mit args.getInt(ReminderEditFragment.YEAR), args.getInt (ReminderEditFragment.MONTH), args.getInt(ReminderEditFragment.DAY) Jahr, Monat und Tag so übergeben, wie sie als Argumente dieses Fragments angegeben wurden.

Die Methode showDatePicker() erstellen

Wenn Sie die Klasse DatePickerDialogFragment erstellt haben, können Sie eine Instanz derselben erstellen und dem Benutzer anzeigen. Die Methode onClickListener der Schaltfläche ruft showDatePicker() auf, weshalb Sie diese nun implementieren müssen. Fügen Sie den folgenden Code hinter onCreateView() am Ende der Klasse ReminderEditFragment hinzu:

//

// Dialogkonstanten

//
static final String YEAR = "year";
static final String MONTH = "month";
static final String DAY = "day";
static final String HOUR = "hour";
static final String MINS = "mins";
static final String CALENDAR = "calendar";

private void showDatePicker() {

FragmentTransaction ft = getFragmentManager().beginTransaction();  →11

DialogFragment newFragment = new DatePickerDialogFragment(); →12

Bundle args = new Bundle(); →13

args.putInt(YEAR, mCalendar.get(Calendar.YEAR)) →14

args.putInt(MONTH, mCalendar.get(Calendar.MONTH));

args.putInt(DAY, mCalendar.get(Calendar.DAY_OF_MONTH));

newFragment.setArguments(args); →17

newFragment.show(ft, "datePicker"); →18

}

→11: Implementiert FragmentTransaction für das neue Fragment.

→12: Erzeugt eine DatePickerDialogFragment-Instanz.

→13: Erzeugt die Fragment-Konstruktorargumente in einem Bundle.

→14: Setzt Jahr, Monat und Tag des Dialogfeld-Fragments auf die über das mCalendar-Objekt gesetzten Werte.

→17: Setzt die Argumente des Fragments auf die Werte aus dem in Zeile 4 erzeugten Bundle.

→18: Ruft die Methode show() auf, um das datePicker-Dialogfeld auf dem Bildschirm anzuzeigen. Da show() die Methode commit() aufruft, müssen Sie das nicht explizit erledigen.

TimePicker einbinden

Über TimePickerDialogFragment können Benutzer einen Zeitpunkt auswählen, zu dem sie an den anstehenden Termin erinnert werden wollen.

Den Klick-Listener für die time-Schaltfläche einrichten

Die Einrichtung von TimePickerDialogFragment ist mit der von DatePickerDialog Fragment nahezu identisch. Erst einmal müssen Sie den onClickListener() für die UHRZEIT-Schaltfläche deklarieren. Fügen Sie dazu in ReminderEditFragment.onCreateView() das folgende Codesegment direkt vor dem return am Ende ein:

mTimeButton.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

showTimePicker();

}

});

Diese Methode ist mit onClickListener() der Datumsschaltfläche identisch, nur dass showTimePicker() und nicht showDatePicker() aufgerufen wird.

Die Methode showTimePicker() erstellen

Um die Methode showTimePicker() zu erstellen, fügen Sie den Code aus Listing 11.3 hinter der Methode showDatePicker() in die Datei ReminderEditFragment.java ein.

private void showTimePicker() {

FragmentTransaction ft = getFragmentManager().beginTransaction();

DialogFragment newFragment = new TimePickerDialogFragment(); →3

Bundle args = new Bundle();

args.putInt(HOUR, mCalendar.get(Calendar.HOUR_OF_DAY)); →5

args.putInt(MINS, mCalendar.get(Calendar.MINUTE));

newFragment.setArguments(args);

newFragment.show(ft, "timePicker"); →8

}

Listing 11.3: Die Methode showTimePicker()

Der Code aus Listing 11.3 ist recht einfach, weil er mit dem der Methode showDate Picker() nahezu identisch ist. Es gibt aber Unterschiede in den folgenden Zeilen:

→3: Hier wird eine neue Instanz von TimePickerDialogFragment erzeugt.

→5: Setzt die Argumente für TimePickerDialogFragment auf die Komponenten Stunde und Minute (HOUR und MINS) des Calendar-Objekts.

→8: Zeigt das Fragment unter Verwendung des für Benutzer unsichtbaren Tags "time Picker" an.

TimePickerDialogFragment erzeugen

Das Android-Betriebssystem enthält ein TimePickerDialog-Element, über das Benutzer eine Uhrzeit auswählen können, anstatt sie eingeben zu müssen. Wie DatePickerDialog ist auch TimePickerDialog nicht in ein Fragment eingebettet, weshalb Sie diese Aufgabe wieder selbst übernehmen müssen.

Der Code für TimePickerDialogFragment ist mit dem von DatePickerDialogFragment nahezu identisch, nur dass diesmal ein TimePickerDialog-Element anstelle eines DatePickerDialog-Elements eingebunden wird. Fügen Sie diesen Code in die Datei TimePickerDialogFragment.java ein:

package com.dummies.android.terminplaner;

import android.app.Dialog;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;

public class TimePickerDialogFragment extends DialogFragment {

@Override

public Dialog onCreateDialog(Bundle savedInstanceState) {

Bundle args = getArguments();

OnTimeSetListener listener = (OnTimeSetListener)

getFragmentManager()

.findFragmentByTag(

ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG);

return new TimePickerDialog(getActivity(), listener,

args.getInt(ReminderEditFragment.HOUR),

args.getInt(ReminderEditFragment.MINS), false);

}

}

Das Fragment für die Verarbeitung von DatePicker- und TimePicker-Callbacks erstellen

Nun bleibt noch ein Schritt zu erledigen, um Ihre Auswahlelemente für Datum und Uhrzeit funktionsfähig zu bekommen. Sie müssen noch OnDateSetListener und OnTimeSetListener implementieren, damit das Dialogfeld die neuen Werte für Datum und Uhrzeit nach der Benutzerauswahl auch in einem Calendar-Objekt speichert. Das lässt sich in drei Schritten erledigen:

1. Sie fügen die Schnittstellen OnDateSetListener und OnTimeSetListener zu ReminderEditFragment hinzu.

2. Sie fügen ein Calendar-Objekt namens mCalender zur Speicherung der vom Benutzer gesetzten Werte hinzu.

3. Sie setzen die Werte des mCalendar-Objekts, wenn Benutzer ein neues Datum oder eine neue Uhrzeit auswählen, und aktualisieren dann die entsprechenden Schaltflächen der Bedienschnittstelle mit den neuen Werten.

Um Ihre Datum/Zeit-Auswahlelemente funktionsfähig zu machen, fügen Sie den fett dargestellten Code aus Listing 11.4 zur Klasse ReminderEditFragment hinzu.

public class ReminderEditFragment extends Fragment implements

OnDateSetListener, OnTimeSetListener { →2

private static final String DATE_FORMAT = "yyyy−MM−dd";

private static final String TIME_FORMAT = "kk:mm";

private Calendar mCalendar; →7

@Override

public void onDateSet(DatePicker view, int year, int monthOfYear,

int dayOfMonth) { →11

mCalendar.set(Calendar.YEAR, year);

mCalendar.set(Calendar.MONTH, monthOfYear);

mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);

updateButtons(); →15

}

@Override

public void onTimeSet(TimePicker view, int hour, int minute) { →19

mCalendar.set(Calendar.HOUR_OF_DAY, hour);

mCalendar.set(Calendar.MINUTE, minute);

updateButtons();

}

private void updateButtons() { →25

// Text des TimeButtons setzen

SimpleDateFormat timeFormat = new SimpleDateFormat(TIME_FORMAT);

String timeForButton = timeFormat.format(mCalendar.getTime());

mTimeButton.setText(timeForButton);

// Text des DateButtons setzen

SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);

String dateForButton = dateFormat.format(mCalendar.getTime());

mDateButton.setText(dateForButton);

}

}

Listing 11.4: Die Schnittstellen von OnDateSetListener und OnTimeSetListener zur Klasse ReminderEditFragment hinzufügen

Listing 11.4 funktioniert wie folgt:

→2: Implementiert die Callback-Objekte OnDateSetListener und OnTimeSetListener.

→7: Speichert die Benutzerauswahl von Datum und Uhrzeit mit Hilfe des Java-Standardobjekts Calendar.

→11: Implementiert die Methode onDateSet() für OnDateSetListener. Wenn Benutzer das Datum im Dialogfeld setzen, wird das Calendar-Objekt aktualisiert, um das neue Datum wiederzugeben.

→15: Aktualisiert die Schaltflächen der Bedienschnittstelle, damit sie dem neuen Datum und der neuen Uhrzeit entsprechen.

→19: Setzt die Stunden und Minuten des Calendar-Objekts für die Methode onTimeSet() von OnTimeSetListener.

→25: Aktualisiert die Schaltflächen, damit deren Werte den vom Benutzer ausgewählten Werten entsprechen. Das Calendar-Objekt wird in eine Reihe von Textstrings für die Datum- und Uhrzeit-Schaltflächen umgewandelt, die dann als Text für die Beschriftung dieser Schaltflächen benutzt wird. Das erste SimpleDateFormat benutzt TIME_FORMAT, um eine Zeichenkette mit der Uhrzeit im Format hh:mm zu erzeugen, während die zweite entsprechende Anweisung mit DATE_FORMAT einen String im Format yyyy−MM−dd erstellt.

Unter http://docs.oracle.com/javase/6/docs/api/java/text/Simple DateFormat.html erfahren Sie weitere Einzelheiten über die Formatierung von Datum und Uhrzeit mit SimpleDateFormat.

Um das mCalendar-Objekt hinzuzufügen, erweitern Sie onCreate() um diese Zeilen:

if (savedInstanceState != null

&& savedInstanceState.containsKey(CALENDAR)) { →2

mCalendar = (Calendar) savedInstanceState.getSerializable(CALENDAR);  →3

} else {

mCalendar = Calendar.getInstance(); →5

}

Der Code funktioniert wie folgt:

→2: Diese Zeile prüft, ob in savedInstanceState ein CALENDAR-Feld vorliegt, das auf eine frühere Aktivität hinweist, die gespeichert und vorübergehend zerstört wurde, wie dies beispielsweise beim Drehen des Bildschirms vom Quer- in den Hochkantmodus der Fall ist.

Wenn Sie eine völlig neue Instanz dieses Fragments erzeugen, hat savedInstanceState den Wert null und die App wird mit Zeile 5 fortgesetzt.

→3: Wenn savedInstanceState ein CALENDAR-Feld umfasst, übernimmt diese Zeile das zugehörige Calendar-Objekt und setzt mCalendar auf dessen Wert.

→5: Wenn Sie ein ReminderEditFragment-Objekt völlig neu erstellen und keine vorherige Instanz wiederherstellen, setzt diese Zeile mCalendar auf eine neue CalendarInstanz (mit Hilfe eines Aufrufs von Calendar.getInstance()). Neue CalendarInstanzen übernehmen standardmäßig immer das aktuelle Datum und die aktuelle Uhrzeit.

Damit wurde die mCalendar-Instanz initialisiert.

Da onCreate() in den ersten beiden Zeilen in savedInstanceState nach einer Calendar-Instanz sucht, müssen Sie Ihr mCalendar-Objekt jeweils in savedInstanceState speichern, wenn Ihre Activity zerstört wird. Auf diese Weise kann onCreate() den gespeicherten Wert übernehmen, sofern die Activity jemals wiederhergestellt werden muss. Fügen Sie dazu die folgende Methode zu Ihrer Klasse hinzu:

@Override
public void onSaveInstanceState(Bundle outState) { →2

super.onSaveInstanceState(outState);

// Calendar−Instanz speichern, falls Änderungen vorgenommen wurden  →4

outState.putSerializable(CALENDAR, mCalendar);

}

Der Code funktioniert wie folgt:

→2: onSaveInstanceState() ist eine spezielle Methode, die von Android jeweils vor der anstehenden Zerstörung eines Fragments aufgerufen wird. Sie können mit ihr beliebige verwendete Daten speichern. Auf diese Weise können Sie dafür sorgen, dass Android alle benötigten Daten hat, wenn es dieselbe Activity einmal wiederherstellen muss.

Die meisten Views wissen bereits von sich aus, wie sie ihren Zustand speichern und selbst wiederherstellen können, und erledigen das im Aufruf von super.onSaveInstanceState().

→4: Die einzige Aufgabe, die Sie selbst manuell erledigen müssen, betrifft das mCalendar-Feld, weshalb es in dieser Zeile im Bundle gespeichert wird, das später zur Wiederherstellung der Activity benutzt wird.

Calendar-Objekte lassen sich serialisieren (als Daten speichern), weshalb diese Zeile die Methode putSerializable() benutzt, um sie zu speichern.

Mehr zur Java-Serialisierung erfahren Sie unter http://java.sun.com/ developer/technicalArticles/Programming/serialization.

Sie können beliebige andere Typen in Bundles speichern, wie beispielsweise int, long, string, parcelable und einige weitere exotische Elemente. Eine vollständige Liste finden Sie unter http://d.android.com/reference/ android/os/Bundle.html.

Feldnamen in Android speichern

Android-Aktivitäten und -Fragmente unterscheiden sich von Java-Standardobjekten, bei denen Sie Daten in einem Feld des Objekts speichern und erwarten können, dass diese für immer dort verbleiben. Wenn in Java in einem person-Objekt der Name "Michael" gespeichert wird, können Sie normalerweise erwarten, dass der Wert immer "Michael" lauten wird, aber das ist unter Android erstaunlicherweise nicht immer so.

Im Unterschied zu Java kann Android Activities und Fragments jederzeit zerstören. Diese Elemente können später auch wiederhergestellt werden, wobei wiederhergestellte Activities sich nicht von jenen unterscheiden dürfen, die nie zerstört und wiederhergestellt wurden. Android behält sich das Recht vor, Objekte bei Speicherknappheit zu zerstören, wobei diese später allerdings wiederhergestellt werden können, damit Benutzer davon nichts merken.

Wenn Sie den String "Michael" in einem Feld namens name speichern, wird dieses Feld nicht automatisch gespeichert, wenn eine Activity oder ein Fragment zerstört und wiederhergestellt wird. Sie müssen das Feld manuell speichern, indem Sie es in einem Bundle in onSaveInstanceState() speichern und es aus dem savedInstanceState-Bundle in onCreate() wiederherstellen.

Wenn Sie ein Feld zu einer Activity oder einem Fragment hinzufügen, müssen Sie jeweils auch daran denken, den entsprechenden Code zu den Methoden onSavedInstanceState() und onCreate() hinzuzufügen, um es speichern und wiederherstellen zu können. Ansonsten wird sich Ihre App in einigen Fällen seltsam, in anderen hingegen völlig normal verhalten.

Ein Alert-Dialogfeld erstellen

Gelegentlich müssen Sie Benutzer auch über Dialogfelder darüber informieren, dass bestimmte Dinge geschehen sind. In der Terminplaner-App werden Sie Benutzern vielleicht eine Willkommensmeldung oder Hinweise dazu anzeigen wollen, wie neue Termine erstellt werden können. Im Android-System gibt es ein Framework für Dialogfelder, die alle potenziell benötigten Implementierungen abdecken.

Es stehen mehrere verschiedene Arten von Dialogfeldern zur Auswahl:

Alert: Informiert den Benutzer über wichtige Ereignisse. Ermöglicht auch das Setzen der Textwerte der Schaltflächen und die Definition der Aktionen, die beim Anklicken durchgeführt werden sollen. Als Entwickler können Sie an den AlertDialog eine Reihe von anzuzeigenden Elementen übergeben, zwischen denen der Benutzer auswählen kann.

Progress: Wird zur Anzeige eines Fortschrittsbalkens oder eines Rädchens genutzt. Dieses Dialogfeld wird über die Klasse ProgressDialog erstellt.

Custom: Die speziell angepassten Dialogfelder müssen Sie als Android-Entwickler weitgehend selbst erstellen und programmieren. Dazu erstellen Sie eigene Dialogklassen und erweitern die Basisklasse Dialog oder spezifische XML-Layoutdateien.

Warum Sie Dialogfelder nutzen sollten

Haben Sie schon einmal eine Anwendung benutzt, die zwar im Hintergrund hektisch vor sich hin gewerkelt hat, dabei aber keine weiteren Warnungen, Benachrichtigungen oder Informationen angezeigt hat? Falls nicht, denken Sie an das folgende Beispiel. Stellen Sie sich einen E-Mail-Client vor, der Sie nicht über den Eingang neuer E-Mails benachrichtigt. Das wäre doch höchst irritierend, oder? Benutzer über wichtige Aspekte oder auszuwählende Optionen zu informieren, gehört zu den zentralen Elementen der Benutzerfreundlichkeit.

Die folgende Liste enthält einige Beispiele für Ereignisse, bei denen Sie Benutzer über Dialogfelder darüber informieren können, dass Nachrichten eingetroffen und/oder Maßnahmen ergriffen werden müssen:

Es geschieht etwas im Hintergrund (darüber informiert ein ProgressDialog).

Die Werte in einer EditText-View sind ungültig.

Die Netzwerkverbindung wurde unterbrochen.

Benutzer müssen ein Datum und/oder eine Uhrzeit auswählen (wie eben dargestellt).

Die Geräteausstattung ist nicht mit der Anwendung kompatibel. Vielleicht muss für die App GPS aktiviert sein, vielleicht ist sie auf eine SD-Speicherkarte angewiesen oder vielleicht fehlt eine Kamera und das wurde beim Anwendungsstart erkannt.

Benutzer müssen ein Element aus einer Optionsliste auswählen.

Diese Liste ist sicherlich nicht umfassend, sollte Ihnen aber einen Eindruck davon vermitteln, was mit Dialogfeldern sinnvoll oder möglich ist.

Wenn Sie Prozesse nutzen, die Ihre App blockieren können (Datenübertragung über ein Netzwerk, länger laufende Tasks und so weiter), sollten Sie immer irgendein Dialogfeld oder eine Fortschrittsanzeige einblenden, um den Benutzer darüber zu informieren, was gerade geschieht. Wenn Benutzer gar nicht wissen, dass etwas vor sich geht, werden sie schnell vermuten, dass die Anwendung nicht mehr reagiert, und sie nicht weiter verwenden. Das Android-Framework stellt verschiedene Varianten zur Fortschrittsanzeige bereit. ProgressDialog und ProgressBar zählen dabei zu den häufig verwendeten Klassen.

Die Klasse AsyncTask wird aufgrund ihrer fortgeschrittenen Natur in die-sem Buch zwar nicht behandelt, mit ihr können aber länger laufende Tasks verwaltet und dabei die Benutzerschnittstelle aktualisiert werden. Ein hervorragendes Tutorial mit der Überschrift »Painless Threading« finden Sie unter http://android-developers.blogspot.com/2009/05/painless-threa ding.html. Sie können auch einen neuen Thread im Code erzeugen, aber die Klasse AsyncTask vereinfacht derartige Aufgaben.

Zu den Aufgaben passende Dialogfelder auswählen

Sie müssen letztlich selbst festlegen, welches Dialogfeld Sie für die jeweiligen Situationen verwenden wollen. Ich folge bei der entsprechenden Entscheidung allerdings einer logischen Abfolge von Schritten:

1. Handelt es sich um einen länger laufenden Task?

• Ja: Verwenden Sie einen ProgressDialog, um den Benutzer darüber zu informieren, dass etwas im Hintergrund geschieht und die App nicht abgestürzt ist. Wie Sie dabei vorgehen müssen, wird unter http://d.android.com/guide/topics/ui/ dialogs.html#ProgressDialog gut erläutert.

• Nein: Weiter mit Schritt 2.

2. Muss der Benutzer anspruchsvollere Aktionen im Dialogfeld durchführen?

Mit anspruchsvolleren Aktionen sind Dinge gemeint, die von der Klasse AlertDialog nicht unterstützt werden.

• Ja: Erzeugen Sie eine speziell angepasste Dialog-Klasse, die die Dialog-Basisklasse erweitert, oder erstellen Sie ein Dialogfeld über eine XML-Layoutdatei. Weitere Informationen zu speziell angepassten Dialogfeldern finden Sie unter der Adresse http://d.android.com/guide/topics/ui/dialogs.html#CustomDialog.

• Nein: Weiter mit Schritt 3.

3. Muss der Benutzer Fragen wie »Sind Sie sicher?« mit »Ja« oder »Nein« beantworten?

• Ja: Erstellen Sie einen AlertDialog und reagieren Sie auf die Schaltflächenereignisse im Dialogfeld über onClickListener()-Aufrufe.

• Nein: Weiter mit Schritt 4.

4. Muss der Benutzer eine Option aus einer einfachen Liste auswählen?

• Ja: Erstellen Sie einen AlertDialog.

• Nein: Weiter mit Schritt 5.

5. Muss der Benutzer einfach nur über etwas benachrichtigt werden?

• Ja: Erstellen Sie einen einfachen AlertDialog.

• Nein: Vielleicht wird gar kein Dialogfeld benötigt. Fragen Sie sich selbst, ob Sie den Benutzer nicht auf eine andere Weise benachrichtigen können.

Ein eigenes Alert-Dialogfeld erstellen

Manchmal müssen Sie Benutzer über wichtige Ereignisse informieren und dazu Dialogfelder anzeigen. Android erleichtert dies stark durch die angebotene Klasse AlertDialog.Builder. Mit dieser Klasse können Sie leicht einen AlertDialog mit verschiedenen Optionen und Schaltflächen erstellen. Sie können auf das Anklicken der Schaltflächen reagieren, wenn Sie dafür jeweils onClickListener() definieren.

Die Klasse AlertDialog.Builder wird zwar in einfachen Apps nicht benötigt, Listing 11.5 zeigt Ihnen aber dennoch, wie Sie mit ihr Dialogfelder für komplexere Apps erstellen können.

Nehmen Sie an, dass dem Benutzer in der Terminplaner-Anwendung beim Anklicken der Schaltfläche SPEICHERN ein Dialogfeld mit einer Bestätigung angezeigt werden soll, das Abbildung 11.3 ähnelt und in dem der Benutzer gefragt wird, ob er die Änderung wirklich speichern will.

Abbildung 11.3: Ein AlertDialog-Fenster zum Bestätigen der Eingabe

In Listing 11.5 erzeugen Sie ein AlertDialog-Objekt unter Verwendung der Klasse AlertDialog.Builder und fügen dann ein AlertDialogFragment hinzu, das ähnlich wie DatePickerDialogFragment und TimePickerDialogFragment arbeitet.

public class AlertDialogFragment extends DialogFragment {

@Override

public Dialog onCreateDialog(Bundle savedInstanceState) {

AlertDialog.Builder builder

= new AlertDialog.Builder(getActivity()) →5

builder.setMessage("Soll der Termin gespeichert werden?") →6

.setTitle("Sind Sie sicher?") →7

.setCancelable(false) →8

.setPositiveButton("Ja", →9

new DialogInterface.OnClickListener() →10

public void onClick(DialogInterface dialog, int id) {

// Aktionen durchführen und beispielsweise →12

// ein Element speichern

}

})

.setNegativeButton("Nein", new DialogInterface.OnClickListener()  →15

public void onClick(DialogInterface dialog, int id) {

dialog.cancel(); →17

}

});

return builder.create(); →20

}

}

Listing 11.5: Ein AlertDialogFragment mit der Klasse AlertDialog.Builder erzeugen

Dieser Code lässt sich wie folgt erläutern:

→5: Richtet die Klasse AlertDialog.Builder als aktuell laufende Aktivität ein.

→6: In dieser Zeile wird der Text der Meldung festgelegt, der mitten im AlertDialog angezeigt werden soll (siehe Abbildung 11.3). Bei diesem Wert kann es sich um einen String oder eine String-Ressource handeln.

→7: Hier wird der Titel von AlertDialog festgelegt. Bei dem Wert kann es sich um einen String oder eine String-Ressource handeln.

→8: In dieser Zeile wird das Attribut Cancelable auf false gesetzt. Das bedeutet, dass der Benutzer eine der beiden Schaltflächen in AlertDialog anklicken muss. Der Benutzer kann dann nicht mehr die ZURÜCK-Taste (oder -Schaltfläche) am Gerät benutzen, um den AlertDialog auszublenden. Wenn Sie das Attribut auf true setzen, können Benutzer das Dialogfeld auch über die Zurück-Schaltfläche verlassen.

→9: Hier wird der Text angegeben, der auf der »positiven Schaltfläche« angezeigt werden soll. Dabei handelt es sich um die Schaltfläche, die der Benutzer anklickt, wenn die im AlertDialog angegebene Aktion ausgeführt werden soll. Hier wird Ja als Text verwendet. Bei diesem Wert kann es sich um einen String oder eine String-Ressource handeln.

→10: Bei diesem aus den Zeilen 10 bis 14 bestehenden Codeblock wird der onClick Listener() für die positive Schaltfläche (Ja) definiert. Beim Anklicken der Schaltfläche wird dieser Code ausgeführt. In Zeile 12 befindet sich ein Kommentar mit einem Hinweis darauf, was hier geschehen soll.

→15: Hier wird der Text angegeben, der auf der »negativen Schaltfläche« angezeigt werden soll. Dabei handelt es sich um die Schaltfläche, die der Benutzer anklickt, wenn die im AlertDialog angegebene Aktion nicht ausgeführt werden soll. Hier wird Nein als Text verwendet. Bei diesem Wert kann es sich um einen String oder eine String-Ressource handeln.

→17: Hier wird der onClickListener() für die negative Schaltfläche definiert. Der Listener verweist auf das aktuell angezeigte Dialogfeld. Ich rufe die Methode cancel() des Dialog-Objekts auf, um das Dialogfeld zu schließen, wenn der Benutzer im AlertDialog die NEIN-Schaltfläche anklickt.

→20: Diese Zeile sorgt dafür, dass Android den AlertDialog über die Methode create() erzeugt.

Um das Dialogfeld anzuzeigen, starten Sie wie üblich eine FragmentTransaction:

FragmentTransaction ft = getFragmentManager().beginTransaction();

DialogFragment newFragment = new AlertDialogFragment();
newFragment.show(ft, "alertDialog");

Es ist viel einfacher, Dialogfelder über die Klasse AlertDialog.Builder zu erstellen, als eine eigene Dialog-Klasse abzuleiten. Sofern möglich, sollten Sie Ihre Dialogfelder über die Klasse AlertDialog.Builder erzeugen, da Ihre Anwendung dann mit der übrigen Android-Oberfläche konsistent bleibt und für den Benutzer vertraut aussieht.

Wenn der Benutzer die SPEICHERN-Schaltfläche (oder eine andere Schaltfläche, der Sie den Code zugeordnet haben) anklickt, wird ein AlertDialog angezeigt, über den er noch einmal bestätigen muss, dass der Termin gespeichert wird. Bisher werden die Termine zwar noch nicht wirklich gespeichert, aber damit befasst sich Kapitel 12, in dem die Termine in einer SQLite-Datenbank gespeichert werden.

Die Dialog-Klasse verfügt noch über eine Reihe weiterer Optionen. Gute Beispiele dafür finden Sie in der Android-Dokumentation unter http://d.android.com/guide/topics/ui/dialogs.html.

Eingaben prüfen

Sie haben eine App mit einem Formular erstellt, in das Benutzer Daten eingeben können, und vielleicht haben Sie ja sogar schon dafür gesorgt, dass diese in einer Datenbank gespeichert oder an einen auf einem anderen Rechner laufenden Server übergeben werden. Was passiert aber, wenn Benutzer ungültige oder gar keine Daten eingeben? Dann wird es wichtig, die Eingaben zu prüfen.

Eingabeprüfungen finden statt, bevor die Daten wirklich gespeichert werden. Nehmen Sie an, dass der Benutzer keinen Text für den Titel oder als Beschreibung angegeben hat und den Termin zu speichern versucht. Sollte das erlaubt sein? Zumindest im Falle des leeren Titels natürlich nicht!

Wie Eingabeprüfungen vorgenommen werden, bleibt letztlich Ihnen überlassen. Einige gängige Verfahren sind:

EditText.setError(): Wenn Sie erkennen, dass ein Benutzer nicht zulässigen Text in ein Feld einzugeben versucht hat, rufen Sie einfach setError() auf und übergeben die Fehlermeldung. Android verziert die EditText-View dann mit einem Fehlersymbol und zeigt eine Fehlermeldung an. Diese bleibt auf dem Bildschirm stehen, bis der Benutzer den Feldwert ändert oder in Ihrer App setError(null) aufgerufen wird.

TextWatcher: TextWatcher wird für die EditText-View implementiert. Diese Klasse bietet Callbacks, die jeweils aufgerufen werden, wenn der Text in der EditText-View geändert wird. Damit können Sie den Text bei jedem Tastendruck untersuchen.

OnSave: Wenn der Benutzer die aktuellen Formulardaten zu speichern versucht, können Sie die Werte aller Formularfelder untersuchen und den Benutzer auf möglicherweise vorhandene Probleme hinweisen.

onFocusChanged(): Sie untersuchen die Werte im Formular beim Ereignis onFocusChanged(), wenn also der Fokus von einem Steuerelement zu einem anderen wechselt. Das Verlassen eines Eingabefeldes eignet sich üblicherweise gut für die Prüfung von Eingaben.

Wenn man einmal davon absieht, dass DatePicker und TimePicker keine Falscheingaben zulassen, finden in der Terminplaner-App keine weiteren Prüfungen der Benutzereingaben statt. Sie können das aber natürlich ändern.

Toast-Meldungen

Das gebräuchlichste Verfahren, um Benutzer über potenzielle Probleme zu informieren, ist die Anzeige von Toast-Meldungen. Bei einem »Toast« (Trinkspruch) handelt es sich um ein kleines Fenster mit irgendwelchen Informationen für den Benutzer (hier zum Beispiel Fehlermeldungen bei falschen Eingaben), das nur wenige Sekunden lang angezeigt wird und anschließend automatisch wieder vom Bildschirm verschwindet.

Um einen Toast anzuzeigen und den Benutzer damit auf Eingabefehler aufmerksam zu machen, müssen Sie nur den folgenden Code implementieren:

Toast.makeText(ReminderEditFragment.this, "Titel muss angegeben  werden", Toast.LENGTH_SHORT).show();

Sie können diese Toast-Meldung anzeigen, wenn der Benutzer beim Anklicken der SPEICHERN-Schaltfläche das Titelfeld leer gelassen hat.

Das einzige Problem bei Toast-Meldungen besteht darin, dass sie nur kurz angezeigt werden. Sie lassen sich zwar auch (über Toast.LENGTH_LONG anstelle von Toast.LENGTH_SHORT) so konfigurieren, dass sie länger angezeigt werden, aber wenn Benutzer nicht auf den Bildschirm blicken, kann es immer vorkommen, dass sie Meldungen übersehen, weil sie zwischenzeitlich bereits wieder ausgeblendet wurden.

Andere Prüfungsverfahren nutzen

Neben Toast-Meldungen können Sie andere Verfahren benutzen, um Benutzer über Probleme bei ihren Eingaben zu informieren. Dazu zählen:

AlertDialog: Erzeugen Sie eine AlertDialog-Instanz, um Benutzer auf Fehler hinzuweisen. Dann ist gewährleistet, dass der Benutzer die Fehlermeldung sieht, da sie entweder bestätigt oder aktiv ausgeblendet werden muss.

Hervorhebung von Eingabefeldern: Wenn die Werte im Feld unzulässig sind, kann Rot (oder eine andere Farbe) als Hintergrundfarbe für das betreffende EditText-Widget gesetzt werden, um darauf hinzuweisen.

Spezielle Prüfungen: Wenn Sie entsprechend ambitioniert sind, können Sie eine eigene Bibliothek für allerlei Prüfungen entwickeln. Dabei könnten Sie Felder hervorheben und kleine Pfeile anzeigen lassen, die auf die Fehler hinweisen. So ähnlich verfährt Google bei den Bildschirmen für die erstmalige Anmeldung bei seinen Geräten.

Sie können die hier kurz vorgestellten gängigen Verfahren zur Prüfung von Benutzereingaben verwenden, sich aber natürlich auch andere Verfahren ausdenken und sie anwenden. In Kapitel 14 wird zum Beispiel die Benachrichtigungsleiste vorgestellt, die ich in eigenen Projekten dazu benutzt habe, um Benutzer auf Probleme mit Hintergrunddiensten hinzuweisen.

12

Eingegebene Daten dauerhaft
speichern

In diesem Kapitel

Medien für die Datenspeicherung

Benutzerberechtigungen

Eine SQLite-Datenbank erstellen

Ihre Datenbank abfragen

In bestimmten Anwendungen müssen Entwickler unter Android gewisse Daten für die spätere Wiederverwendung dauerhaft speichern. Dazu zählen typischerweise Benutzereinstellungen wie das verwendete Hintergrundbild oder der zuletzt eingestellte Radiosender, die auch nach dem Ausschalten und erneuten Einschalten des Geräts erhalten bleiben sollen. Die Terminplaner-Anwendung wäre sicherlich nicht sonderlich nützlich, wenn man damit die eingegebenen Termindaten nicht dauerhaft speichern könnte. Aber glücklicherweise bietet die Android-Plattform in Verbindung mit Java auch hier eine Reihe bewährter Werkzeuge, die Sie zu diesem Zweck nutzen können.

In diesem Kapitel wird die Erstellung, Aktualisierung und Abfrage einer SQLite-Datenbank ausführlich behandelt. Die einzelnen Schritte werden zwar eingehend erläutert, da dabei die Datenbanktheorie aber nur oberflächlich behandelt wird, sollten Sie mit dieser halbwegs vertraut sein.

Wenn Sie mit SQL (Structured Query Language) oder dem SQLite-Datenbanksystem nicht vertraut sind, finden Sie auf der SQLite-Website unter www. sqlite.org ausführlichere Informationen.

Dieses Kapitel ist zudem sehr codeintensiv. Sollten Sie einmal den Faden verlieren, können Sie aber jederzeit auf den vollständigen Quellcode der fertigen Anwendung zurückgreifen, der online bereitgestellt wird.

Orte zum Speichern von Daten

In Abhängigkeit von den Anforderungen Ihrer Anwendung müssen Sie Ihre Daten möglicherweise an verschiedenen Orten speichern. Einige Apps nutzen beispielsweise Audiodateien. Die darin enthaltene Musik werden Benutzer aber möglicherweise auch mit anderen Programmen wiedergeben wollen, weshalb Sie sie besser an einem Ort speichern sollten, auf den alle Anwendungen zugreifen können. Andere Apps müssen vielleicht sensitive Daten wie verschlüsselte Benutzernamen und Kennwörter speichern. Dann sollten andere Programme auf diese Daten besser nicht zugreifen können, weshalb sich dann die lokale Speicherung an einem sicheren Ort am besten eignet. Android stellt Ihnen jedenfalls für die verschiedenen Situationen unterschiedliche Optionen zur Speicherung von Daten zur Verfügung.

Die verschiedenen Speicheroptionen

Das Android-System bietet verschiedene Orte für die dauerhafte Speicherung von Daten. Zu den gebräuchlichsten zählen:

Gemeinsam genutzte Einstellungen: Dabei handelt es sich um private Daten, die in Schlüssel/Wert-Paaren gespeichert werden. Wie Sie mit derartigen Einstellungen arbeiten können, wird in Kapitel 15 dargestellt.

Interner Speicher: Dabei handelt es sich um Platz im internen Speicher des Geräts. Standardmäßig sind dort abgelegte Dateien nur für Ihre Anwendung zugänglich (privat), während andere (und auch der Benutzer des Geräts) nicht darauf zugreifen können. Wenn der Benutzer die Anwendung deinstalliert, werden auch die privaten Dateien entfernt.

Lokaler Cache: Wenn Sie einige Daten lieber nur zwischenzeitlich und nicht dauerhaft speichern wollen, können Sie den Cache im internen Datenordner erstellen. Dazu sollten Sie die Methode getCacheDir() verwenden, die von den Activity- oder ContextObjekten von Android zur Verfügung gestellt werden.

Beachten Sie, dass Android die im lokalen Cache gespeicherten Dateien möglicherweise löscht, um Platz zu gewinnen, wenn der interne Systemspeicher knapp wird. Sie sollten dafür sorgen, dass Sie möglichst nicht mehr als etwa ein Megabyte Speicherplatz für derartige Zwecke verwenden.

Externer Speicher: Alle Android-Geräte unterstützen gemeinsam nutzbaren externen Speicher, in dem Sie Dateien ablegen können. Dabei kann es sich um Wechselmedien wie SD Cards (Secure Digital Cards) und/oder fest im Gerät installierten Speicher handeln. Die Dateien im externen Speicher sind typischerweise öffentlich zugänglich (public) und können daher von allen Anwendungen (und Benutzern) geändert werden. Bei extern gespeicherten Dateien greifen keinerlei Sicherheitsmechanismen. Sie können vom Benutzer mit einem Dateimanager geändert werden und sind auch zugänglich, wenn Sie das Gerät über ein USB-Kabel mit einem Computer verbinden und als externen Speicher einbinden. Bevor Sie externen Speicher nutzen, sollten Sie immer dessen aktuellen Status über das Environment-Objekt abfragen und mit einem Aufruf von getExternalStorageState() prüfen, ob das Speichermedium verfügbar ist.

Mit Android 2.2 kamen eine Reihe neuer Methoden für den Umgang mit externen Dateien hinzu. Bei der wichtigsten Methode getExternalFilesDir() des Context-Objekts handelt es sich um einen Aufruf, dem ein String-Parameter als Schlüssel übergeben werden kann, über den sich festlegen lässt, welche Art von Medien gespeichert werden soll, wie zum Beispiel Klingeltöne, Musik oder Fotos. Weitere Informationen dazu finden Sie bei den Beispielen und der Dokumentation zum externen Speicher unter http://d.android.com/guide/topics/data/data−storage.html#filesExternal.

SQLite-Datenbank: Android unterstützt SQLite-Datenbanken umfassend. Bei SQLite handelt es sich um ein einfaches SQL-Datenbanksystem (Structured Query Language), das für unterschiedliche Plattformen wie Android, iPhone, Windows, Linux, Mac und einige andere eingebettete Systeme zur Verfügung steht. Sie können Tabellen erstellen und sie mit SQL-Anweisungen abfragen. In diesem Kapitel werde ich eine SQLite-Datenbank implementieren, um die in der Terminplaner-App eingegebenen Daten dauerhaft zu speichern.

Netzwerkverbindung: Bei der letzten, aber bestimmt nicht unwichtigsten Option handelt es sich um Netzwerkspeicher, der von anderen Geräten bereitgestellt wird. Dabei kann es sich um beliebige Datenquellen handeln, auf die Sie zugreifen können. Flickr stellt beispielsweise ein API bereit, über das Sie Bilder auf den Servern des Unternehmens speichern können. Diese Möglichkeit könnten Sie in Ihren Apps nutzen. Sie könnten auch Android-Anwendungen schreiben, die auf andere beliebte Internetwerkzeuge zurückgreifen, wie zum Beispiel Twitter, Facebook oder Basecamp. Dann würde Ihre App bei der Speicherung von Daten diese via HTTP (oder andere möglicherweise erforderliche Protokolle) an die APIs von Drittanbietern weiterreichen.

Auswahl einer Speicheroption

Wie Sie sehen, stehen Ihnen eine ganze Menge verschiedener Optionen zur Speicherung von Daten zur Verfügung. Wichtig ist nur, dass Sie sich für eine der Optionen entscheiden. Zuweilen werden Sie möglicherweise in einer einzigen App auch mehrere Alternativen nutzen müssen.

Wenn Ihre Anwendung zum Beispiel mit dem API eines Drittanbieters wie Twitter kommuniziert, werden Sie eine lokale Kopie aller seit der letzten Aktualisierung des Servers veränderten Daten aufbewahren wollen, sofern die Netzwerkverbindung langsam und/oder nicht ganz zuverlässig ist. Dadurch kann die Anwendung bis zur nächsten Online-Aktualisierung (zumindest teilweise) nutzbar bleiben. Sie könnten die Daten in einer lokalen Kopie einer SQLiteDatenbank speichern und diese erneut aktualisieren, sobald der Benutzer die entsprechende Aktion anstößt.

Wenn Ihre Anwendung sich bei der Abfrage und Speicherung von Daten allein auf Netzwerkkommunikation stützt, sollten Sie überlegen, ob Sie nicht eine SQLite-Datenbank (oder andere Speicherverfahren) nutzen, um die Anwendung auch dann nutzbar zu halten, wenn sie sich im Offline-Modus befindet, weil keine Netzwerkverbindung hergestellt werden kann. Und das geschieht erstaunlicherweise nicht gerade selten. Wenn sich Ihre Anwendung nicht auch ohne funktionierende Netzwerkverbindung nutzen lässt, hagelt es sehr wahrscheinlich negative Beurteilungen im Google Play Store (und eine Menge Aufforderungen, die App auch offline nutzbar zu machen). Auch wenn das im Rahmen Ihrer Android-Anwendungsentwicklung ein wenig Mehrarbeit bedeutet, zahlt es sich hinsichtlich der Benutzerfreundlichkeit zehnfach aus.

Die SQLite-Datenbank für Ihre App erstellen

Ihre Terminplaner-Anwendung muss Termine erstellen, auslesen, aktualisieren und löschen können. Dazu müssen die Daten irgendwo gespeichert und wieder abgerufen werden können, wofür sich eine SQLite-Datenbank am besten eignet.

Gelegentlich ist in diesem Zusammenhang die Rede von CRUD-Operationen. Das Kürzel steht für »Create, Read, Update, and Delete«, also das Erstellen, Lesen, Aktualisieren und Löschen von Datensätzen.

Die Arbeitsweise der SQLite-Datenbank

Die beiden Fragmente in der Terminplaner-App müssen verschiedene Aufgaben erfüllen. Dazu gehören bei ReminderEditFragment die folgenden Schritte:

1. Neue Datensätze erstellen

2. Einzelne Datensätze zur Anzeige und Bearbeitung auslesen

3. Vorhandene Datensätze aktualisieren

ReminderListFragment erledigt die folgenden Aufgaben:

1. Alle Termine zur Darstellung auf dem Bildschirm auslesen

2. Einzelne Termine nach einem Klick-Ereignis (Lang-Druck auf ein Element) und entsprechender Auswahl im Kontextmenü löschen

Um mit einer SQLite-Datenbank arbeiten zu können, müssen Sie mit der Datenbank über ContentProvider kommunizieren. Dabei wird die Datenbankkommunikation häufig möglichst weitgehend von den Activity- und Fragment-Objekten getrennt. Die Datenbankfunktionen werden in eine andere Java-Datei (und bei umfangreicheren Datenbankteilen in ein Paket) ausgelagert, um mehrere Funktionsebenen innerhalb der Anwendung zu schaffen. Wenn Sie dann den die Datenbank betreffenden Code ändern müssen, wissen Sie, dass Sie sich dabei auf eine Stelle beschränken können.

Einen ContentProvider für den Datenbankcode erstellen

Um einen ContentProvider in Ihrem Android-Projekt zu erstellen, der den datenbankspezifischen Code aufnehmen soll, öffnen Sie die Datei ReminderProvider.java, die Sie in Kapitel 9 als noch leeres Gerüst erstellt haben.

Die Schlüsselelemente einer Datenbank definieren

Zunächst müssen Sie einige Schlüsselfelder definieren, bevor Sie Ihre Datenbank erstellen und öffnen können.

Fügen Sie dazu in die Klasse ReminderProvider den Code aus Listing 12.1 ein.

package com.dummies.android.terminplaner;

import android.content.ContentProvider;
import android.database.sqlite.SQLiteDatabase;

public class ReminderProvider extends ContentProvider {

// Datenbank−Konstanten

private static final int DATABASE_VERSION = 1; →8

private static final String DATABASE_NAME = "data"; →9

private static final String DATABASE_TABLE = "reminders"; →10

// Datenbank−Spalten

public static final String COLUMN_ROWID = "_id"; →13

public static final String COLUMN_DATE_TIME = "reminder_date_time";  →14

public static final String COLUMN_BODY = "body"; →15

public static final String COLUMN_TITLE = "title"; →16

private static final String DATABASE_CREATE = "create table " →18

+ DATABASE_TABLE + " (" + COLUMN_ROWID

+ " integer primary key autoincrement, " + COLUMN_TITLE

+ " text not null, " + COLUMN_BODY + " text not null, "

+ COLUMN_DATE_TIME + " integer not null);";

private SQLiteDatabase mDb; →24

@Override

public boolean onCreate() { →27

mDb = new DatabaseHelper(getContext()).getWritableDatabase();  →28

return true;

}

}

Listing 12.1: Die Konstanten, Felder und Konstruktoren der Klasse ReminderProvider

Achten Sie auf die Leerzeichen in den Strings, aus denen die SQL-Anweisungen zusammengesetzt werden!

Die einzelnen Zeilen werden nachfolgend ausführlicher erläutert:

→8: Hier wird die Version der Datenbank angegeben. Wenn Sie den Aufbau Ihrer Datenbank ändern, sollten Sie die Version erhöhen und die Implementierung der Methode onUpgrade() von DatabaseHelper ändern.

→9: Hierbei handelt es sich um den Namen der Datenbankdatei im Android-Dateisystem.

→10: Hierbei handelt es sich um den Namen der Datenbanktabelle zur Aufnahme der Termine.

→13 bis  →16: In diesen Zeilen werden die Spaltennamen der Tabelle definiert.

→18: In dieser Zeile wird das Skript erzeugt, mit dem die Datenbank erstellt wird. Dabei werden die verschiedenen Werte zu einer einzigen SQL-Anweisung zusammengezogen, die dann die verschiedenen Spalten der Datenbank anlegt.

→24: Hierbei handelt es sich um eine Instanz des SQLite-Datenbankobjekts auf Klassenebene, über die Sie Datensätze anlegen, lesen, aktualisieren und löschen können.

→27: Erzeugt ContentProvider und ruft onCreate() auf.

→28: Ruft die Methode getWritableDatabase() des DatabaseHelper-Objekts auf.

Damit kann die SQL-Datenbank mit dem in der Variablen DATABASE_CREATE enthaltenen Skript erstellt werden.

Mehr zur Datenbanktabelle und die Komponenten des Skripts erfahren Sie im nächsten Abschnitt. Mit DatabaseHelper oder der Datenbanktabelle befasst sich dann der darauffolgende Abschnitt »Die Datenbanktabelle erstellen«.

Die SQL-Tabelle visualisieren

In SQL handelt es sich beim Tabellenobjekt um ein Konstrukt, das die zu verwaltenden Daten enthält. Datenbanktabellen kann man sich wie Arbeitsblätter in einem Tabellenkalkulationsprogramm vorstellen. Die Zeilen entsprechen den Datensätzen und die Spalten den zugehörigen Datenfeldern. In Listing 12.1 wurden die Spaltennamen der Datenbank definiert. Diese würden den Spaltenüberschriften im Arbeitsblatt entsprechen (siehe Tabelle 12.1). Die Zeilen enthalten einen Wert in den einzelnen Spalten. Konzeptionell werden die Daten so in SQLite gespeichert.

_id | title | body | reminder_date_time
1 | Flugtickets bestellen | Im Reisebüro Tickets bestellen | 2013-05-15 16:15
2 | Auszeit anberaumen | E-Mail-Manager im Büro umkonfigurieren | 2013-05-17 15:00
3 | Urlaub nehmen | Ja! Endlich die nötige Entspannung. | 2013-06-10 14:30
4 | Rechnungen bezahlen | Überweisungsformulare ausfüllen | 2013-05-10 12:15

Tabelle 12.1: Visuelle Darstellung von Daten in der Terminplaner-App

In Zeile 18 wird in Listing 12.1 das Skript zusammengestellt, das die Datenbank über den SQL-Befehl create erstellt. Dazu werden verschiedene Konstanten zusammengeführt. Wenn SQLite das Skript ausführt, wird eine Tabelle namens reminders in einer Datenbank namens data erstellt.

Die Spalten werden im Skript zur Erstellung der Datenbank wie folgt definiert:

"create table " + DATABASE_TABLE: Dieser Teil des Skripts sorgt dafür, dass SQLite eine Datenbanktabelle namens reminders (dem Wert der Variablen DATABASE_ TABLE) erstellt.

COLUMN_ROWID: Diese Eigenschaft fungiert als Kennung für den Termin. Dieser Spalte werden die Attribute integer primary key autoincrement zugeordnet. integer gibt den Datentyp für die Spalte vor. Über primary key wird die Spalte COLUMN_ROWID zum Primärschlüssel der Termine. Das Attribut autoincrement sorgt dafür, dass SQLite beim Einfügen eines neuen Termins jeweils den Wert von COLUMN_ROWID inkrementiert und den nächsthöheren Integer-Wert verwendet. Wenn beispielsweise bereits Zeilen mit den Werten 1, 2 und 3 von COLUMN_ROWID existieren, wird der Wert der Spalte beim nächsten eingefügten Datensatz auf 4 gesetzt.

COLUMN_TITLE: Hierbei handelt es sich um den vom Benutzer für den Termin angegebenen Titel, wie zum Beispiel »Flugtickets bestellen«. Das Attribut text sorgt dafür, dass SQLite eine Spalte mit diesem Datentyp anlegt. Das Attribut not null legt fest, dass Benutzer für diese Spalte einen Wert eingeben müssen. (Leere Datenfelder haben in SQL-Datenbanken den Wert null.)

COLUMN_BODY: Hierbei handelt es sich um die Beschreibung des Termins. Die Attribute dieser Spalte sind mit denen von COLUMN_TITLE identisch.

COLUMN_DATE_TIME: Hier werden Datum und Uhrzeit des Erinnerungstermins gespeichert. Das Feld speichert einen Integer-Wert, weil SQLite keinen speziellen Datentyp für die Speicherung von Datum und/oder Uhrzeit kennt. Also wandeln Sie das Calender-Objekt in einen Java-Wert des Datentyps long um, der dann problemlos als SQL-Ganzzahl verarbeitet werden kann.

Mehr Informationen über den Umgang mit Datum und Uhrzeit in SQLite erfahren Sie aus der Dokumentation unter www.sqlite.org/datatype3.html# datetime.

Die Datenbanktabelle erstellen

Nun können Sie Ihre erste Datenbanktabelle erstellen. Dazu implementieren Sie SQLiteOpenHelper. Den entsprechenden Code für die Klasse ReminderProvider finden Sie in Listing 12.2. Damit wird eine in die Klasse RemindersDbAdapter eingebettete Java-Klasse erstellt.

private static class DatabaseHelper extends SQLiteOpenHelper { →1

DatabaseHelper(Context context) {

super(context, DATABASE_NAME, null, DATABASE_VERSION); →3

}

@Override

public void onCreate(SQLiteDatabase db) { →7

db.execSQL(DATABASE_CREATE); →8

}

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion,

int →12

throw new UnsupportedOperationException();

}

}

Listing 12.2: Eine Datenbanktabelle erstellen

Nachfolgend werden die Codezeilen erläutert:

→1: Die Implementierung von SQLiteOpenHelper

→3: Der Aufruf des Basiskonstruktors SQLiteOpenHelper, der eine Datenbank erstellt, öffnet und/oder verwaltet. Die Datenbank wird nicht wirklich erstellt oder geöffnet, bis getReadableDatabase() oder getWriteableDatabase() für die Instanz SQLiteOpenHelper aufgerufen wird.

→7: Die Methode onCreate(), die zum Erstellen der Datenbank aufgerufen wird

→8: Hier werden Ihre Datenbank und Ihre Datenbanktabelle erstellt. Die Methode execSQL() übernimmt einen String mit einem SQL-Skript als Parameter. Dabei handelt es sich um den SQL-Code aus Listing 12.1, der die Datenbanktabelle erstellt.

→12: Die Methode onUpgrade() wird benutzt, wenn Sie eine bereits bestehende Datenbank aktualisieren müssen.

Ihre Datenbank aktualisieren

Stellen Sie sich vor, dass Sie Ihre Anwendung veröffentlicht haben und sie bereits von 10.000 Benutzern installiert wurde, die die darin definierte Datenbanktabelle aktiv nutzen. Und die Benutzer schätzen Ihre Anwendung auch! Einige Benutzer wünschen sich zudem weitere Funktionen und informieren Sie darüber. Nun entschließen Sie sich, eine Funktion zu implementieren, für die Sie den Datenbankaufbau ändern müssen. Daher wollen Sie innerhalb des Aufrufs onUpgrade() zur Aktualisierung der Datenbank die SQL-Anweisung ALTER ausführen. Sie könnten sicherlich die komplette vorhandene Datenbank löschen und dann eine neue anlegen. Das wollen Sie aber nicht, weil dabei alle Daten des Benutzers gelöscht werden würden! Stellen Sie sich vor, Sie würden Ihre bevorzugte Terminplaner-Anwendung aktualisieren, nur um anschließend feststellen zu müssen, dass dadurch alle zuvor erstellten Termine gelöscht wurden! Das wäre ein gravierender Fehler.

ContentProvider-URLs auflösen

Wenn Sie ContentProvider unter Android verwenden, benutzen Sie URLs für die anzusprechenden Daten. Typischerweise können Sie URLs verwenden, um ein bestimmtes Datenelement, wie beispielsweise einen einzelnen Termin oder auch alle Termine in Ihrer Datenbank anzusprechen. Wenn Sie dort andere Datentypen speichern, können Sie auch dafür URLs verwenden.

In Ihrer App verwenden Sie zwei Arten von URLs, nämlich content://com.dummies.andro id.terminplaner.ReminderProvider/reminder, um eine Liste aller Termine in Ihrer Datenbank abzurufen, und content://com.dummies.android.terminplaner.ReminderPro vider/reminder/9, um einen bestimmten Termin aus der Datenbank abzurufen. (Im Beispiel handelt es sich um den Termin mit der Kennung 9.)

Diese ContentProvider-URLs ähneln zweifellos den Ihnen bereits bekannten URLs. Nachfolgend werden die Hauptunterschiede beschrieben:

content://: Ein ContentProvider beginnt mit content:// und nicht mit http://.

com.dummies.android.terminplaner.ReminderProvider: Der zweite Teil der URL gibt die Quelle (ReminderProvider) des Inhalts an. Auch wenn Sie diesen String nahezu beliebig wählen können, wird hier üblicherweise gemäß Konvention Ihr voll qualifizierter ContentProvider-Name verwendet.

reminder: Der dritte Teil der URL gibt den Pfad an, bei dem es sich hier um den Datentyp handelt, nach dem gesucht wird. Dieser String gibt an, welche Tabelle in der Datenbank gelesen werden soll. Wenn die App mehrere Datentypen in der Datenbank speichert (beispielsweise eine Liste der Benutzer neben einer Liste der Termine), könnte ein zweiter Pfadtyp user genannt werden.

9: In der ersten URL endet der Pfad mit reminder. In der zweiten URL wird der Pfad jedoch fortgesetzt und enthält zudem noch die spezifische ID des angeforderten Termins.

Bevor Sie das ContentProvider-Element verwenden können, müssen Sie noch dafür sorgen, dass das Element in der Datei AndroidManifest.xml aufgeführt wird. Fügen Sie dazu den folgenden Code vor dem Tag </application> ein:

<provider

android:name="com.dummies.android.terminplaner.ReminderProvider"

android:authorities="com.dummies.android.terminplaner.ReminderProvider"

android:exported="false"

/>

Diese Zeilen teilen Android mit, dass sich ein ContentProvider namens Reminder Provider um die Verarbeitung der URLs kümmern wird, die der spezifischen Quelle com.dummies.android.terminplaner.ReminderProvider zugeordnet werden können. Sie geben auch an, dass die darin enthaltenen Daten nicht in andere Apps auf dem Gerät des Benutzers exportiert werden. Allgemein sollten Sie immer exported="false" setzen, sofern Sie Ihren ContentProvider nicht wirklich für andere Apps bereitstellen wollen.

Nun müssen Sie den Code für die Unterstützung dieser URLs in Ihren ContentProvider hinzufügen. Öffnen Sie ReminderProvider.java und fügen Sie die folgenden Zeilen zur Klasse hinzu:

// ContentProvider URI und Quelle
public static String AUTHORITY = "com.dummies.android. terminplaner.ReminderProvider"; →2
public static final Uri CONTENT_URI = Uri.parse ("content://" + AUTHORITY

+ "/reminder"); →4

// Für Begriffe oder Suche nach einzelnen Definitionen verwendete  // MIME−Typen
public static final String REMINDERS_MIME_TYPE = ContentResolver. CURSOR_DIR_BASE_TYPE

+ "/vnd.com.dummies.android.terminplaner.reminder"; →8
public static final String REMINDER_MIME_TYPE = ContentResolver. CURSOR_ITEM_BASE_TYPE

+ "/vnd.com.dummies.android.terminplaner.reminder";

// UriMatcher−Zeug
private static final int LIST_REMINDER = 0; →13
private static final int ITEM_REMINDER = 1;
private static final UriMatcher sURIMatcher = buildUriMatcher();  →15

/**

* Erstellt ein UriMatcher−Objekt für Suchvorschläge und Kurzabfragen.

*/
private static UriMatcher buildUriMatcher() {

UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); →22

matcher.addURI(AUTHORITY, "reminder", LIST_REMINDER); →23

matcher.addURI(AUTHORITY, "reminder/#", ITEM_REMINDER); →24

return matcher;

}

/**

* Diese Methode wird benötigt, um die unterstützten Typen abfragen zu

* können. Sie wird auch in der Methode query() genutzt, um den Typ

* der empfangenen URI festzustellen.

*/

@Override
public String getType(Uri uri) { →33

switch (sURIMatcher.match(uri)) {

case LIST_REMINDER:

return REMINDERS_MIME_TYPE;

case ITEM_REMINDER:

return REMINDER_MIME_TYPE;

default:

throw new IllegalArgumentException("Unknown Uri: " + uri);

}

}

Dieser Codeblock mag vielleicht ein wenig irritierend wirken, er besteht aber vorwiegend aus Konstanten mit nur einer nützlichen Methode (getType()). Die nummerierten Zeilen funktionieren wie nachfolgend beschrieben:

→2: Die Quelle für den ContentProvider, bei der es sich laut Konvention um denselben wie den voll qualifizierten Klassennamen handelt. Dieser Wert muss demjenigen entsprechen, den Sie für die Provider-Quellen zur Datei AndroidManifest.xml hinzugefügt haben.

→4: Die Basis-URL (oder URI) für ContentProvider. Wenn Ihre App Daten von dieser URL abruft, leitet Android die Anforderung jeweils an diesen ContentProvider weiter.

ContentProvider unterstützt zwei Arten von URLs, eine für die Auflistung aller Termine und eine für die Ausgabe eines spezifischen Termins.

Der erste URL-Typ ist CONTENT_URI und der zweite CONTENT_URI mit am Ende angehängter Terminkennung.

→8: Da ContentProvider zwei Arten von Daten unterstützt, werden zwei Typen (oder MIME-Typen) für diese Daten definiert. Bei MIME-Typen handelt es sich einfach um Strings, die im Web häufig für die Identifikation von Datentypen verwendet werden. Für HTML-Inhalte wird im Web beispielsweise typischerweise der MIME-Typ text/html benutzt, während für MP3-Audiodateien audio/mpeg3 verwendet wird. Da die Termine keinem bekannten Standardtyp entsprechen, können Sie sich eigene Strings für den MIME-Typ ausdenken, sofern Sie sich nur an die Android- und MIME-Konventionen halten.

Der MIME-Typ für die Liste beginnt mit ContentResolver.CURSOR_DIR_BASE_TYPE, während der für Einzeltermine mit ContentResolver.CURSOR_ITEM_BASE_TYPE anfängt. DIR entspricht der Liste und ITEM dem Einzelelement, was sich recht leicht merken lässt.

Diese Zeile prüft auch, ob der Subtyp (der dem / folgt) mit vnd beginnt. An den Subtyp schließen sich der voll qualifizierte Klassenname und der Typ der Daten an, bei dem es sich hier um com.dummies.android.terminplaner und reminder handelt. Unter http://developer.android.com/reference/android/content/ContentResolver.html finden Sie weitere Informationen zu den Android-Konventionen für MIME-Typen.

→13: Hier werden weitere int-Konstanten definiert, um zwischen Listen- und Element-Typ zu unterscheiden.

→15: UriMatcher wird benutzt, um den URL-Typ (Liste oder Element) festzulegen. UriMatcher erstellen Sie mit der Methode buildUriMatcher() in Zeile 21.

→22: Erzeugt das Objekt UriMatcher, das angeben kann, ob eine angegebene URL vom Listen- oder Element-Typ ist. Der Parameter UriMatcher.NO_MATCH teilt der App mit, welchen Vorgabewert sie bei einer Übereinstimmung zurückgeben soll.

→23: Definiert den Listen-Typ. Alle URLs der Quelle com.dummies.android.terminplaner.ReminderProvider und dem Pfad "reminder" geben den Wert LIST_REMINDER zurück.

→24: Definiert den Listen-Typ. Alle URLs der Quelle com.dummies.android.terminplaner.ReminderProvider und einem Pfad der Form reminder/# (wobei # für eine Zahl steht) geben den Wert ITEM_REMINDER zurück.

→33: Verwendet UriMatcher aus Zeile 15, um anzugeben, welcher MIME-Typ zurückgegeben werden soll. Wenn es sich bei der URL um eine Listen-URL handelt, wird REMINDERS_MIME_TYPE zurückgegeben, bei einer Element-URL lautet der Rückgabewert REMINDER_MIME_TYPE.

Termine mit SQLite erstellen und bearbeiten

Nachdem Sie nun einen ContentProvider erstellt haben, können Sie sich der Eingabe von Terminen zuwenden. Dazu fügen Sie Datensätze in Ihre Datenbank ein. Anschließend müssen die Termine in ReminderListFragment auf dem Bildschirm aufgelistet werden. Benutzer können dann einzelne Termine zur Bearbeitung antippen oder sie nach Lang-Druck über das Kontextmenü löschen. Diese Benutzerinteraktionen umfassen das Erstellen, Lesen, Aktualisieren und Löschen von Datensätzen (Terminen).

Einen Termineintrag einfügen

Wenn Sie erst einmal wissen, wie es geht, lassen sich Termine recht leicht einfügen. Um Ihren ersten Termin in die SQLite-Datenbank einzufügen, erstellen Sie die Methode OnClickListener() für die SPEICHERN-Schaltfläche, die die folgenden Aufgaben übernimmt:

1. Werte der EditText-Views übernehmen,

2. diese mit Hilfe eines ContentResolvers in der ReminderProvider-Datenbank speichern und

3. die Benutzerschnittstelle durch Anzeige eines Toasts und Schließen der Edit-Activity aktualisieren.

Wenn Sie Ihren ersten Termin eingefügt haben, sollten Sie genug über die Nutzung der Klasse ReminderProvider wissen, um sie für weitere Aufgaben einsetzen zu können. In den nächsten Abschnitten werden Sie erfahren, wie Sie ReminderProvider und damit die CRUD-Operationen vollständig implementieren können.

Die Werte auf dem Bildschirm in der Datenbank speichern

Wenn Benutzer Termine erstellen, findet dies in der Methode OnClickListener des Elements mConfirmButton in ReminderEditFragment statt. Dort reagiert die App darauf, wenn Benutzer die SPEICHERN-Schaltfläche anklicken. Wenn mRowId für das Fragment den Wert 0 hat, will der Benutzer einen neuen Termin hinzufügen. Wenn der Wert von mRowId größer als 0 ist, will der Benutzer einen vorhandenen Termineintrag bearbeiten. Erst müssen Sie einige Parameter einrichten, dann müssen Sie mit Hilfe von ContentResolver die Erstellung oder Aktualisierung abschließen und schließlich das Ergebnis verarbeiten und den Benutzer benachrichtigen.

Fügen Sie das folgende OnClickListener-Element für mConfirmButton in ReminderEditFragment.onCreateView() hinzu:

mConfirmButton.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

ContentValues values = new ContentValues(); →4

values.put(ReminderProvider.COLUMN_ROWID, mRowId); →5

values.put(ReminderProvider.COLUMN_TITLE, mTitleText.getText()

.toString()); →7

values.put(ReminderProvider.COLUMN_BODY, mBodyText.getText()

.toString());

values.put(ReminderProvider.COLUMN_DATE_TIME,

mCalendar.getTimeInMillis()); →11

if (mRowId == 0) { →13

Uri itemUri = getActivity().getContentResolver().insert(

ReminderProvider.CONTENT_URI, values); →15

mRowId = ContentUris.parseId(itemUri); →16

} else {

int count = getActivity().getContentResolver().update(

ContentUris.withAppendedId(

ReminderProvider.CONTENT_URI, mRowId),

values, null, null); →21

if (count != 1) →22

throw new IllegalStateException(mRowId)

+ " konnte nicht aktualisiert werden.";

}

Toast.makeText(getActivity(),

getString(R.string.task_saved_message),

Toast.LENGTH_SHORT).show(); →29

getActivity().finish (); →30

}

});

Nachfolgend werden die Codezeilen erläutert:

→4: Von ContentResolver wird eine Abfrage ausgeführt, die letztlich von ReminderProvider verarbeitet wird. Genauer ausgedrückt fügt diese Zeile einen Termineintrag ein oder aktualisiert ihn. Das ContentValues-Objekt gibt den zu aktualisierenden Datensatz und die zu verwendenden Daten an.

→5: Die Zeilenkennung des Termineintrags. Beim Wert 0 wird ein neuer Datensatz erzeugt. Wenn Benutzer einen bereits bestehenden Termineintrag aktualisieren, wird die spezifische ID übergeben.

→7: Der Titel des Eintrags, bei dem es sich um die Benutzereingabe für die EditText-View mTitleText handelt. Die App ruft mTitleText.getText() auf, um eine CharSequence zu erhalten, und ruft dann zum Erzeugen des Strings toString() auf.

→11: Enthält die vom Benutzer für den Termineintrag ausgewählten Werte für Datum und Uhrzeit. Ruft auch Calendar.getTimeInMillis() auf und legt das Ergebnis in einem ContentValues-Objekt ab. (SQLite bietet keine Möglichkeiten zur direkten Darstellung von Datumswerten, weshalb Sie das Objekt in einen long-Wert umwandeln müssen.)

→13: Prüft, ob Benutzer einen neuen Termineintrag hinzufügen oder einen vorhandenen aktualisieren wollen. Wenn mRowID den Wert 0 hat, fügt der Benutzer einen neuen Termin hinzu. Andernfalls will er einen vorhandenen Termineintrag bearbeiten.

→15: Ruft die Methode getContentResolver() der Activity und dann insert() auf, um einen Termineintrag in die Datenbank einzufügen. insert() müssen Sie mit den einzufügenden Daten und der URI der Tabelle, in die sie eingefügt werden sollen, zwei Parameter übergeben. insert() liefert Ihnen dann die vollständige URI des eingefügten Datensatzes zurück.

→16: Wertet die ID der URI des eingefügten Eintrags aus. Dazu wird ContentUris.parseId() verwendet und der Rückgabewert in mRowId gespeichert.

→21: Um die Aktualisierung durchzuführen, wird hier die Methode update() von ContentResolver aufgerufen. Anstelle der Basis-URI wird die URI des zu aktualisierenden Elements übergeben, die durch einen Aufruf von ContentUris.withAppendedId() mit Übergabe der Basis-URI und der Termin-ID des Termins ermittelt wird. Die beiden letzten Nullparameter geben an, dass es sich um eine einfache SQL-Abfrage handelt, bei der nur eine einzige Zeile aktualisiert werden muss.

→22: Gibt an, wie viele Datensätze aktualisiert wurden. Der Wert sollte hier immer 1 lauten. Wenn daher keine oder mehrere Zeilen aktualisiert wurden, sollte eine Fehlermeldung angezeigt werden.

→29: Toast.makeText() wird verwendet, um kurz eine Erfolgsmitteilung anzeigen zu können. Dann wird diese mit show() auf dem Bildschirm ausgegeben.

Der vom Toast angezeigte Text wird in der Variablen task_saved_message abgelegt. Diese müssen Sie in strings.xml definieren. Fügen Sie dazu in diese Datei die Zeile <string name="task_saved_message">Task wurde gespeichert ein.</string> ein.

→30: Ruft die Methode finish() der Aktivität auf, um die Bearbeitung des Datensatzes abzuschließen und die Terminliste wieder anzuzeigen.

Die vollständige Implementierung von ReminderProvider

Manchmal ist es besser, alle Elemente eines Programms gleich auf einen Blick zu sehen, als diese einzeln zu betrachten. So verhält es sich auch beim Arbeiten mit der Klasse RemindersDbAdapter in SQLite. Listing 12.3 enthält die vollständige Implementierung von ReminderProvider, damit Sie einen Eindruck davon bekommen, womit Sie es zu tun haben.

package com.dummies.android.terminplaner;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;

public class ReminderProvider extends ContentProvider {

// URI des Inhaltsanbieters und Quelle der Daten

public static String AUTHORITY =

"com.dummies.android.terminplaner.Reminder
Provider";

public static final Uri CONTENT_URI = Uri.parse ("content://" + AUTHORITY

+ "/reminder");

// Für Begriffe oder Suche nach einzelnen Definitionen   // verwendete MIME−Typen

public static final String REMINDERS_MIME_TYPE =

ContentResolver.CURSOR_DIR_BASE_TYPE

+ "/vnd.com.dummies.android.terminplaner .reminder";

public static final String REMINDER_MIME_TYPE =

ContentResolver.CURSOR_ITEM_BASE_TYPE

+ "/vnd.com.dummies.android.terminplaner .reminder";

// Datenbankspalten

public static final String COLUMN_ROWID = "_id";

public static final String COLUMN_DATE_TIME = "reminder_date_time";

public static final String COLUMN_BODY = "body";

public static final String COLUMN_TITLE = "title";

// Datenbank − Hilfskonstanten

private static final int DATABASE_VERSION = 1;

private static final String DATABASE_NAME = "data";

private static final String DATABASE_TABLE = "reminders";

// SQL−Anweisung zusammensetzen

private static final String DATABASE_CREATE = "create table "

+ DATABASE_TABLE + " (" + COLUMN_ROWID

+ " integer primary key autoincrement, " + COLUMN_TITLE

+ " text not null, " + COLUMN_BODY + " text not null, "

+ COLUMN_DATE_TIME + " integer not null);";

// UriMatcher

private static final int LIST_REMINDER = 0;

private static final int ITEM_REMINDER = 1;

private static final UriMatcher sURIMatcher = buildUriMatcher();

private SQLiteDatabase mDb;

/**

* Erzeugt ein UriMatcher−Objekt für Suchvorschläge und deren

Aktualisierung

*/

private static UriMatcher buildUriMatcher() {

UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

// Um Definitionen zu übernehmen ...

matcher.addURI(AUTHORITY, "reminder", LIST_REMINDER);

matcher.addURI(AUTHORITY, "reminder/#", ITEM_REMINDER);

return matcher;

}

@Override

public boolean onCreate() {

mDb = new DatabaseHelper(getContext()).getWritableDatabase();

return true;

}

@Override

public Cursor query(Uri uri, String[] ignored1, String ignored2,

String[] ignored3, String ignored4) { →75

String[] projection = new String[] { ReminderProvider.COLUMN_ROWID,

ReminderProvider.COLUMN_TITLE, ReminderProvider.COLUMN_BODY,

ReminderProvider.COLUMN_DATE_TIME }; →79

// UriMatcher verwenden, um den Abfragetyp festzustellen

// und die Datenbankanfrage entsprechend zu formatieren

Cursor c;

switch (sURIMatcher.match(uri)) { →84

case LIST_REMINDER: →85

c = mDb.query(ReminderProvider.DATABASE_TABLE, projection,

null, null, null, null, null); →87

break;

case ITEM_REMINDER: →89

c = mDb.query(ReminderProvider.DATABASE_TABLE, projection, →90

ReminderProvider.COLUMN_ROWID + "=?", new

String[] { Long

.toString(ContentUris.parseId(uri)) }, →92

null, null, null, null);

if (c != null && c.getCount() > 0) { →94

c.moveToFirst(); →95

}

break;

default: →98

throw new IllegalArgumentException("Unbekannte URI: " + uri);

}

c.setNotificationUri(getContext().getContentResolver(), uri);  →102

return c; →103

}

@Override

public Uri insert(Uri uri, ContentValues values) { →107

values.remove(ReminderProvider.COLUMN_ROWID); →108

long id = mDb.insertOrThrow(ReminderProvider.DATABASE_TABLE,  →109

null, values);

getContext().getContentResolver().notifyChange(uri, null);  →111

return ContentUris.withAppendedId(uri, id); →112

}

@Override

public int delete(Uri uri, String ignored1, String[] ignored2) {  →116

int count = mDb.delete(ReminderProvider.DATABASE_TABLE,

ReminderProvider.COLUMN_ROWID + "=?",

new String[] { Long.toString(ContentUris.parseId (uri)) }); →119

if (count > 0) →120

getContext().getContentResolver().notifyChange(uri, null);

return count; →122

}

@Override

public int update(Uri uri, ContentValues values,

String ignored1, String[] ignored2) { →127

int count = mDb.update(ReminderProvider.DATABASE_TABLE,  values,

COLUMN_ROWID + "=?",

new String[] { Long.toString(ContentUris.parseId(uri)) });  →130

if( count>0 )

getContext().getContentResolver().notifyChange(uri, null); →132

return count; →133

}

/**

* Diese Methode wird benötigt, um die unterstützten Typen abzufragen.

* Auch nützlich für Ihre eigene query()−Methode, um den übernommenen

* URI−Typ zu ermitteln.

*/

@Override

public String getType(Uri uri) {

switch (sURIMatcher.match(uri)) {

case LIST_REMINDER:

return REMINDERS_MIME_TYPE;

case ITEM_REMINDER:

return REMINDER_MIME_TYPE;

default:

throw new IllegalArgumentException("Unbekannte Uri: " + uri);

}

}

// Code für SQLite−DatabaseHelper; aus Platzgründen weggelassen

}

Listing 12.3: Die vollständige Implementierung der Klasse ReminderProvider

Die Codezeilen werden nachfolgend erläutert:

→75: Die Methode query() übernimmt einen Termin oder eine Terminliste. Bei der übergebenen URI kann es sich entweder um einen Listen- oder Elementtyp handeln.

→79: Erzeugt eine Variable namens projection, die den verschiedenen Abfragen mitteilt, welche Datenspalten Benutzer abfragen wollen. Dazu zählen hier COLUMN_ROWID, COLUMN_TITLE, COLUMN_BODY und COLUMN_DATE_TIME.

→84: Ruft URIMatcher.match() auf, um festzustellen, ob es sich um eine Listen- oder eine Element-URI handelt.

→85 bis  →87: Für eine Listen-URI ruft diese Zeile die Methode query() der SQLiteDatabase auf, um alle Zeilen für alle Termine zu übernehmen.

Die Nutzung dieser Methode und ihre Parameter werden ausführlich im Abschnitt »Die query-Operation zum Auslesen von Datensätzen verstehen« weiter hinten in diesem Kapitel erläutert.

→89/ →90: Für eine Element-URI ruft diese Zeile die Methode query() der SQLiteDatabase auf, um alle Spalten des angegebenen Termins zu übernehmen. Dabei wird angegeben, welche Datenbanktabellen und Spalten zurückgegeben werden sollen.

→92: Der erste Parameter ist ein String der Form COLUMN_ROWID=?, wobei der Wert für ? vom nächsten Parameter geliefert wird. Bei diesem nächsten Parameter handelt es sich um einen Array mit Strings, die jeweils auf ein Fragezeichen abgebildet werden. Da der Parameter ein Fragezeichen enthält, muss der Array einen String enthalten, bei dem es sich um die ID des Termins (aus der URI) handelt.

→94/ →95: Wenn der Aufruf der Abfrage erfolgreich verläuft und mindestens ein Wert zurückgegeben wird, wird anschließend der erste Wert verarbeitet. Die Methode moveToFirst() des Cursor-Objekts sorgt dafür, dass der Datenbankzeiger (Cursor) auf den ersten Datensatz der Ergebnismenge gesetzt wird. Diese Methode wird nur aufgerufen, wenn Cursor nicht den Wert null hat. Der Datenbankzeiger wird nur deshalb nicht gleich auf den ersten Datenbanksatz gesetzt, weil es sich um eine Ergebnismenge handelt. Bevor die App den Datensatz verarbeiten kann, muss der Datensatz aufgesucht werden. Stellen Sie sich die Ergebnismenge wie eine Art Karteikasten vor, aus der Sie die einzelnen Karten erst herausnehmen müssen, um sie lesen zu können.

→98: Zeigt eine Fehlermeldung an, wenn es sich bei der URI weder um eine Listen- noch um eine Element-URI handelt.

→102: Ruft die Methode setNotificationUri() des Cursor-Objekts auf, um das Abfrageergebnis mit der URI zu verknüpfen. Es wird an ContentResolver und der aktualisierten URI übergeben.

Ein ContentProvider kann Benutzer über Änderungen an ihren Daten benachrichtigen. Wenn ein Fragment beispielsweise eine Listenansicht für Ihre Termindaten anzeigt, ein anderes Fragment einen bestimmten Termin bearbeitet und Sie diesen speichern, wird die Listenansicht im ersten Fragment darüber benachrichtigt, dass sich die Inhalte geändert haben, und automatisch aktualisiert.

→103: Gibt das Cursor-Objekt zurück, damit es vom Fragment bei der Verarbeitung der Ergebnisse benutzt werden kann.

→107: Diese Methode wird aufgerufen, wenn Benutzer einen Termin in die Datenbank einfügen.

→108: Da Sie beim Einfügen von Terminen keine ID vorgeben können, wird hier ein möglicherweise vorhandener ID-Parameter entfernt. Die Datenbank verwendet immer die nächste verfügbare Datenbankkennung (ID).

→109: Fügt den Termin unter Verwendung der übergebenen Werte in die Datenbank ein.

Die Verwendung der Methode insertOrThrow() und deren Parameter werden im nachfolgenden Abschnitt ausführlich beschrieben.

→111: Ruft die Methode notifyChange() von ContentResolver für diese URI auf, bei der es sich um das Gegenstück des Aufrufs von setNotificationUri() in Zeile 102 handelt. Wenn Sie die Tabelle ändern, indem Sie ein Element einfügen, müssen Sie alle möglicherweise noch schwebenden Abfragen dieser Tabelle darüber benachrichtigen, dass sich das Abfrageergebnis potenziell geändert haben kann.

→112: Hier wird letztlich die Element-URI für diesen Termin erzeugt, indem an die Basis-ID die erzeugte ID angehängt wird.

→116: Die Methode delete() für ContentProvider.

→119: Die Methode ContentUris.parseId() wird dazu verwendet, um die id des Tasks anhand der URI zu ermitteln, und ruft die Methode delete() der SQLite-Datenbank auf, um einen Termineintrag aus der Datenbank zu löschen.

Die Verwendung der Methode delete() und deren Parameter werden im Abschnitt »Die delete-Operation verstehen« weiter hinten in diesem Kapitel ausführlich beschrieben.

→120: Wenn irgendwelche Einträge aus der Datenbank gelöscht wurden, werden alle relevanten Aktivitäten darüber benachrichtigt, dass die Datenbank aktualisiert wurde, damit sie ihre Abfragen aktualisieren können.

→122: Im Unterschied zu insert() gibt delete() die Anzahl der gelöschten Zeilen (Datensätze) zurück.

→127: Die Arbeitsweise der Methode update() ähnelt der der Methode delete().

→130: Die Methode update() ist für die Aktualisierung vorhandener Termineinträge mit neuen Daten zuständig.

Die Verwendung der Methode update() und deren Parameter werden im Abschnitt »Die update-Operation verstehen« weiter hinten in diesem Kapitel ausführlich beschrieben.

→132: Wenn alle Aktualisierungen der Datenbank abgeschlossen wurden, werden alle anderen relevanten Fragmente und Aktivitäten darüber informiert, damit sie ihre Abfragen aktualisieren können.

→133: Gibt die Anzahl der veränderten Zeilen zurück.

Eine CRUD-Routine akzeptiert eine ganze Reihe unterschiedlicher Parameter, die in den nachfolgenden Abschnitten ausführlicher erläutert werden.

Die insert-Operation verstehen

Beim Einfügen von Datensätzen (Zeilen) handelt es sich um eine recht einfache Operation, da dabei einfach nur eine neue Zeile in die Datenbank eingefügt wird. Sie rufen aber die Methode insertOrThrow() und nicht insert() auf, weil ein Ausnahmefehler ausgelöst werden soll, wenn beim Einfügen von Datensätzen in die Datenbank Probleme auftreten sollten. Die Methode insertOrThrow() akzeptiert die folgenden Parameter:

table: Der Name der Tabelle, in die Daten eingefügt werden sollen. Für den Wert wird die Konstante DATABASE_TABLE verwendet.

nullColumnHack: SQL erlaubt nicht das Einfügen völlig leerer Zeilen. Wenn der nächste Parameter ContentValues also leer ist, wird dieser Spalte explizit der Wert null zugewiesen und null als Wert übergeben.

values: Dieser Parameter legt die Anfangswerte fest, die als ContentValues-Objekte definiert wurden. Er enthält das eine neue Zeile definierende Schlüssel/Wert-Paar als Daten.

Die query-Operation zum Auslesen von Datensätzen verstehen

Mit der Methode query() können Sie Datenbankabfragen erstellen und damit in erster Linie Datensätze auslesen. Bei ihrer Verwendung wird auf der Basis der von Ihnen angegebenen Kriterien eine Ergebnismenge zurückgegeben. Dabei handelt es sich um ein Cursor-Objekt, das den wahlfreien Lese- und Schreibzugriff auf die von der Abfrage (query) zurückgegebene Ergebnismenge ermöglicht.

Die Methode query() akzeptiert die folgenden Parameter:

table: Der Name der Datenbanktabelle, die abgefragt werden soll. Für diesen Parameter übergebe ich den in der Konstanten DATABASE_TABLE enthaltenen Wert.

columns: Eine Liste der Spalten, die von der Abfrage zurückgegeben werden sollen. Bei Übergabe des Wertes null werden alle Spalten zurückgegeben, was aber deshalb nur selten erwünscht sein dürfte, weil dabei auch gar nicht benötigte Daten gelesen und zurückgegeben werden. Sollten Sie tatsächlich alle Spalten benötigen, können Sie aber natürlich null übergeben. Bei dieser App wird ein Array mit Strings übergeben, die die Namen der zurückzugebenden Spalten enthalten.

selection: Ein Filter, der beschreibt, welche Zeilen zurückgegeben werden sollen, und der als SQL-Klausel WHERE (ohne das Schlüsselwort WHERE selbst) formatiert wird. Bei der Übergabe von null werden alle Zeilen der Tabelle zurückgegeben. Diese Möglichkeit wird im Beispiel für die Listenoperationen genutzt. Wenn es sich um eine get-Operation handelt, die nur einen Termineintrag zurückgeben soll, übergeben Sie den Abfragestring COLUMN_ROWID=?. SQL weiß, dass Fragezeichen für einen spezifischen Wert in COLUMN _ROWID im Parameter selectionArgs stehen.

selectionArgs: Innerhalb von selection können Sie Fragezeichen (?) verwenden. Diese werden dann durch Werte aus selectionArgs in der Reihenfolge ersetzt, in der die Fragezeichen in selection auftreten. Diese Werte sind auf Strings beschränkt. Je nach Aufgabenstellung übergeben Sie entweder null oder einen String-Array, der die ID des zu übernehmenden Datensatzes enthält.

groupBy: Ein Filter, der beschreibt, wie Zeilen gefiltert werden sollen und der als SQL-Klausel GROUP BY (ohne die Schlüsselwörter GROUP BY selbst) formatiert wird. Wenn der Wert null übergeben wird, werden die Zeilen nicht in Gruppen zusammengefasst. Da in der Ergebnismenge keine Datengruppen benötigt werden, wird hier der Wert null übergeben.

having: Wenn mit Zeilengruppen gearbeitet wird, lässt sich darüber festlegen, welche Zeilengruppen in das Cursor-Objekt übernommen werden sollen. Wird der Wert null übergeben, werden alle Gruppen übernommen. Da dies beim Verzicht auf Gruppen notwendig ist, wird hier der Wert null übergeben.

orderBy: Gibt über die SQL-Klausel ORDER BY (ohne ORDER BY selbst) an, wie die Zeilen sortiert werden sollen. Bei Übergabe von null wird die vorgegebene Reihenfolge verwendet, bei der die Daten möglicherweise nicht sortiert sind. Da die zurückgegebenen Daten nicht sortiert werden sollen, können Sie hier den Wert null übergeben.

limit: Beschränkt die Anzahl der von der Abfrage zurückgegebenen Zeilen über die SQL-Klausel LIMIT. Bei Übergabe des Wertes null wird die LIMIT-Klausel nicht angewendet. Da alle Zeilen zurückgegeben werden sollen, ohne deren Anzahl irgendwie zu beschränken, wird hier der Wert null übergeben.

Die update-Operation verstehen

Bei der Aktualisierung eines Datensatzes in einer Datenbank werden einfach die ankommenden Parameter übernommen, um mit ihnen die Zielzellen innerhalb einer (oder mehrerer) angegebenen Datenbankzeile(n) zu ersetzen. Wie die nachfolgende Löschoperation kann auch update mehrere Zeilen betreffen. Sie müssen wissen, welche Parameter die Methode update() unterstützt und wie sie sich auf die Sätze in der Datenbank auswirken:

table: Die zu aktualisierende Tabelle. Für diesen Parameter wird der in der Konstanten DATABASE_TABLE enthaltene Wert verwendet.

values: Das ContentValues-Objekt, das die zu aktualisierenden Felder enthält.

whereClause: Über die WHERE-Klausel von SQL lassen sich die zu aktualisierenden Zeilen einschränken. Sie können die Datenbank zum Löschen einer Zeile mit einer bestimmten ID löschen, indem Sie ihr den Stringwert COLUMN_ROWID + "=?" übergeben und die ID in whereArgs angeben.

whereArgs: Weitere Argumente für whereClause. Sie übergeben die ID über das ? aus dem whereClause-Argument. Hier handelt es sich bei whereArgs um einen String-Array, der die ID des zu aktualisierenden Termins enthält. Für jeden ?-Platzhalter in whereClause sollte es immer genau einen Wert im Array whereArgs geben.

Die delete-Operation verstehen

Bei Verwendung der Methode delete() lassen sich die Löschkriterien in der Datenbank über verschiedene Parameter definieren. Eine delete-Anweisung kann keinen, einen, mehrere oder alle Datensätze in der Datenbank betreffen. Um nicht versehentlich Daten zu löschen, müssen Sie die Parameter der Methode delete()verstehen:

table: Die Tabelle, aus der Zeilen gelöscht werden sollen. Der Wert dieses Parameters wird über die Konstante DATABASE_TABLE bereitgestellt.

whereClause: Dabei handelt es sich um die optionale WHERE-Klausel, über die beim Löschen von Datensätzen die Auswahl eingeschränkt werden kann. Wenn Sie den Wert null übergeben, werden alle Zeilen einer Datenbanktabelle gelöscht. Dieser Wert wird durch manuelles Erstellen der WHERE-Klausel über den String COLUMN_ROWID + "=?" bereitgestellt.

whereArgs: Die optionalen Argumente der WHERE-Klausel. Sie übergeben die ID, die dem ? im whereClause-Argument zugewiesen wird. Hier handelt es sich bei whereArgs um einen String-Array, der die ID des zu löschenden Termins enthält. Für jeden ?-Platzhalter in whereClause sollte es immer genau einen Wert im Array whereClause geben.

Loader

Bei allen Ein-/Ausgabeoperationen, wie beispielsweise dem Lesen von Daten über ein Netzwerk oder von einem Datenträger bei der Nutzung von Datenbanken, müssen Sie einen Hintergrundprozess mit dieser Aufgabe betrauen. Wenn Sie vom Hauptthread der Bedienschnittstelle aus arbeiten, riskieren Sie ansonsten immer, den entsprechenden Task für eine unbekannte Zeitspanne so zu blockieren, dass das Gerät nur noch sprunghaft oder gar nicht mehr reagiert. Unter besonders ungünstigen Umständen wird vielleicht sogar das gefürchtete Dialogfeld mit der Meldung angezeigt, dass Ihre Anwendung nicht mehr reagiert. Viele Benutzer werden dann glauben, Ihre Anwendung sei wirklich abgestürzt.

Loader wurden mit Android 3.x eingeführt und sollten die Lösung derartiger Probleme erleichtern. Sie stellen Verfahren bereit, mit deren Hilfe Hintergrundtasks (wie das Auslesen von Datenbanken) gestartet werden können, die nach Aufruf einer Rückruffunktion fortgesetzt und beendet werden können, um dann beispielsweise die Bedienschnittstelle zu aktualisieren.

Ein typisches Beispiel für einen Loader ist CursorLoader. CursorLoader können Sie beispielsweise dazu nutzen, um Daten unter Nutzung eines Datenbankzeigers aus einer SQLite-Datenbank auszulesen. Um einen CursorLoader zu einem Ihrer Listen-Fragmente hinzuzufügen, implementieren Sie die Schnittstelle LoaderCallback in Ihrer Callback-Funktion und diese drei LoaderCallback-Methoden:

onCreateLoader(): Diese Methode wird in einem Background-Thread aufgerufen, wenn Sie mit initLoader() einen Loader erzeugen. Darin sind Sie als Programmierer für die Erzeugung eines CursorLoader-Objekts und dessen Rückgabe verantwortlich. CursorLoader verwendet eine URI, um einen ContentProvider zur Übergabe von Daten aufzufordern.

onLoadFinished(): Diese Methode wird aufgerufen, wenn das CursorLoader-Objekt seine Daten aus der Datenbank vollständig geladen hat. In ihr müssen Sie für die Aktualisierung der Bedienoberfläche und die Anzeige der neuen Daten sorgen.

onLoaderReset(): Diese Methode wird aufgerufen, wenn der Loader zurückgesetzt oder beendet wird. In einem solchen Fall müssen Sie dafür sorgen, dass Ihre Fragmente nicht mehr den Loader oder seinen Datenbankzeiger nutzen.

Um einen Loader zu starten, müssen Sie erst einmal über einen Aufruf der Methoden getLoaderManager() und initLoader() ein LoaderManager-Objekt für Ihre Activity erzeugen und initialisieren. initLoader() startet das Laden von Daten im Hintergrund mit einem Aufruf von onCreateLoader() und führt in Ihrem LoaderCallback-Objekt abschließend onLoaderFinished() aus.

Sie können Loader natürlich auch für andere Dinge als den Abruf von Daten aus einer Datenbank nutzen. Unabhängig davon, ob Daten aus einer Datenbank, über ein Netzwerk oder ganz andere Quellen abgerufen werden, müssen alle Loader aber dieselben drei Methoden implementieren.

Unter http://developer.android.com/guide/components/loaders.html finden Sie weitere Informationen über Loader.

Alle Termine über einen Cursor zurückgeben

Sie können mittlerweile zwar Termine erstellen, diese werden aber noch nicht in der Terminliste angezeigt? Stimmt, leider ist das vorläufig wirklich noch so. Daher werde ich Ihnen zeigen, wie Sie die aktuell in der Datenbank existierenden Termine in ReminderListFragment in der ListView ausgeben können.

Listing 12.4 enthält das Gerüst des gesamten Fragments ReminderListFragment mit dem neuen Code, der die Terminliste aus der Datenbank in die ListView übernehmen kann.

package com.dummies.android.terminplaner;

import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.View;
import android.widget.ListView;

import com.dummies.android.terminplaner.R.string;

public class ReminderListFragment extends ListFragment implements

LoaderCallbacks<Cursor> {  →16

private SimpleCursorAdapter mAdapter;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// Einen Array erzeugen, um die in der Liste anzuzeigenden

// Felder anzugeben (nur TITLE)

String[] from = new String[] { ReminderProvider.COLUMN_TITLE };  →26

// ... und einen Array der Felder, mit denen diese Felder

// verbunden werden sollen (hier nur text1)

int[] to = new int[] { R.id.text1 }; →30

// Nun wird ein SimpleCursorAdapter erzeugt und auf display  // gesetzt

mAdapter = new SimpleCursorAdapter(getActivity(),

R.layout.reminder_row, null, from, to, 0);  →34

setListAdapter(mAdapter); →35

getLoaderManager().initLoader(0, null, this); →37

}

@Override

public void onViewCreated(View view, Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

setEmptyText(getResources().getString(string.no_reminders));

registerForContextMenu(getListView());

setHasOptionsMenu(true);

}

@Override

public void onListItemClick(ListView l, View v, int position,  long id) {

super.onListItemClick(l, v, position, id);

startActivity(new Intent(getActivity(), ReminderEditActivity.class) →51

.putExtra(ReminderProvider.COLUMN_ROWID, id));

}

@Override

public boolean onContextItemSelected(MenuItem item) { →56

switch (item.getItemId()) {

case R.id.menu_delete:

AdapterContextMenuInfo info = (AdapterContextMenuInfo) item

.getMenuInfo(); →60

getActivity().getContentResolver().delete(

ContentUris.withAppendedId(ReminderProvider. CONTENT_URI,

info.id), null, null); →63

return true;

}

return super.onContextItemSelected(item);

}

@Override

public Loader<Cursor> onCreateLoader(int ignored, final Bundle args) {  →70

return new CursorLoader(getActivity(), ReminderProvider .CONTENT_URI,

null, null, null, null); →71

}

@Override

public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { →75

mAdapter.swapCursor(cursor);

}

@Override

public void onLoaderReset(Loader<Cursor> loader) { →80

// Wird aufgerufen, wenn der letzte an onLoadFinished() übergebene

// Cursor geschlossen werden soll. Sorgen Sie dafür, dass der Adapter

// den Cursor nicht mehr nutzt, indem Sie ihm den Wert null zuweisen

mAdapter.swapCursor(null);

}

// Menücode aus Platzgründen weggelassen

}

Listing 12.4: Die gesamte Klasse ReminderListFragment mit Datenbankzugriff

Nachfolgend wird der Code für das Auslesen der Terminliste erläutert:

→16: Fügt die LoaderCallbacks-Schnittstelle zur Schnittstellenliste hinzu.

Um Daten in einem asynchronen Hintergrund-Thread laden zu können und damit diese Zeile auch wirklich funktioniert, müssen Sie weiter hinten in der Klasse erst noch etliche Callback-Funktionen für den Loader implementieren. Mehr dazu erfahren Sie im Abschnitt »Loader« weiter vorn in diesem Kapitel.

→26: Übernimmt das Titelfeld des Termineintrags.

→30: Hier wird der Array der Views definiert, die als View für die Zeile gebunden werden sollen. Der Titel ist dann einer bestimmten Termin-ID zugeordnet. Daher wurde die Variable in Zeile 26 auch from und die in dieser Zeile to genannt. Die Werte aus Zeile 26 werden auf die in Zeile 30 übertragen.

→34: In dieser Zeile wird ein SimpleCursorAdapter erzeugt, der Spalten aus einem Cursor auf die in einer XML-Layoutdatei definierten TextView-Objekte abbildet. Über diese Methode können Sie festlegen, welche Spalten angezeigt werden sollen und welche XML-Datei für das Aussehen der Views maßgeblich sein soll.

Die Verwendung von SimpleCursorAdapter und der zugehörigen Parameter wird im nachfolgenden Abschnitt beschrieben.

→35: SimpleCursorAdapter wird als Adapter-Parameter an die Methode setList Adapter() übergeben, um der ListView mitzuteilen, wo sich deren Daten befinden.

→37: Fordert LoaderManager auf, mit dem Laden des Loaders mit der ID 0 zu beginnen. Wenn der Loader seine Arbeit abgeschlossen hat, führt er die Callback-Methoden der LoaderCallback-Klasse aus, die Sie ihm im letzten Parameter dieses Aufrufs übergeben haben (hier: "this").

→51: Diese Codezeile überträgt die ID des zu bearbeitenden Termins in den Intent. ReminderEditFragment untersucht diesen Intent und versucht, dem Benutzer die Bearbeitung des Termins zu ermöglichen, wenn die ID gefunden wird.

→56: In dieser Zeile wird die Methode definiert, die Kontextmenüereignisse verarbeitet, die dann auftreten, wenn der Benutzer ein Element im Kontextmenü nach längerem Drücken auf einen Termin in der Listenansicht auswählt.

→60: Diese Codezeile nutzt die Methode getMenuInfo() des angeklickten Elements, um eine Instanz von AdapterContextMenuInfo zu übernehmen. Diese Klasse stellt verschiedene Angaben zum Menüelement und das in der ListView länger gedrückte Element bereit.

→63: Übernimmt einen ContentResolver und fordert ihn auf, den Termineintrag zu löschen, dessen ID aus dem id-Feld des AdapterContextMenuInfo-Objekts übernommen wurde. Dieses Feld enthält die ID der Zeile im ListView-Element, die zum Erstellen der URI für die Zeile mit ContentUris.withAppendedId() verwendet wurde. Nach dem Löschen des Termineintrags wird das ListView-Element automatisch aktualisiert, damit es die aktuellen Daten anzeigt.

→70: Beim Aufruf von initLoader() in Zeile 37 wird die Methode onCreateLoader() aufgerufen. Hier erzeugen Sie den Loader, dessen Ausführung Android in einem Hintergrund-Thread startet. Komplexe Fragmente können zwar mehrere Loader unterschiedlichen Typs verwenden, aber in diesem einfachen Fragment können Sie sich auf einen einzelnen Loader beschränken und die ID-Parameter der Methode onCreateLoader() getrost ignorieren.

→71: Erzeugt einen neuen CursorLoader, der genutzt wird, wenn Sie Elemente aus einem ContentProvider- oder einem SQLiteDatabase-Objekt laden. CursorLoader lädt Daten unter Verwendung von ReminderProvider, weshalb Sie dafür sorgen müssen, dass Sie CONTENT_URI von ReminderProvider angeben. Dabei handelt es sich um eine einfache Listenoperation, weshalb Sie hier die optionalen Parameter (projection, selection, selectionArgs und sortOrder) des CursorLoader-Konstruktors ignorieren können.

→75: Wenn ein Loader seine Arbeit abgeschlossen hat, ruft er die Callback-Funktion onLoadFinished() auf und gibt den Cursor zurück, der die geladenen Daten enthält. Wenn der Adapter den alten Cursor durch den neuen ersetzt, wird auch das ListView-Element automatisch aktualisiert.

→80: Ruft onLoaderReset() auf, wenn der letzte an onLoadFinished() übergebene Cursor geschlossen wird. Der Adapter des Cursors wird nicht mehr benötigt und auf den Wert null gesetzt.

Grundlagen von SimpleCursorAdapter

In Zeile 34 von Listing 12.4 wurde ein SimpleCursorAdapter erstellt. Er nimmt Ihnen eine Menge der Schwerarbeit ab, wenn Sie Daten von einem Cursor-Objekt mit einem ListView-Element verbinden wollen. Wenn Sie einen SimpleCursorAdapter einrichten, müssen Sie die folgenden Parameter übergeben:

getActivity() – Kontext: Den Kontext, in dem sich der Adapter befindet.

R.layout.reminder_row – Layout: Die ID der Layoutressource, über die die Datei angegeben wird, die für dieses Listenelement verwendet werden soll.

null – Cursor: Der Datenbank-Cursor, den der Adapter zum Laden von Daten verwendet. Da bisher noch kein Datenbankzeiger existiert, verwenden Sie null.

from: Ein Array der Spaltennamen, die benutzt werden, um die Daten vom Cursor mit der View zu verbinden. Er wird in Zeile 26 definiert.

to: Ein Array der View-IDs, die die Spaltendaten vom from-Parameter anzeigen sollen. Das to-Feld wird in Zeile 30 definiert.

0 – Flags: Alle optionalen Flags, die an SimpleCursorAdapter übergeben werden sollen. Da hier keine benötigt werden, übergeben Sie den Wert 0.

Die Parameter to und from sorgen für die Zuordnungen im SimpleCursorAdapter zwischen den Daten im Cursor und den Views im Zeilenlayout.

Wenn Sie die App nun starten, wird eine Liste der erstellten und aus der SQLite-Datenbank ausgelesenen Elemente angezeigt. Da der Code für das Einfügen von Terminen noch nicht erstellt wurde, wird hier vorläufig noch die Meldung »Keine Termine vorhanden« angezeigt. Nun können Sie das Menü aufrufen und über die entsprechende Option Termineinträge hinzufügen.

Einen Termin löschen

Um einen Termin zu löschen, muss der Benutzer nur länger auf ein Element in ReminderListFragment drücken und die entsprechende Option im Kontextmenü auswählen. Das eigentliche Löschen des Termins aus der Datenbank erfolgt über die Methode delete() des SQLite-Datenbankobjekts. Diese Methode wird in Zeile 119 von Listing 12.3 aufgerufen.

Die Methode delete() von ContentResolver wird aus dem Methodenaufruf von onContextSelectedItem() in Zeile 63 von Listing 12.4 heraus aufgerufen. Um einen Termin aus der Datenbank löschen zu können, wird vorab nur dessen ID in der Datenbank benötigt. Um diese zu ermitteln, verwenden Sie das AdapterContextMenuInfo-Objekt, das zusätzliche Informationen zu Menüs liefert. Wenn ein Menü für die ListView angezeigt wird, werden diese der Kontextmenüauswahl übermittelt. Da Sie die Liste mit einem Datenbank-Cursor füllen, enthält die ListView die gesuchte ID. In Zeile 60 von Listing 12.4 wird das AdapterContextMenuInfo-Objekt übernommen und in Zeile 63 wird die Methode delete() mit der ID als Parameter aufgerufen.

Wenn Sie die App im Emulator starten, sollten Sie nun Termine erstellen, auflisten (lesen), aktualisieren und entfernen (löschen) können!

13

Terminerinnerungen mit
AlarmManager

In diesem Kapitel

Aufgabenplanung verstehen

Berechtigungen planen

Alarm-Ereignisse einrichten

Der Einfluss von Geräteneustarts auf Alarm-Ereignisse

Viele Aufgaben müssen täglich erledigt werden. Aufstehen, Duschen, Frühstücken und so weiter – das kennen Sie bestimmt auch. Für die meisten von uns gehört dies (in Variationen) zur alltäglichen Routine, bevor wir zur Arbeit gehen. Vielleicht werden Sie ja von Ihrer »inneren Uhr« geweckt und wachen immer rechtzeitig auf, ich stelle jedenfalls den Wecker, um nicht zu verschlafen! Bei der Arbeit nutze ich einen Kalender, der mich an anstehende Ereignisse wie Meetings und wichtige Server-Updates erinnert, an denen ich teilnehmen oder die ich veranlassen muss. Terminerinnerungen, Wecker, Alarm-Ereignisse, Aufgabenplanung, Task-Planer oder wie Sie das auch nennen wollen, zählen zur täglichen Routine, auf die wir uns auf die eine oder die andere Weise verlassen.

Es wäre recht mühsam, wenn Sie sich Ihr System für die Terminplanung selbst programmieren müssten. Glücklicherweise gibt es die Aufgabenplanung (den Task-Manager) unter Windows, cron unter Linux und die AlarmManager-Klasse unter Android. Android basiert zwar auf Linux, Sie können aber nicht auf cron zurückgreifen. Daher müssen Sie bei der Aufgabenplanung AlarmManager nutzen.

Warum AlarmManager benötigt wird

Die Terminplaner-Anwendung wäre nicht besonders nützlich, wenn sie nicht auch an die gespeicherten Termine erinnern könnte. Benutzer müssen Titel, Beschreibung und Datum/Uhrzeit für Terminerinnerungen festlegen können, damit sie entsprechend alarmiert werden können. Dazu müssen sie Android mitteilen können, wann der Alarm für einen Termin ausgelöst werden soll. Denken Sie an eine Situation, in der Sie bereits etliche Termine in die Terminplaner-Anwendung eingefügt haben, die alle im weiteren Verlauf des Tages fällig werden. Sie stecken Ihr Gerät in die Tasche und arbeiten weiter. Wenn Sie an die anstehenden Termine nicht erinnert werden, würden Sie sie vergessen. Daher müssen Sie irgendwie daran erinnert werden. Und genau diese Aufgabe erfüllt die AlarmManager-Klasse.

Über die AlarmManager-Klasse können Benutzer einen zukünftigen Termin festlegen, an dem der Terminplaner ausgeführt werden soll. Bei einem ausgelösten Alarm wird ein Intent vom System übertragen. Ihre Anwendung reagiert dann auf diesen Broadcast-Intent und führt irgendwelche Aufgaben aus. Sie wird beispielsweise geöffnet, macht den Benutzer über ein Symbol in der Statusleiste auf sich aufmerksam (damit befasst sich Kapitel 14) oder führt irgendwelche anderen Aktionen aus.

Den Benutzer um Erlaubnis bitten

Sicherlich würde es Ihnen gar nicht gefallen, wenn Ihr Nachbar seine Weihnachtsdekoration in Ihrem Gartenhaus lagern würde, ohne Sie vorher um Erlaubnis gebeten zu haben, oder? Bestimmt nicht! Android verhält sich da ähnlich und es bedarf irgendeiner Erlaubnis des Benutzers, um Daten irgendwo auf dem Gerät speichern zu dürfen. Aber nicht nur dafür ist die Zustimmung des Benutzers erforderlich.

Welchen Einfluss Berechtigungen auf die Benutzerfreundlichkeit haben

Wenn Benutzer Anwendungen aus dem Google Play Store installieren, wird über deren Manifest-Datei ermittelt, welche Berechtigungen benötigt werden, damit die App funktioniert. Immer wenn Ihre App auf kritische Komponenten wie etwa externen Speicher, das Internet oder den Gerätestatus zugreifen will, wird der Benutzer darüber informiert. Dann kann er entscheiden, ob die Installation fortgesetzt werden soll.

Fordern Sie keine Berechtigungen an, die von Ihrer App nicht benötigt werden. Stellen Sie sich vor, die Anwendung LAUTLOSMODUS-UMSCHALTER (die in einigen der vorherigen Kapitel dieses Buches erstellt wurde) wollte Ihre aktuelle GPS-Position abfragen, auf das Internet zugreifen und weitere Angaben zum Gerät (Hardwareinfo) ermitteln. Dann würden sicherheitsbewusste Benutzer sehr wahrscheinlich misstrauisch werden und Ihre App gar nicht erst installieren.

Berechtigungen über die Datei AndroidManifest.xml anfordern

Wenn Ihr Projekt bestimmte Berechtigungen benötigt, müssen Sie sie über die Datei AndroidManifest.xml anfordern. Um mit einer SQLite-Datenbank arbeiten zu können, sind keine Berechtigungen erforderlich. Sie benötigen aber zwei Berechtigungen für die Terminplaner-App für den AlarmManager-Code:

android.permission.RECEIVE_BOOT_COMPLETED. Über diese Berechtigung kann Ihre App ermitteln, ob das Gerät neu gestartet wird. Dann kann sie die benötigten AlarmManager-Funktionen registrieren.

android.permission.WAKE_LOCK. Über die Berechtigung WAKE_LOCK bleibt das Telefon kurzfristig aktiv, während bestimmte Hintergrundprozesse ausgeführt werden.

Da AlarmManager den oder die Prozessoren so lange wach hält, wie die Methode onReceive() des Alarmempfängers ausgeführt wird, ist garantiert, dass das Gerät nicht in den Schlafmodus wechselt, während die App noch Broadcast-Meldungen verarbeitet.

Wenn Ihre Anwendung Zugriff auf das Internet benötigt oder Daten auf SD Card schreiben muss, was beim Terminplaner nicht der Fall ist, müssten Sie diese beiden Berechtigungen anfordern:

Internet: android.permission.INTERNET

SD Card: android.permission.WRITE_EXTERNAL_STORAGE

Es gibt zwei Möglichkeiten, um Berechtigungen zur Datei AndroidManifest.xml hinzuzufügen:

Über den PERMISSIONS-Editor von AndroidManifest.xml. Lassen Sie dazu die Datei AndroidManifest.xml in Eclipse anzeigen, aktivieren Sie die Registerkarte PERMISSIONS, klicken Sie die Schaltfläche ADD an und wählen Sie im dann angezeigten Dialogfeld USES PERMISSION in der Liste aus.

Durch manuelle Bearbeitung der XML-Datei. Bei dieser Variante müssen Sie ein oder mehrere uses−permission-Elemente in das manifest-Element einfügen. Die Anforderung einer Berechtigung sieht in der XML-Datei dann so aus:

<uses−permission android:name="android.permission.WAKE_LOCK" />

Sofern noch nicht geschehen, fügen Sie die Berechtigungen WAKE_LOCK und RECEIVE_ BOOT_COMPLETED zur Terminplaner-Anwendung hinzu. Sollten Sie sich für eine vollständige Liste der verfügbaren Berechtigungen interessieren, finden Sie sie in der Android-Dokumentation unter http://d.android.com/reference/android/Manifest .permission.html.

Wenn Sie die von Ihrer App benötigten Berechtigungen nicht deklarieren, funktioniert sie im Emulator oder auf physischen Geräten nicht wie erwartet. Jeder dann erzeugte Laufzeitfehler kann Ihre App zum Absturz bringen und potenziell auch zu Datenverlusten führen. Fordern Sie benötigte Berechtigungen auch immer an.

Einen Prozess mit AlarmManager aufwecken

Wenn Sie einen Prozess mit AlarmManager aufwecken wollen, müssen Sie erst einmal den Alarm setzen. In der Terminplaner-Anwendung erledigen Sie das am besten direkt in der Methode onClickListener() der Speichern-Schaltfläche. Bevor Sie diesen Code jedoch hinzufügen können, müssen Sie Ihr Projekt erst noch um vier Klassendateien erweitern:

ReminderManager.java: Diese Klasse ist für die Einrichtung der Terminerinnerungen über AlarmManager verantwortlich. Den Code für diese Klasse finden Sie in Listing 13.1.

OnAlarmReceiver.java: Diese Klasse ist für die Verarbeitung des Broadcasts nach der Auslösung des Alarms zuständig. Den Code für diese Klasse finden Sie in Listing 13.2 im Abschnitt »Die Klasse OnAlarmReceiver erstellen« weiter hinten in diesem Kapitel. Damit Ihre Anwendung diesen Empfänger erkennen kann, müssen Sie die folgende Zeile zum application-Element in der Datei AndroidManifest.xml hinzufügen:

<receiver android:name=".OnAlarmReceiver" />

Die Syntax mit dem führenden Punkt teilt Android mit, dass sich der Empfänger (receiver) im aktuellen Paket befindet, das im application-Element der Datei AndroidManifest.xml festgelegt wurde.

WakeReminderIntentService.java: Diese abstrakte Klasse ist für die Verwaltung der Einschlafsperre zuständig. Den Code für diese Klasse finden Sie in Listing 13.3 im Abschnitt »Die Klasse WakeReminderIntentService erstellen« weiter hinten in diesem Kapitel.

ReminderService.java: Bei dieser Klasse handelt es sich um eine Implementierung von WakeReminderIntentService, die für die in Kapitel 14 dargestellte Einrichtung der Benachrichtigung zuständig ist. Den Code für diese Klasse finden Sie in Listing 13.4 im Abschnitt »Die Klasse ReminderService erzeugen« weiter hinten in diesem Kapitel.

Damit Ihre Anwendung diesen Dienst erkennen kann, müssen Sie die folgende Codezeile zum application-Element in der Datei AndroidManifest.xml hinzufügen:

<service android:name=".ReminderService" />

Die Klasse ReminderManager erstellen

Wie bereits erwähnt, ist die Klasse ReminderManager in Android für die Einrichtung der Terminerinnerungen über die Klasse AlarmManager verantwortlich. In diese Klasse werde ich alle Aktionen einbinden, bei denen es um die Einrichtung von Alarm-Ereignissen der Klasse AlarmManager geht.

Für den Alarm für diesen Termin fügen Sie die folgende Zeile in die Klasse ReminderEditFragment am Ende der Methode setOnClickListener() der Schaltfläche mConfirmButton ein:

new ReminderManager(getActivity()).setReminder(mRowId, mCalendar);

Diese Codezeile sorgt dafür, dass ReminderManager eine neue Terminerinnerung für den Termin mit der Zeilenkennung mRowId für ein bestimmtes Datum und die Uhrzeit einrichtet, die über die Variable mCalendar definiert werden.

Listing 13.1 zeigt den Code der Klasse ReminderManager, für den Sie eine neue Datei unter dem Namen ReminderManager.java erstellen und in das Projekt einbinden müssen. Das können Sie im PACKAGE EXPLORER wie gehabt über das Kontextmenü erledigen. Das Hinzufügen der erforderlichen import-Anweisungen über die Option SOURCE:ORGANIZE IMPORTS im Kontextmenü sollte Ihnen dabei mittlerweile zur Routine geworden sein.

public class ReminderManager {

private Context mContext;
private AlarmManager mAlarmManager;
public ReminderManager(Context context) { →5

mContext = context;

mAlarmManager =

(AlarmManager)context.getSystemService(Context.ALARM_SERVICE); →8

}

public void setReminder(Long taskId, Calendar when) { →11

Intent i = new Intent(mContext, OnAlarmReceiver.class); →12

i.putExtra(ReminderProvider.COLUMN_ROWID, (long)taskId); →13

PendingIntent pi =

PendingIntent.getBroadcast(mContext, 0, i,

PendingIntent.FLAG_ONE_SHOT); →17

mAlarmManager.set(AlarmManager.RTC_WAKEUP, when. getTimeInMillis(), pi); →19

}

}

Listing 13.1: Die Klasse ReminderManager

Nachfolgend werden die einzelnen Codezeilen erläutert:

→5: Es wird eine Instanz der Klasse ReminderManager mit einem Context-Objekt erzeugt.

→8: Über den Aufruf von getSystemService() wird ein AlarmManager-Objekt übernommen.

→11: Die Methode setReminder() wird mit der Datenbankkennung des Termins und dem Calendar-Objekt mit dem Zeitpunkt der Auslösung des Alarms aufgerufen.

→12: Es wird ein neues Intent-Objekt erzeugt. Dieses Objekt ist dafür zuständig, was nach der Auslösung des Alarms geschehen soll. Hier wird festgelegt, dass der Empfänger OnAlarmReceiver aufgerufen werden soll.

→13: Das Intent-Objekt wird mit zusätzlichen Informationen versorgt (der Kennung des Termins in der Datenbank).

→17: AlarmManager wird in einem eigenständigen Prozess ausgeführt. Damit AlarmManager eine Anwendung darüber informieren kann, dass eine Aktion ausgeführt werden muss, muss ein PendingIntent erzeugt werden. Das PendingIntent-Objekt enthält ein Intent-Objekt, das in Zeile 12 erzeugt wurde. In dieser Zeile wird ein Pending Intent-Objekt mit dem Statusindikator FLAG_ONE_SHOT erzeugt, der dafür sorgt, dass es nur einmal benutzt werden kann.

→19: Die Methode set() des AlarmManager-Objekts wird aufgerufen, um den Alarmtermin zu setzen. Dieser Methode werden die folgenden Parameter übergeben:

• type: AlarmManager.RTC_WAKEUP gibt die koordinierte Weltzeit (UTC – Universal Time Coordinated) an. Dieser Parameter weckt das Gerät auf, wenn die über das Argument triggerAtTime angegebene Zeitspanne abgelaufen ist.

• triggerAtTime: when.getTimeInMillis() gibt die Uhrzeit an, zu der der Alarm ausgelöst werden soll. Das Calendar-Objekt stellt die Methode getTimeInMillis() bereit, die die Zeit in einen long-Wert umwandelt, der der Zeit in Einheiten von Millisekunden entspricht.

• operation: Bei pi handelt es sich um den schwebenden Intent (pending intent), auf den reagiert werden soll, wenn der Alarm ausgelöst wird. Der Alarm wird nun zur angeforderten Zeit ausgelöst.

Wenn bereits ein Alarm mit einem schwebenden Intent anberaumt wurde, der dieselbe Signatur enthält, wird der vorherige Alarm annulliert und ein neuer eingerichtet.

Die Klasse OnAlarmReceiver erstellen

Die Klasse OnAlarmReceiver ist für die Verarbeitung des Intents zuständig, der beim Auftreten eines Alarms ausgelöst wird. Da es sich im Wesentlichen um eine einfache Implementierung von BroadcastReceiver handelt, die auf Broadcast-Ereignisse im Android-System reagieren kann, fungiert OnAlarmReceiver als Einhängepunkt im Alarmsystem.

Erstellen Sie für die Java-Klasse OnAlarmReceiver die Datei OnAlarmReceiver.java und fügen Sie dort den Code aus Listing 13.2 ein.

public class OnAlarmReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

long rowid =

intent.getExtras().getLong(ReminderProvider.COLUMN_ROWID); →5

WakeReminderIntentService.acquireStaticLock(context); →7

Intent i = new Intent(context, ReminderService.class); →9

i.putExtra(ReminderProvider.COLUMN_ROWID, rowid); →10

context.startService(i); →a11

}

}

Listing 13.2: Die Klasse OnAlarmReceiver

Nachfolgend werden die einzelnen Codezeilen erläutert:

→5: Nachdem der Empfänger mit der Verarbeitung des Intents begonnen hat, übernimmt diese Zeile die Datenbank-ID des Termins vom Intent.

→7: Hier wird WakeReminderIntentService angewiesen, sich eine statische CPU-Sperre zu beschaffen, um das Gerät wach zu halten, solange Arbeiten erledigt werden.

→9: In dieser Zeile wird ein neues Intent-Objekt definiert, das ReminderService starten soll.

→10: In dieser Zeile wird dem Intent die ID des Termins übergeben, mit dem der die Arbeit verrichtende Dienst gestartet wird. Damit erhält die Klasse ReminderService die Kennung des Termins, mit dem sie arbeiten muss.

→11: Diese Zeile startet ReminderService.

Bei der Methode onReceive() von OnAlarmReceiver handelt es sich um den ersten Eintrittspunkt für den gesetzten Alarm. Während dieser BroadcastReceiver verarbeitet wird, wäre es nicht erwünscht, dass das Gerät in den Schlafzustand wechselt, weil ein Termin dann nie abgeschlossen werden könnte und Ihre App möglicherweise in einen unterbrochenen Status übergehen würde, durch den Daten in der Datenbank beschädigt werden können.

Wenn ein Alarm ausgelöst wird, wird der mit dem Alarm anberaumte schwebende Intent über das System übertragen. Alle Broadcast-Empfänger, die etwas damit anzufangen wissen, werden ihn dann verarbeiten.

Bei einem BroadcastReceiver handelt es sich um eine Komponente, die weiter nichts macht, als übertragene Systemnachrichten zu empfangen und darauf zu reagieren. Sie zeigt jedoch keine Bedienschnittstelle an, sondern startet als Antwort lediglich eine Activity. Bei OnAlarmReceiver handelt es sich um eine Instanz von BroadcastReceiver.

Wenn AlarmManager den schwebenden Intent überträgt, reagiert die Klasse OnAlarmReceiver auf den Intent, weil er an diese Klasse adressiert ist (siehe Zeile 12 von Listing 13.1). Diese Klasse übernimmt dann den Intent, sperrt die CPU und verrichtet die erforderliche Arbeit.

Die Klasse WakeReminderIntentService erstellen

Bei der Klasse WakeReminderIntentService handelt es sich um die Basisklasse der ReminderService-Klasse. Sie ist für die Anforderung und Freigabe der CPU-Einschlafsperre und deren Verwaltung zuständig. Eine CPU-Einschlafsperre (»wake lock« oder als Menüoption auch AKTIV LASSEN genannt) sorgt dafür, dass das Gerät (aber nicht zwangsläufig auch der Bildschirm) eingeschaltet bleibt, während Daten verarbeitet werden. Nach Abschluss der Verarbeitung hebt diese Klasse die Sperrung auf, damit das Gerät wieder in den Schlafmodus wechseln kann.

Erstellen Sie für die Java-Klasse WakeReminderIntentService die Datei WakeReminderIntentService.java und fügen Sie dort den Code aus Listing 13.3 ein.

public abstract class WakeReminderIntentService extends IntentService {

abstract void doReminderWork(Intent intent); →2

public static final String

LOCK_NAME_STATIC="com.dummies.android.terminplaner.Static"; →5

private static PowerManager.WakeLock lockStatic=null; →6

public static void acquireStaticLock(Context context) {

getLock(context).acquire(); →9

}

synchronized private static PowerManager.WakeLock

getLock(Context context) { →13

if (lockStatic==null) {

PowerManager

mgr=(PowerManager)context

.getSystemService(Context.POWER_SERVICE); →17

lockStatic=mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,

LOCK_NAME_STATIC); →20

}

return(lockStatic); →23

}

public WakeReminderIntentService(String name) { →a26

super(name);

}

@Override

final protected void onHandleIntent(Intent intent) { →31

try {

doReminderWork(intent); →33

} finally {

getLock(this).release(); →35

}

}

}

Listing 13.3: Die Klasse WakeReminderIntentService

Nachfolgend werden die einzelnen Codezeilen erläutert:

→2: Diese abstrakte Methode wird in allen Nachfahren dieser Klasse implementiert, wie zum Beispiel in Zeile 7 von Listing 13.4 in ReminderService.

→5: Dies ist die Kennung, die als Name für die übernommene CPU-Einschlafsperre verwendet wird und die die Fehlersuche unterstützt.

→6: Hierbei handelt es sich um die private statische Variable für die Einschlafsperre, auf die später in dieser Klasse verwiesen und die dort gesetzt wird.

→9: Hier wird die Methode getLock() aufgerufen. Um sicherzustellen, dass sich das Gerät im angeforderten Status der partiellen Einschlafsperre befindet, wird nach der Rückkehr aus dem Methodenaufruf die Methode acquire() aufgerufen. Diese Sperre verhindert zwar, dass das Gerät in den Schlafmodus wechselt, schaltet aber den Bildschirm nicht ein.

→13: In dieser Zeile wird die Methode getLock() definiert, die PowerManager.WakeLock zurückgibt, über die Sie Android mitteilen können, dass das Gerät eingeschaltet bleiben soll, solange Ihre App noch Daten verarbeitet.

→17: In dieser Zeile wird PowerManager vom Aufruf getSystemService() übernommen. Mit ihr wird die Sperre erzeugt.

→20: Hier wird mit dem Methodenaufruf new WakeLock() eine neue WakeLock-Sperre erzeugt. Diese Methode akzeptiert die folgenden Parameter:

• flags: Sie können diesem Aufruf zwar zahlreiche Schalter übergeben, ich benutze mit PowerManager.PARTIAL_WAKE_LOCK aber nur einen. PARTIAL_WAKE_LOCK teilt Android mit, dass die CPU eingeschaltet bleiben muss, der Bildschirm aber abgeschaltet werden kann.

• tag: Bei LOCK_NAME_STATIC kann es sich um den Namen Ihrer Klasse oder einen anderen String handeln. Dieser String wird für Zwecke der Fehlersuche genutzt und in Zeile 5 definiert.

→23: Hier wird WakeLock an den Aufrufer zurückgegeben.

→26: Dies ist der Konstruktor mit dem Namen der ihn erzeugenden Kindinstanz. Der Name wird nur für Fehlersuchzwecke genutzt.

→31: Hierbei handelt es sich um den Aufruf der Methode onHandleIntent() von IntentService. Diese Methode wird aufgerufen, sobald der Dienst gestartet wurde, um den ihr übergebenen Intent zu verarbeiten.

→33: Der Dienst versucht, die notwendige Arbeit durch Aufruf von doReminderWork() zu erledigen.

→35: Unabhängig vom Erfolg des Aufrufs von doReminderWork() soll dafür gesorgt werden, dass WakeLock freigegeben wird. Wenn release() für WakeLock nicht aufgerufen wird, könnte das Gerät bis zu dessen nächstem Neustart eingeschaltet bleiben. Das wäre höchst unerfreulich, weil dadurch die Akkus entladen werden. Deshalb wird die Methode release() auch abschließend im finally-Block der Ausnahmebehandlung über try−catch aufgerufen. Der finally-Block von try−catch wird unabhängig vom Erfolg des try-Blocks immer aufgerufen.

Sie müssen darauf achten, dass Sie release() für die Einschlafsperre immer genauso oft wie acquire() aufrufen. Sie können die Aufrufe von acquire() und release() in einen try−finally-Block einbetten, um dafür zu sorgen, dass release() bei auftretenden Ausnahmefehlern immer aufgerufen wird. Unter http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html finden Sie weitere Information zu try−finally-Blöcken.

Auch wenn doReminderWork() in ReminderService bisher noch nicht implementiert wurde, reagiert die Terminplaner-Anwendung bereits auf Alarm-Ereignisse. Wenn Sie wollen, können Sie mehrere Termine einrichten und Haltepunkte im Debugger setzen, um zu beobachten, wie der Ausführungspfad in der Methode doReminderWork() von ReminderService abbricht.

AlarmManager speichert Alarm-Ereignisse nicht dauerhaft. Wenn das Gerät neu gestartet wird, müssen die Alarm-Ereignisse daher erneut eingerichtet werden. Das muss bei jedem Geräteneustart erledigt werden.

Der eben vorgestellte Code zeigt, was erforderlich ist, um Daten mit einem Gerät zu verarbeiten, das in einen Schlafmodus übergehen oder gesperrt werden könnte. Er fordert eine WakeLock-Sperre an und ruft, während das Gerät aktiv gehalten wird, die in ReminderService implementierte Methode doReminderWork() auf.

Die Klasse ReminderService erzeugen

Die Klasse ReminderService verrichtet die Arbeit, wenn ein Alarm ausgelöst wird. Bei der Implementierung in diesem Kapitel wird vorläufig nur ein Gerüst für die auszuführenden Arbeiten erstellt. Die Benachrichtigungen in der Statusleiste werden später noch in Kapitel 14 implementiert.

Erstellen Sie für die Klasse ReminderService die Datei ReminderService.java und fügen Sie dort den Code aus Listing 13.4 ein.

public class ReminderService extends WakeReminderIntentService { →1

public ReminderService() {

super("ReminderService");

}

@Override

void doReminderWork(Intent intent) { →7

Long rowId = intent.getExtras()

.getLong(ReminderProvider.COLUMN_ROWID); →9

// Hier Code für Benachrichtigungen in Statusleiste einfügen

}

}

Listing 13.4: Die Klasse ReminderService

Nachfolgend werden die einzelnen Codezeilen erläutert:

→1: In dieser Zeile wird die Klasse ReminderService als Nachfahre von WakeReminderIntentService definiert.

→7: Hier wird die abstrakte Methode doReminderWork() von WakeReminderIntentService implementiert.

→8: Hier wird die ID des Termins übernommen, die im an diese Klasse übergebenen Intent-Objekt enthalten war.

Die Klasse ReminderService enthält keine Implementierung und macht nichts weiter, als die ID des Tasks vom Intent zu übernehmen.

Geräte neu starten

Wahrscheinlich werden auch Sie zuweilen einmal etwas vergessen. Das ist doch nur menschlich, oder? Und auch der AlarmManager von Android bildet hier keine Ausnahme. Er kann Alarm-Ereignisse nicht dauerhaft speichern. Nach einem Geräteneustart müssen die Alarm-Ereignisse daher komplett neu eingerichtet werden. Das ist zwar nichts Weltbewegendes, aber sicherlich wissenswert.

Wenn Sie die Alarm-Ereignisse nicht neu setzen, kann naturgemäß auch kein Alarm ausgelöst werden, weil Android dann nichts von ihnen weiß.

Einen BootReceiver erzeugen

Die Berechtigung RECEIVE_BOOT_COMPLETED ermöglicht Ihrer App den Empfang von Android-Systemnachrichten nach Abschluss des Gerätestarts, wenn interaktive Benutzereingaben entgegengenommen werden können. Da das Android-System dann Systemnachrichten versenden kann, müssen Sie zu Ihrem Projekt einen weiteren BroadcastReceiver hinzufügen, der die Android-Systemnachricht über den abgeschlossenen Neustart verarbeitet. Wenn die Nachricht empfangen wird, muss der Empfänger über ReminderProvider eine Verbindung mit SQLite herstellen, die Termine durchlaufen und die entsprechenden Alarm-Ereignisse setzen. Auf diese Weise sorgen Sie dafür, dass die Terminerinnerungen bei einem Neustart nicht vergessen werden.

Sie müssen zu Ihrer Terminplaner-Anwendung also einen neuen BroadcastReceiver hinzufügen, der hier OnBootReceiver heißt. Zudem müssen Sie zum application-Element der Datei AndroidManifest.xml die folgenden Codezeilen hinzufügen:

<receiver android:name=".OnBootReceiver" android:exported="false">

<intent−filter>

<action android:name="android.intent.action.BOOT_COMPLETED" />

</intent−filter>

</receiver>

Damit teilen Sie Android mit, dass OnBootReceiver die Systemnachricht über die Aktion BOOT_COMPLETED empfangen können soll. Einfach ausgedrückt informiert sie OnBootReceiver darüber, dass das Gerät neu gestartet wurde.

Erstellen Sie für die Klasse OnBootReceiver die Datei OnBootReceiver.java. Fügen Sie dort die vollständige Implementierung ein, die Sie in Listing 13.5 finden.

public class OnBootReceiver extends BroadcastReceiver { →1

private static final String TAG = OnBootReceiver.class.getSimpleName();

@Override

public void onReceive(Context context, Intent intent) { →6

ReminderManager reminderMgr = new ReminderManager(context); →8

Cursor cursor = context.getContentResolver().query(

ReminderProvider.CONTENT_URI, null, null, null, null); →11

if (cursor != null) {

cursor.moveToFirst(); →a14

int rowIdColumnIndex = cursor

.getColumnIndex(ReminderProvider.COLUMN_ROWID); →17

int dateTimeColumnIndex = cursor

.getColumnIndex(ReminderProvider.COLUMN_DATE_TIME);  →19

while (cursor.isAfterLast() == false) { →21

long rowId = cursor.getLong(rowIdColumnIndex); →23

long dateTime = cursor.getLong(dateTimeColumnIndex); →24

Calendar cal = Calendar.getInstance();

cal.setTime(new java.util.Date(dateTime)); →27

reminderMgr.setReminder(rowId, cal); →29

cursor.moveToNext(); →31

}

cursor.close(); →33

}

}

}

Listing 13.5: Die Klasse OnBootReceiver

Nachfolgend werden die einzelnen Codezeilen erläutert:

→1: Hier wird OnBootReceiver definiert.

→6: Hier handelt es sich um die Methode onReceive(), die aufgerufen wird, wenn ein Intent empfangen wird und Aktionen durchgeführt werden müssen.

→8: Hier wird ein neues ReminderManager-Objekt eingerichtet, mit dem Alarm-Ereignisse anberaumt werden können.

→11: Hier wird ein Cursor mit allen Erinnerungsterminen von ReminderProvider über die Methode getContentResolver() übernommen. Dabei handelt es sich um denselben Aufruf, der auch zur Aktualisierung und Löschen von Terminen in ReminderEditFragment und ReminderListFragment benutzt wird.

→14: Hier wird das Cursor-Objekt auf den ersten Datensatz gesetzt.

→17 bis  →19: Die jeweiligen Zeilen im Cursor-Objekt enthalten mehrere Spalten mit Daten. Zeile 17 ermittelt den Index für die ID und Zeile 19 den für Datum und Uhrzeit.

Neben der ID der Zeile werden Sie auch Datum und Uhrzeit ermitteln wollen, um den Termin für den Eintrag setzen zu können. Um diese Informationen zu bekommen, müssen Sie den Index der Spalten ermitteln, die sie enthalten.

→21: In dieser Zeile beginnt eine while-Schleife, in der mit der Methode isAfterLast() geprüft wird, ob sich der Cursor hinter dem letzten Datensatz befindet. Wenn das Ergebnis false lautet, geht es mit Zeile 23 weiter. Beim Wert true gäbe es keine weiteren nutzbaren Datensätze mehr im Cursor-Objekt.

→23  →24: Die ID und dateTime für diese Zeile werden mit Hilfe der Spaltenindizes aus den Zeilen 17 bis 19 aus dem Cursor übernommen.

→27: Nach der Übernahme des Datums aus dem Cursor-Objekt muss die Variable Calendar mit der richtigen Uhrzeit aktualisiert werden. In dieser Zeile werden die Datumswerte für die Übernahme in das lokale Calendar-Objekt formatiert.

→29: Hier wird ein neuer Erinnerungstermin mit der Zeilenkennung aus der Datenbank und der zuvor belegten Calendar-Variablen erstellt.

→31: Die Methode moveToNext() wechselt zum nächsten Datensatz im CursorObjekt. Wenn keine weiteren Datensätze mehr vorhanden sind, liefert der Aufruf von isAfterLast() in Zeile 21 den Wert true und die while-Schleife wird verlassen. Ansonsten wird der nächste Datensatz verarbeitet.

→33: Hier wird das Cursor-Objekt geschlossen, da es nicht länger benötigt wird. BroadcastReceiver verwenden generell keine Loader, weshalb Sie den Cursor schließen müssen.

Bei der bisherigen Arbeit mit dem Cursor-Objekt in Kapitel 12 musste der Cursor nicht geschlossen werden. Das lag daran, dass er dort von Loader verwaltet wurde.

Wenn Sie die App starten, ein paar Termine erstellen und dann das Gerät neu booten würden, könnten Sie nun sehen, dass die Erinnerungstermine weiterhin existieren.

Funktion von BootReceiver prüfen

Wenn Sie sich nicht sicher sind, ob OnBootReceiver funktioniert, können Sie die folgenden Log-Anweisungen in die while-Schleife einbauen:

Log.d("OnBootReceiver", "Alarm beim Start.");

Log.d("OnBootReceiver", "rowIdColumnIndex − " + rowIdColumnIndex);

Dadurch werden Meldungen in das Systemprotokoll eingetragen, die in der DDMS-Perspektive angezeigt werden. Sie können dann den Emulator beenden oder Ihr Gerät herunterfahren und diese neu starten. Beobachten Sie die DDMS-Anzeigen und achten Sie dabei auf die OnBootReceiver-Meldungen. Wenn in Ihrer Datenbank zwei Termine gespeichert sind, sollten zwei Sätze von Meldungen angezeigt werden, die darüber informieren, dass das System nach dem Start Alarmereignisse hinzugefügt hat. Bei der nächsten Meldung sollte es sich dann um die ID für den Terminplaner handeln.

Wenn Sie die App debuggen wollen, müssen Sie daran denken, das Attribut debuggable im Anwendungs-Manifest auf true zu setzen. Wie das geht, wurde in Kapitel 5 ausführlich beschrieben. (Dort finden Sie auch weitere Informationen zu DDMS.)

14

Die Android-Statusleiste
aktualisieren

In diesem Kapitel

Die Grundlagen der Statusleiste

Mit der Android-Benachrichtigungsverwaltung arbeiten

Benachrichtigungen aktualisieren und entfernen

Im Verlauf dieses Buches wurden verschiedene Möglichkeiten behandelt, Benutzer über Dialogfelder, Toasts und neue Aktivitäten auf besondere Ereignisse aufmerksam zu machen. Diese Verfahren funktionieren zwar in bestimmten Situationen recht gut, aber zuweilen werden Sie Benutzer auch auf Dinge hinweisen wollen, ohne deren Aufmerksamkeit von ihren aktuellen Aktivitäten abzulenken. Genau für derartige Zwecke eignet sich die Statusleiste.

Elemente der Statusleiste

Abbildung 14.1 zeigt die Statusleiste (unter Android 2.x auf einem Tablet am oberen Bildschirmrand), über die Sie Benutzer über Ereignisse informieren können und die eine ganze Menge Symbole anzeigen kann. Da einem uralten Sprichwort zufolge ein Bild mehr als tausend Worte sagt, lässt sich die Statusleiste am besten über ein Bild beschreiben. Betrachten Sie also Abbildung 14.1.

Abbildung 14.1: Die Statusleiste mit mehreren vorhandenen Symbolen

Das erste Symbol oben links in Abbildung 14.1 informiert darüber, dass eine App-Aktualisierung bereitsteht, die heruntergeladen werden kann. Das zweite ist eine Kalender-Benachrichtigung, die auf einen Termin aufmerksam macht. Die beiden folgenden repräsentieren erfolgte Downloads. Dann finden Sie hier noch zwei Symbole für den Ladezustand der Batterie und die Temperatur des Geräts und ein Symbol für einen laufenden Audioplayer. Rechts sehen Sie die Uhrzeit, ein Symbol für eine hergestellte Netzwerkverbindung und das Standardsymbol des Geräts für den Batterieladezustand. Sie können die Statusleiste auch antippen und herunterziehen, um sich wie in Abbildung 14.2 weitere Informationen zu den Benachrichtigungssymbolen anzeigen zu lassen.

Abbildung 14.2: Heruntergezogene Statusleiste

In Abbildung 14.2 sehen Sie, dass es zu den einzelnen Benachrichtigungen erweiterte Views mit weiteren Informationen zu den Symbolen gibt. Benutzer können die sie interessierende erweiterte View auswählen und damit die angeforderte Aktion starten.

Die Statusleiste stellt Ihnen verschiedene Werkzeuge zur Verfügung, um Benutzer zu benachrichtigen. Bei der einfachen Anzeige von Symbolen im Benachrichtigungsbereich handelt es sich jedoch nicht um Ihre einzige Option. Sie können die Wirkung Ihrer Benachrichtigung durch weitere Merkmale verstärken. Zu diesen Optionen zählen:

Vibration: Sie können das Gerät bei Benachrichtigungen vibrieren lassen. Das ist nützlich, wenn Benutzer es in der Tasche tragen.

Sound: Zusammen mit der Benachrichtigung können Sie irgendwelche Alarm- oder Tonsignale erklingen lassen. Diese können Sie auch selbst aufnehmen und zusammen mit Apps installieren. Das ist nützlich, wenn die Lautstärke der jeweiligen Geräte hoch genug eingestellt ist.

Lichtsignale: Viele Geräte verfügen auch über programmierbare LEDs. Dann können Sie darauf zugreifen und sie in bestimmten Intervallen oder auch bestimmten Farben blinken lassen. Wenn die LED nur eine Farbe (beispielsweise Weiß) unterstützt, werden dabei Farbvorgaben ignoriert. Wenn Benutzer ihr Gerät in den Lautlosmodus versetzt haben, können Lichtsignale gut auf Ereignisse hinweisen, die der Aufmerksamkeit des Benutzers bedürfen.

Mit Android 3.0 wurde Android für den Einsatz auf Tablets neu gestaltet. Dort werden seither eine Aktionsleiste am oberen und eine Statusleise am unteren Bildschirmrand angezeigt, die Softbuttons für die wichtigsten Funktionen enthält und von etlichen Herstellern zusätzlich erweitert wird (siehe Abbildung 14.3).

Abbildung 14.3: Statusleise bei einem Samsung-Tablet unter Android 4.x mit geöffnetem Benachrichtigungsfeld

Während sich Android 1.x und 2.x laut Aussage von Google selbst eigentlich nicht für Tablets eignet (sondern vorwiegend für Telefone und ähnliche Geräte), lässt sich andererseits mit Android 3.0x (Honeycomb) auf Geräten mit relativ kleinem Bildschirm wenig Staat machen.

Mit den Android-Versionen 4.x wurden die Android-Benachrichtigungen noch einmal erheblich verbessert. Hier verfügen Benachrichtigungen über diese Funktionen:

Erweiterbare Vorschau: Benutzer können Benachrichtigungen mit der Pinch/Zoom-Geste aufziehen und vergrößern. Damit lassen sich Vorschauen auf den Inhalt von Benachrichtigungen anzeigen, wie beispielsweise E-Mails.

Aktionsschaltflächen: Benutzer konnten Benachrichtigungen schon immer antippen und auf diesem Weg jene Apps starten, von denen diese stammten. Nun können Sie jedoch zusätzliche Schaltflächen hinzufügen, über die beliebige Operationen gestartet werden können. Ein herausragendes Beispiel im Terminplaner wäre eine Verbergen-Schaltfläche, über die Benachrichtigungen vorübergehend für einen gewissen Zeitraum ausgeblendet werden können.

Verschiedene Layouts: Mit Jelly Bean wurden beispielsweise drei neue Layouts für Benachrichtigungen definiert:

• BigTextStyle: Zeigt eine mehrzeilige TextView an.

• BigInboxStyle: Zeigt eine Liste mit Informationen an.

• BigPictureStyle: Zeigt ein Bild an.

Größere Darstellung: Benachrichtigungen können bis zu 256 dp groß sein.

Da die neueren Funktionen auf älteren Geräten nicht zur Verfügung stehen, können Sie sie naturgemäß dort auch nicht benutzen. Wenn Sie mehr über Benachrichtigungen unter Jelly Bean erfahren wollen, können Sie beispielsweise http://developer.android.com/guide/topics/ui/notifiers/notifications.html aufsuchen.

Wenn Sie diese verschiedenen Optionen für Benachrichtigungen verwenden, können Sie Benutzer selbst bei abgeschaltetem Bildschirm auf bestimmte Geschehnisse auf ihren Geräten hinweisen, die der Aufmerksamkeit bedürfen.

NotificationManager benutzen

NotificationManager dient der Benachrichtigungsverwaltung und damit als Schnittstelle zu den entsprechenden Android-Optionen. Um Benachrichtigungen in der Statuszeile anzuzeigen, benutzen Sie NotificationManager.

Mit dieser Codezeile erhalten Sie über einen Aufruf der Methode getSystemService() das NotificationManager-Objekt.

NotificationManager mgr =

(NotificationManager)getSystemService(NOTIFICATION_SERVICE);

Eine Benachrichtigung erstellen

Die Terminplaner-Anwendung muss den Benutzer irgendwie auf anstehende Termine aufmerksam machen. Dazu kann beispielsweise ein Alarm für den entsprechenden Termin ausgelöst werden. Um diese Benachrichtigung in der Statusleiste anzeigen zu lassen, müssen Sie NotificationManager nutzen.

Fügen Sie in die Methode doReminderWork() der Klasse ReminderService den Code aus Listing 14.1 ein.

Long rowId = intent.getExtras().getLong(ReminderProvider.COLUMN_ROWID);  →1

NotificationManager mgr =

(NotificationManager)getSystemService(NOTIFICATION _SERVICE); →4

Intent notificationIntent = new Intent(this, ReminderEditActivity.class);  →6
notificationIntent.putExtra(ReminderProvider.COLUMN_ROWID, rowId); →7

PendingIntent pi = PendingIntent.getActivity(this, 0, notificationIntent,

PendingIntent.FLAG_ONE_SHOT); →9

Notification note=new Notification(android.R.drawable.stat_sys_warning,

getString(R.string.notify_new_task_message),

System.currentTimeMillis()); →14

note.setLatestEventInfo(this, getString(R.string.notify_new_task_title),

getString(R.string.notify_new_task_message), pi);  →17

note.defaults |= Notification.DEFAULT_SOUND; →19
note.flags |= Notification.FLAG_AUTO_CANCEL; →20

// Ein Problem kann auftreten, wenn vom Benutzer mehr als 2.147.483.647

// Termine eingegeben werden (mehr als der int−Wertebereich).

// Zwar unwahrscheinlich, aber gut zu wissen.

int id = (int)((long)rowId); →27
mgr.notify(id, note); →28

Listing 14.1: Implementierung von doReminderWork()

Nachfolgend werden die einzelnen Zeilen von Listing 14.1 erläutert:

→1: Der Intent, der ReminderService gestartet hat, enthält die rowId des Termins, mit dem aktuell gearbeitet wird. Diese ID wird benötigt, weil sie als Teil von PendingIntent für den Status gesetzt wird. Wenn die Benachrichtigung in der Statusleiste ausgewählt wird, soll ReminderEditActivity mit der rowId als Teil des schwebenden Intents starten. Auf diese Weise wird ReminderEditActivity angezeigt, liest die Daten der entsprechenden rowId und zeigt sie dem Benutzer an.

→4: Eine Instanz von NotificationManager übernehmen.

→6: Hier wird ein neuer Intent erstellt und die Klasse auf ReminderEditActivity gesetzt. Dabei handelt es sich um die Aktivität, die gestartet werden soll, wenn der Benutzer die Benachrichtigung auswählt.

→7: rowId in den Intent übernehmen.

→9: Einen schwebenden Intent einrichten, der vom Benachrichtigungssystem verwendet werden soll. Da das Benachrichtigungssystem in einem anderen Prozess ausgeführt wird, muss ein PendingIntent genutzt werden. Das Flag FLAG_ONE_SHOT wird verwendet, um dafür zu sorgen, dass der schwebende Intent nur ein einziges Mal benutzt werden kann.

→14: In dieser Zeile wird das in der Statusleiste anzuzeigende Notification-Objekt erzeugt. Die Klasse Notification akzeptiert die folgenden Parameter:

• icon: Bei android.R.drawable.stat_sys_warning handelt es sich um die Ressourcenkennung des in der Statusleiste anzuzeigenden Symbols (ein kleines Dreieck mit darin enthaltenem Ausrufezeichen). Da dies ein vordefiniertes Symbol der Android-Plattform ist, müssen Sie sich nicht um die Erstellung von Symbolen für unterschiedliche Auflösungen kümmern.

• tickerText: getString(R.string.notify_new_task_message) übernimmt den Text, der bei der ersten Anzeige der Benachrichtigung durch die Statuszeile gerollt wird, aus einer String-Ressource, die Sie noch definieren müssen.

• when: System.currentTimeMillis() ermittelt die im Zeitfeld der Benachrichtigung anzuzeigende Uhrzeit.

Eclipse teilt Ihnen hier möglicherweise mit, dass dieser Konstruktor und der nachfolgende Aufruf von setLatestInfo() nicht mehr verwendet werden sollte und Sie stattdessen Notification.Builder benutzen sollten. Da Notification.Builder aber erst ab API Level 11 bereitsteht, lässt es sich nicht für ältere Geräte nutzen.

→17: Diese Zeile setzt den Inhalt der erweiterten View und verwendet dabei das Standardlayout von Android für das letzte Ereignis (LatestEvent). Sie könnten aber auch selbst ein XML-Layout erstellen, das angezeigt werden soll. Hier verzichten Sie darauf und greifen für die Benachrichtigung einfach auf eine vordefinierte View zurück. Die Methode setLatestEventInfo() akzeptiert die folgenden Parameter:

• context: this gibt den mit der Ereignisinformation zu verbindenden Kontext an.

• contentTitle: getString(R.string.notifiy_new_task_title) übernimmt den Text, der als Titel in der erweiterten View angezeigt werden soll, aus einer String-Ressource. Diese wird im Abschnitt »String-Ressourcen hinzufügen« weiter hinten im Kapitel hinzugefügt.

• contextText: getString(R.string.notify_new_task_message) übernimmt den erläuternden Text, der in der erweiterten View angezeigt werden soll, aus einer String-Ressource.

• contentIntent: pi legt fest, welcher Intent bei der Auswahl der erweiterten View gestartet werden soll.

→19: Beim Setzen des Notification-Objekts wird ein bitweises Oder verwendet, um für eine Benachrichtigung zu sorgen. Dadurch wird der voreingestellte Benachrichtigungston wiedergegeben, sofern die Lautstärke am Gerät nicht heruntergezogen wurde.

→20: Beim Setzen des Notification-Objekts wird ein bitweises Oder verwendet, um die Benachrichtigung auszublenden, wenn sie vom Benutzer ausgewählt wurde.

→27: Typumwandlung der rowId in einen Integer-Wert. In der SQLite-Datenbank wird die Kennung als long gespeichert. Hier wandele ich sie unter Genauigkeitsverlusten in einen Integer-Wert um. Ich bezweifle allerdings stark, dass mit dieser Anwendung jemals mehr als 2.147.483.647 (dem Maximalwert einer Integer-Variablen in Java) Termine eingerichtet werden. Daher sollte diese Umwandlung zu keinerlei Problemen führen. Sie ist erforderlich, weil der Code in Zeile 28 nur einen Integer-Wert als Kennung für die Benachrichtigung akzeptiert.

→28: Überträgt die Benachrichtigung in die Statusleiste. Der Aufruf von notify() akzeptiert zwei Parameter:

• id: Bei id handelt es sich um eine eindeutige Kennung innerhalb Ihrer Anwendung.

• Notification: note ist ein Notification-Objekt, das beschreibt, wie der Benutzer benachrichtigt werden soll.

Der Arbeitsablauf

Listing 14.1 sorgt für den folgenden Ablauf:

1. Der Benutzer verwendet eine andere Anwendung, wie zum Beispiel ein E-Mail-Programm.

2. Ein Termin wird fällig und daher wird ein Alarm ausgelöst. Die Benachrichtigung wird in der Statusleiste angezeigt.

3. Der Benutzer kann die Statusleiste aufziehen und die Benachrichtigung auswählen oder sie vorläufig ignorieren.

Wenn der Benutzer die Statusleiste aufzieht und ein Element auswählt, wird der schwebende Intent innerhalb der Benachrichtigung aktiviert. Dieser sorgt wiederum dafür, dass ReminderEditActivity mit der entsprechenden rowId des Termins angezeigt wird.

4. Die Benachrichtigung wird aus der Statusleiste entfernt.

5. Die Termindaten werden aus der Datenbank übernommen und von ReminderEditActivity im Formular angezeigt.

String-Ressourcen hinzufügen

Ich habe bereits darauf hingewiesen, dass Sie noch die folgenden String-Ressourcen in strings.xml definieren müssen, damit Sie die App im aktuellen Zustand starten können:

notify_new_task_message: Diesen Wert habe ich auf »Ein Termin muss überprüft werden!« gesetzt. Diese Meldung wird in der erweiterten View und als Tickertext bei der ersten Anzeige der Benachrichtigung verwendet.

notify_new_task_title: Diesen Wert habe ich auf »Terminplaner« gesetzt. Diese Meldung wird als Titel für die erweiterte View genutzt.

Benachrichtigungen aktualisieren

Manchmal werden Sie die View Ihrer Benachrichtigung möglicherweise aktualisieren müssen. Stellen Sie sich vor, dass Ihre Anwendung im Hintergrund ausgeführt wird und prüft, ob Termine geändert wurden. Dabei ermittelt sie, ob bestimmte Benachrichtigungen nicht weiter angezeigt werden müssen. Sie legen fest, dass das Symbol der Benachrichtigung nach Ablauf von zwei Stunden geändert wird und dass dann beispielsweise ein rotes Ausrufezeichen angezeigt und die LED am Gerät schnell rot blinken soll. Glücklicherweise lassen sich Benachrichtigungen recht leicht ändern.

Wenn Sie die Methode notify() mit einer aktuell in der Statusleiste aktiven Kennung und mit einem neuen Satz von Notification-Parametern aufrufen, wird die Benachrichtigung in der Statusleiste aktualisiert. Daher müssen Sie nur einfach ein neues Notification-Objekt mit dem roten Symbol als Parameter erstellen und notify() aufrufen, um die Benachrichtigung zu aktualisieren.

Benachrichtigungen entfernen

Das Verhalten der Benutzer lässt sich nie sicher vorhersehen. Schließlich können sie sich ja auch an irgendeinem beliebigen Ort der Welt aufhalten! Es könnte sich um Erstanwender, erfahrene Benutzer und so weiter handeln. Und alle benutzen ihre Geräte und Anwendungen auf ihre ganz eigene, spezielle Weise. Irgendwann wird ein Benutzer eine Benachrichtigung sehen und sich dazu entschließen, die App auf dem manuellen Umweg über den Anwendungsstarter aufzurufen. Wenn Benutzer aber diesen Umweg wählen, während eine Benachrichtigung aktiv ist, bleibt die Benachrichtigung in der Statusleiste stehen. Ihre Anwendung sollte den eigenen Status erkennen und möglicherweise bestehende Benachrichtigungen für den betreffenden Termin ausblenden können. Wenn ein Benutzer Ihre App aber öffnet und einen anderen Termin ohne aktive Benachrichtigung betrachtet, sollten diese hingegen erhalten bleiben.

Entfernen Sie nur die Benachrichtigungen für vom Benutzer überprüfte Termine.

Vorhandene Benachrichtigungen lassen sich beim NotificationManager wirklich leicht mit der Methode cancel() entfernen. Diese akzeptiert mit der ID der Benachrichtigung nur einen einzigen Parameter. Erinnern Sie sich noch daran, dass die ID des Termins auch für die Benachrichtigung verwendet wurde? Hier liegt der Grund. Die ID des Termins ist innerhalb der Terminplaner-Anwendung eindeutig. Auf diese Weise kann ich leicht einen Termin anzeigen und alle dafür vorhandenen Benachrichtigungen mit einem Aufruf der Methode cancel() und der Kennung des Termins entfernen.

Irgendwann werden Sie auch einmal alle zuvor angezeigten Benachrichtigungen entfernen müssen. Dazu können Sie einfach die Methode cancelAll() von NotificationManager aufrufen.

15

Arbeiten mit dem AndroidPreference-Framework

In diesem Kapitel

So funktionieren Einstellungen in Android

Einen Bildschirm für Einstellungen erstellen

Einstellungen programmgesteuert ändern

Die meisten Programme lassen sich konfigurieren und über individuelle Einstellungen (weitgehend) an die Benutzerbedürfnisse anpassen. Wenn Sie Benutzern Ihrer Android-Anwendungen auch diese Möglichkeit geben, trägt dies zur Benutzerfreundlichkeit bei. Glücklicherweise lassen sich derartige Prozesse zur Änderung von Einstellungen in Android recht leicht implementieren.

Android enthält direkt ein robustes Preference-Framework, mit dessen Hilfe Sie Einstellungen für Ihre Anwendung deklarieren und programmgesteuert ändern können. Android speichert die Einstellungen dauerhaft als Schlüssel-Wert-Paare primitiver Datentypen. Sie müssen die Werte nicht in einer Datei, Datenbank oder auf anderem Weg speichern. Das Android-Preference-Framework übernimmt die angegebenen Werte und überträgt sie in den internen Speicher, damit sie von Ihrer Anwendung weiter benutzt werden können. Über das Preference-Framework können Sie boolean-, float-, int-, long- und string-Werte speichern. Die Daten bleiben auch über mehrere Benutzersitzungen hinweg erhalten. Der Benutzer kann die App also schließen und später wieder starten und die gespeicherten Einstellungen weiterhin nutzen. Das gilt selbst dann, wenn Ihre App zwangsweise beendet wird.

In diesem Kapitel werde ich Ihnen das Android-Preference-Framework vorstellen und beschreiben, wie Sie es in Ihre Anwendungen einbinden können. Ich werde Ihnen zeigen, wie Sie das PreferenceActivity-Objekt zum Erstellen und Bearbeiten von Einstellungen benutzen und wie Sie Einstellungen über den Code Ihrer Anwendungen auslesen und speichern können. Am Ende des Kapitels werden diese Möglichkeiten vollständig in Ihrer Terminplaner-Anwendung integriert sein.

Wenn Sie Android ab Version 3.x verwenden und keine älteren Geräte mehr unterstützen wollen, sollten Sie besser das neue PreferenceFragment anstelle von PreferenceActivity benutzen. Da PreferenceFragment bisher aber noch nicht im Rahmen der Support-Library bereitgestellt wird, ist PreferenceFragment auf älteren Geräten nicht nutzbar.

Das Android-Preference-Framework verstehen

Richtig toll am Android-Preference-Framework ist es, wie einfach sich ein Bildschirm erstellen lässt, über den der Benutzer Einstellungen ändern kann. Die meiste Schwerarbeit nimmt Ihnen Android ab und dadurch wird diese Aufgabe derart erleichtert, dass Sie nur noch den Bildschirm mit den Einstellungen in einer XML-Datei definieren müssen, die im Ordner res/xml Ihres Projekts abgelegt wird. Diese XML-Dateien sind allerdings nicht mit den Layoutdateien identisch, da es sich um spezifische XML-Definitionen für Bildschirme, Kategorien und die eigentlichen Einstellungen handelt. Zu den verbreiteten Einstellungen, die bereits in das Framework integriert sind, zählen:

EditTextPreference: Eine Einstellung, die einen Text als String speichern kann.

CheckBoxPreference: Speichert einen booleschen Wert.

RingtonePreference: Eine Einstellung, in der Benutzer den bevorzugten der auf dem Gerät verfügbaren Klingeltöne speichern können.

ListPreference: Eine Einstellung, in der Benutzer ein Element aus einer Liste als Vorgabe für ein Dialogfeld speichern können.

Wenn die bereits vordefinierten Einstellungen nicht Ihren Anforderungen genügen, können Sie eigene Einstellungen von der Basisklasse Preference oder DialogPreference ableiten. DialogPreference ist die Basisklasse für Einstellungen, die auf Dialogfeldern basieren. Wenn diese Einstellungen angeklickt werden, wird ein Dialogfeld mit den eigentlichen Steuerelementen für die Einstellungen angezeigt. Beispiele für integrierte DialogPreference-Objekte sind EditTextPreference und ListPreference.

Android bietet auch eine PreferenceActivity, in der Sie die Einstellungsbildschirme auf dieselbe Weise ableiten und laden können, wie Sie ein Layout für die Basisklasse Activity laden würden. Mit der Basisklasse werden die PreferenceActivity-Ereignisse erreichbar und Sie können einige anspruchsvollere Aufgaben erledigen, wie zum Beispiel die Einrichtung eines EditTextPreference-Objekts, das nur bestimmte Zeichen akzeptiert.

Grundlagen der Klasse PreferenceActivity

Die Klasse PreferenceActivity ist dafür verantwortlich, dass eine Hierarchie der Preference-Objekte in einer Liste angezeigt wird, die sich über mehrere Bildschirmseiten erstrecken kann (siehe Abbildung 15.1).

Bearbeitete Einstellungen werden unter Verwendung einer Instanz von SharedPreferences gespeichert. Die Klasse SharedPreferences stellt eine Schnittstelle für den Zugriff und die Bearbeitung von Einstellungsdaten bereit, die von der Methode getSharedPreferences() beliebiger Context-Objekte zurückgegeben werden.

PreferenceActivity ist eine Basisklasse, die der Basisklasse Activity recht stark ähnelt. PreferenceActivity verhält sich jedoch ein wenig anders. Eine der wichtigsten Funktionen von PreferenceActivity besteht darin, Einstellungen im gleichen Stil wie die Systemeinstellungen anzuzeigen. Dadurch erhalten Ihre Anwendungen durchweg ein konsistentes Aussehen und verwenden die üblichen Komponenten der Android-Benutzerschnittstelle. Sie sollten PreferenceActivity nutzen, wenn Sie in Ihren Android-Anwendungen Bildschirme für Einstellungen definieren.

Abbildung 15.1: Der Bildschirm ANRUFEINSTELLUNGEN in Android

Einstellungen dauerhaft speichern

Da das Android-Framework Einstellungen in SharedPreferences speichert und damit dafür sorgt, dass deren Daten automatisch im internen Speicher landen, wird das Definieren und Ändern von Einstellungen leicht. Wenn ein Benutzer eine Einstellung ändert, wird deren Wert automatisch dauerhaft gespeichert, ohne dass Sie sich weiter darum kümmern müssen!

Abbildung 15.2 zeigt ein EditTextPreference-Objekt, das in der Terminplaner-Anwendung verwendet werden soll. Nach Anklicken von OK übernimmt Android den angegebenen Wert und speichert ihn dauerhaft in SharedPreferences. Dazu muss ich weiter gar nichts machen. Wenn es um die dauerhafte Speicherung von Einstellungswerten geht, übernimmt Android die ganze Schwerarbeit.

Das Layout von Einstellungsbildschirmen

Das Arbeiten mit Layouts kann in Android manchmal zu einer mühseligen Angelegenheit werden, bei der Sie sich mit der korrekten Ausrichtung und etlichen anderen Aspekten herumplagen müssen. Das Erstellen von Layouts ähnelt fast der Gestaltung von Webseiten mit verschiedenen auf der Seite verstreuten Tabellen. Manchmal ist es leicht, manchmal aber eben auch nicht. Glücklicherweise lassen sich Bildschirme für Android-Einstellungen sehr viel leichter als die für das Layout Ihrer Anwendungen erstellen.

Abbildung 15.2: Eine Einstellung ändern

Die Einstellungsbildschirme von Android lassen sich den folgenden Kategorien zuordnen:

PreferenceScreen: Entspricht einer Einstellung auf der obersten Ebene, die die Wurzel der Einstellungshierarchie darstellt. Sie können PreferenceScreen an zwei Stellen verwenden:

• In einer PreferenceActivity: Das PreferenceScreen-Objekt selbst wird nicht angezeigt, sondern nur die in seiner Definition enthaltenen Einstellungen.

• In einer anderen Einstellungshierarchie: Wenn PreferenceScreen in einer anderen Hierarchie enthalten ist, dient es als Verbindungselement zu einem anderen Bildschirm mit Einstellungen. Stellen Sie sich das als Verschachtelung von PreferenceScreen-Deklarationen in andere PreferenceScreen-Deklarationen vor. Das mag zwar zunächst ein wenig verwirren, aber stellen Sie es sich wie XML-Definitionen vor. In XML können Sie Elemente deklarieren, die wiederum jeweils dasselbe Elternelement enthalten können. Dann verschachteln Sie die Elemente. Dasselbe gilt für PreferenceScreen. Durch Verschachtelung der Elemente teilen Sie Android mit, dass es bei deren Auswahl einen neuen Bildschirm anzeigen soll.

PreferenceCategory: Diese Einstellung wird zur Gruppierung von Preference-Objekten benutzt und stellt einen die Kategorie beschreibenden Gruppentitel bereit.

Preference: Eine Einstellung, die auf dem Bildschirm angezeigt wird. Dabei kann es sich um allgemeine oder speziell selbst definierte Einstellungen handeln.

Durch Kombinieren der Elemente PreferenceScreen, PreferenceCategory und Preference in XML können Sie leicht Einstellungsbildschirme erstellen, die Abbildung 15.1 ähneln.

Einen eigenen Einstellungsbildschirm erstellen

Beim Erzeugen von Einstellungen mit PreferenceActivity und einer entsprechenden XML-Datei handelt es sich um einen recht einfachen Prozess. Zunächst müssen Sie die XML-Datei für die Einstellungen erstellen, in der Sie das Layout des Bildschirms und die Werte der angezeigten String-Ressourcen definieren. Diese werden als TextView-Objekte auf dem Bildschirm angezeigt, um dem Benutzer mitteilen zu können, welchem Zweck sie dienen.

Über die Einstellungen in der Terminplaner-App sollen Benutzer eine vorgegebene Zeitspanne für einen Erinnerungstermin (in Minuten) und eine Titelvorgabe für neue Termine festlegen können. Beim aktuellen Stand der Anwendung bleibt der Titel erst einmal leer und als Zeitvorgabe wird die jeweils aktuelle Uhrzeit verwendet. Durch diese Einstellungen kann sich der Benutzer beim Erstellen neuer Termine einige Schritte sparen. Wenn der Benutzer beispielsweise häufig Termine erstellt, an die er in 60 Minuten erinnert werden will, kann er dies nun über die Einstellungen vorgeben. Dieser neue Wert wird dann beim Erstellen neuer Termine für die Terminerinnerung standardmäßig angeboten.

Eine Datei für die Einstellungen erstellen

Um Ihren ersten Einstellungsbildschirm zu erstellen, müssen Sie einen Ordner namens res/xml in Ihrem Projekt anlegen. Darin erzeugen Sie über das Kontextmenü eine neue XML-Datei namens task_preferences.xml, in die Sie den Code aus Listing 15.1 einfügen.

<PreferenceScreen →2

xmlns:android="http://schemas.android.com/apk/res/android">

<PreferenceCategory →4

android:key="@string/pref_category_task_defaults_key" →5

android:title="@string/pref_category_task_defaults_title"> →6

<EditTextPreference →7

android:key="@string/pref_task_title_key" →8

android:dialogTitle="@string/pref_task_title_dialog_title" →9

android:dialogMessage="@string/pref_task_title_message" →10

android:summary="@string/pref_task_title_summary" →11

android:title="@string/pref_task_title_title" /> →12

</PreferenceCategory>

<PreferenceCategory →13

android:key="@string/pref_category_datetime_key" →14

android:title="@string/pref_category_datetime_title"> →15

<EditTextPreference →16

android:key="@string/pref_default_time_from_now_key" →17

android:dialogTitle="@string/pref_default_time_from_now_ dialog_title" →18

android:dialogMessage="@string/pref_default_time_from_now_ message" →19

android:summary="@string/pref_default_time_from_now_summary" →20

android:title="@string/pref_default_time_from_now_title" />  →21

</PreferenceCategory>

</PreferenceScreen>

Listing 15.1: Die Datei task_preferences.xml

Listing 15.1 enthält etliche neue String-Ressourcen, die gleich anschließend im nächsten Abschnitt definiert werden. Die nummerierten Codezeilen werden nachfolgend erläutert:

→2: Hierbei handelt es sich um das PreferenceScreen-Element auf oberster Ebene, den Container für den Bildschirm selbst. Alle anderen Einstellungen sind dieser Deklaration untergeordnet.

→4: Dies ist ein PreferenceCategory-Element, das die Kategorie für Terminvorgaben definiert, wie zum Beispiel für Titel oder Beschreibung. Wie Sie vielleicht bemerkt haben, wird in Zeile 13 ein weiteres PreferenceCategory-Element für die Zeitvorgabe für die Termine deklariert. Normalerweise würden Sie diese beiden Elemente in derselben Kategorie unterbringen, aber hier wurden sie getrennt, um darzustellen, wie mehrere PreferenceCategory-Elemente auf einem Bildschirm verwendet werden können.

→5: In dieser Zeile wird der Schlüssel definiert, der verwendet wird, um die Einstellung in SharedPreferences zu speichern und von dort abzurufen. Dieser Schlüssel muss eindeutig sein.

→6: Hier wird der Titel der Kategorie definiert.

→7: Diese Zeile enthält die Definition des EditTextPreference-Elements, in dem die Vorgabe für den Titel eines Termins gespeichert wird.

→8: Diese Zeile enthält den Schlüssel für die Textvorgabe für den Titel des EditTextPreference-Elements.

→9: EditTextPreference ist ein Nachfahre der Klasse DialogPreference. Deshalb wird bei der Auswahl dieser Einstellung ein Dialogfeld angezeigt, das dem aus Abbildung 15.2 ähnelt. In dieser Codezeile wird der Titel dieses Dialogfeld definiert.

→10: Hier wird die im Dialogfeld angezeigte Meldung definiert.

→11: In dieser Zeile wird der zusammenfassende Text definiert, der auf dem Einstellungsbildschirm angezeigt wird (siehe Abbildung 15.1).

→12: Diese Zeile definiert den Titel der Einstellung auf dem Einstellungsbildschirm.

→13: In dieser Zeile wird das PreferenceCategory-Element für die Zeitvorgabe definiert.

→14: Hier wird der Schlüssel der Kategorie definiert.

→15: Hier wird der Titel der Kategorie definiert.

→16: In dieser Zeile beginnt die Definition des EditTextPreference-Elements, das die Vorgabezeit in Minuten speichert, die ein Erinnerungstermin hinter der aktuellen Uhrzeit liegen soll.

→17: In dieser Zeile wird der Schlüssel für die Zeitvorgabe der Termineinstellung definiert.

→18: In dieser Zeile wird der Titel des Dialogfelds definiert, das angezeigt wird, wenn die Einstellung ausgewählt wird.

→19: In dieser Zeile wird die Meldung definiert, die im Dialogfeld angezeigt werden soll.

→20: In dieser Zeile wird die Zusammenfassung der Einstellung definiert, die auf dem Hauptbildschirm der Einstellungen angezeigt wird (siehe Abbildung 15.1).

→21: In dieser Zeile wird der Titel der Einstellung auf dem Einstellungsbildschirm definiert.

String-Ressourcen hinzufügen

Damit Sie Ihre Anwendung kompilieren können, müssen Sie die String-Ressourcen für die Einstellungen hinzufügen. Tragen Sie dazu die folgenden Werte in die Datei res/values/strings.xml ein:

<!−− Einstellungen/Preferences −−>

<string name="pref_category_task_defaults_key"

>task_default_category</string>

<string name="pref_category_task_defaults_title">Vorgabe für

Titel</string>

<string name="pref_task_title_key">default_reminder_title</string>

<string name="pref_task_title_dialog_title">Vorgabe Termintitel

</string>

<string name="pref_task_title_message">Vorgabe für den Titel eines

Termins</string>

<string name="pref_task_title_summary">Vorgegebener Text für den

Termintitel</string>

<string name="pref_task_title_title">Titelvorgabe für Termine

</string>

<string name="pref_category_datetime_key">date_time_default

_category</string>

<string name="pref_category_datetime_title">Vorgaben für Datum/

Uhrzeit</string>

<string name="pref_default_time_from_now_key">time_from_now_default

</string>

<string name="pref_default_time_from_now_dialog_title">Zeit ab

jetzt</string>

<string name="pref_default_time_from_now_message">Vorgegebene

Zeitspanne in Minuten, die eine Terminerinnerung nach der  aktuellen Uhrzeit erfolgen soll.</string>

<string name="pref_default_time_from_now_summary">Stellt die

Vorgabezeit für Erinnerungstermine ein</string>

<string name="pref_default_time_from_now_title">Zeitvorgabe für

Termine</string>

Nun sollten Sie Ihre App kompilieren können.

Die Klasse PreferenceActivity nutzen

Einen Einstellungsbildschirm zu definieren, ist recht einfach, da Sie dazu nur die für die Attribute benötigten Werte festlegen müssen. Dass Sie den Bildschirm in XML definiert haben, heißt aber noch nicht, dass er auch auf dem Gerät angezeigt werden kann. Dazu müssen Sie noch eine PreferenceActivity erstellen.

PreferenceActivity zeigt die Einstellungen auf dem Bildschirm hierarchisch an und folgt dabei den in einer Einstellungsdatei definierten XML-Strukturen, wie Sie sie eben erstellt haben. Die Einstellungen können mehrere Bildschirmseiten umfassen (wenn mehrere verschachtelte PreferenceScreen-Objekte angezeigt werden). Diese Einstellungen werden automatisch in SharedPreferences gespeichert. Zudem folgen die angezeigten Einstellungen automatisch der Optik der Systemeinstellungen, wodurch die Gestaltung Ihrer Anwendung mit den üblichen Gepflogenheiten der Android-Plattform konsistent bleibt und damit zur positiven Benutzererfahrung beiträgt.

Um den gerade erstellten PreferenceScreen zu erzeugen und anzuzeigen, fügen Sie eine von PreferenceActivity abgeleitete Aktivität zu Ihrer Anwendung hinzu, die ich hier TaskPreferences genannt habe. Erstellen Sie die Datei TaskPreferences.java für diese neue Klasse, fügen Sie dort den Code aus Listing 15.2 und die erforderlichen import-Anweisungen ein.

public class TaskPreferences extends PreferenceActivity { →1

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

addPreferencesFromResource(R.xml.task_preferences); →5

EditTextPreference timeDefault = (EditTextPreference)

findPreference(getString(R.string.pref_default_time_from _now_key)); →8

timeDefault.getEditText().setKeyListener

(DigitsKeyListener.getInstance()); →9

}

}

Listing 15.2: Die Datei TaskPreferences

Das war’s! Mehr Code wird für das Anzeigen, die Bearbeitung und die dauerhafte Speicherung von Einstellungen in Android nicht benötigt! Die hervorgehobenen Codezeilen werden nachfolgend erläutert:

→1: Die Klassendatei TaskPreferences wird als Nachfahre der Basisklasse PreferenceActivity definiert.

→5: Dem Aufruf der Methode addPreferencesFromResource() wird die Ressourcenkennung der im Ordner res/xml gespeicherten Datei task_preferences.xml übergeben. Anstelle dieser Methode sollten Sie eigentlich PreferenceFragment aus der Support-Library verwenden, weshalb Eclipse hier wahrscheinlich eine Warnung anzeigen wird, die Sie aber getrost ignorieren können.

→8: Ich ermittle EditTextPreference für die vorgegebene Zeit der Terminerinnerung durch Aufruf der Methode findPreference(), der ich den in der Datei task_ preferences.xml definierten Schlüssel übergebe. Auch anstelle dieser Methode sollten Sie eigentlich PreferenceFragment aus der Support-Library verwenden, weshalb Eclipse hier wohl eine Warnung anzeigt, die Sie ignorieren können.

→7: In dieser Zeile übernehme ich das EditText-Objekt, das ein Kind von EditTextPreference ist, mit der Methode getEditText(). Für dieses Objekt setze ich zur Überwachung von Tastenereignissen KeyListener. Dazu verwende ich die Methode setKeyListener(), der ich eine Instanz von DigitsKeyListener übergebe, die nur die Eingabe von Ziffern bei der Vorgabe für den Erinnerungstermin in EditTextPreference zulässt.

Sie wollen sicherlich verhindern, dass Benutzer Texte in das Feld eingeben, bei denen es sich dann aber nicht mehr um zulässige Integer-Werte handeln würde. Die Verwendung von DigitsKeyListener sorgt dafür, dass hier nur Ziffern für diese Einstellung eingegeben werden können.

Nun kann die Aktivität benutzt werden. Über diese PreferenceActivity können Benutzer ihre Einstellungen bearbeiten und speichern. Wie Sie sehen können, mussten Sie für diese Implementierung nur sehr wenig Code schreiben. Im nächsten Schritt sorgen Sie durch Hinzufügen eines entsprechenden Menüelements dafür, dass der Einstellungsbildschirm in Ihrer App auch angezeigt und genutzt werden kann.

Übernehmen Sie Ihre neue PreferenceActivity mit der folgenden Codezeile auch in die Datei AndroidManifest.xml:

<activity android:name=".TaskPreferences" android:label="@string/
app_name" />

Anlegen der Klasse PreferenceActivity

Um diese neue Aktivität anzulegen, müssen Sie ein Menüelement zu ReminderListActivity hinzufügen. Dazu müssen Sie eine neue Menüdefinition in die Datei list_menu.xml eintragen, die sich im Ordner res/menu befindet. Wenn Sie diese Datei aktualisieren, ändern Sie das Menü zur ReminderListActivity. Die aktualisierte Datei list_menu.xml sieht wie folgt aus, wobei die neuen Zeilen fett hervorgehoben werden:

<?xml version="1.0" encoding="utf−8"?>

<menu

xmlns:android="http://schemas.android.com/apk/res/android">

<item android:id="@+id/menu_insert"

android:icon="@android:drawable/ic_menu_add"

android:title="@string/menu_insert" />

<item android:id="@+id/menu_settings"

android:icon="@android:drawable/ic_menu_preferences"

android:title="@string/menu_settings" />

</menu>

Das letzte Element fügt ein Menüelement für die Einstellungen hinzu. Es verwendet das interne Android-Symbol und eine String-Ressource namens menu_settings.

Sie müssen eine neue String-Ressource namens menu_settings mit dem Wert Einstellungen zu Ihren String-Ressourcen in der Datei strings.xml hinzufügen.

Verarbeitung der Menüauswahl

Jetzt haben Sie zwar Ihr Menü aktualisiert, müssen aber noch dafür sorgen, dass Ihre Anwendung auch auf die Auswahl dieses Menüelements reagiert. Dazu müssen Sie den Code der Methode onOptionsItemSelected() in ReminderListFragment erweitern. Der Code für das Menü EINSTELLUNGEN wird fett hervorgehoben:

@Override
public boolean onOptionsItemSelected (MenuItem item) {

switch(item.getItemId()) {

case R.id.menu_insert:

((OnEditReminder) getActivity()).editReminder(0);

return true;

case R.id.menu_settings:

Intent i = new Intent(getActivity(), TaskPreferences.class);

startActivity(i);

return true;

}

return super.onOptionsItemSelected(item);

}

Der hervorgehobene Code erzeugt hier einfach ein neues Intent-Objekt mit der Zielklasse TaskPreferences. Wenn der Benutzer das Menüelement EINSTELLUNGEN auswählt, wird ihm nun der entsprechende Bildschirm angezeigt. Wenn Sie die App starten und diese Menüauswahl treffen, sollte das Ergebnis Abbildung 15.3 ähneln.

Abbildung 15.3: Der Bildschirm EINSTELLUNGEN

Während der Programmausführung mit Einstellungen in Aktivitäten arbeiten

Einstellungen in einer PreferenceActivity vornehmen zu können, ist zwar nützlich, ist aber letztlich eigentlich wenig wert, wenn Sie sie nicht während der Programmausführung aus dem SharedPreferences-Objekt auslesen und in Ihrer Anwendung nutzen können. Aber auch das macht Ihnen Android glücklicherweise leicht.

In der Terminplaner-Anwendung müssen Sie diese Werte in ReminderEditFragment auslesen, um die Vorgaben zu setzen, wenn Benutzer einen neuen Termin erstellen. Da die Einstellungen in SharedPreferences gespeichert sind, können Sie darauf in verschiedenen Aktivitäten in Ihrer Anwendung zurückgreifen.

Werte von Einstellungen ermitteln

Öffnen Sie ReminderEditFragment.java und gehen Sie zur Methode onCreateView(). Sie stellt fest, ob es sich um einen bereits bestehenden oder einen neuen Termin handelt. Bei einem neuen Termin werden die Vorgabewerte aus SharedPreferences in die Aktivität übernommen, um genutzt werden zu können. Wenn ein Benutzer aus irgendwelchen Gründen bisher noch keinerlei Einstellungen vorgenommen hat, handelt es sich dabei um leere Strings, die dann ignoriert werden. Kurz gesagt werden die Einstellungen nur genutzt, wenn sie vom Benutzer zuvor gesetzt wurden.

Um die Werte von Einstellungen abzurufen, müssen Sie das SharedPreferences-Objekt wie nachfolgend beschrieben nutzen. Fügen Sie den in Listing 15.3 fett dargestellten Code am Ende von onCreateView() vor der Zeile return v; ein.

if (mRowId == 0) { →1

// Neuer Termin – gesetzte Vorgaben laden

SharedPreferences prefs = PreferenceManager →3

.getDefaultSharedPreferences(getActivity());

.getDefaultSharedPreferences(getActivity());

String defaultTitleKey = getString(R.string.pref_task_title_key); →5

String defaultTimeKey =

getString(R.string.pref_default_time_from_now_key); →7

String defaultTitle = prefs.getString(defaultTitleKey, null); →8

String defaultTime = prefs.getString(defaultTimeKey, null); →9

if (defaultTitle != null)

mTitleText.setText(defaultTitle); →12

if (defaultTime != null && defaultTime.length()>0)

gmCalendar.add(Calendar.MINUTE, Integer.parseInt(defaultTime));  →15

updateButtons(); →17

} else {

// Einen Background−Loader auslösen, um die Daten aus der

// Datenbank zu übernehmen.

getLoaderManager().initLoader(0, null, this);

}

Listing 15.3: Werte aus SharedPreferences übernehmen

Die neuen Codezeilen werden nachfolgend erläutert:

→1: Die if-Anweisung kümmert sich um die logischen Abläufe bei einem neuen Termin.

→3: In dieser Zeile wird das SharedPreferences-Objekt vom statischen Aufruf getDefaultSharedPreferences() des PreferenceManager-Objekts übernommen.

→5: In dieser Zeile übernehme ich den Schlüsselwert der Einstellung für die Titelvorgabe aus den String-Ressourcen. Dabei handelt es sich um denselben Schlüssel, der in Listing 15.1 bei der Definition der Einstellung verwendet wurde.

→7: In dieser Zeile wird der Schlüsselwert für den vorgegebenen Zeitversatz (in Minuten) aus den Einstellungen übernommen.

→8: In dieser Zeile wird mit einem Aufruf von getString() des SharedPreferences-Objekts die Vorgabe für den Titel aus den Einstellungen übernommen. Beim ersten Parameter handelt es sich um den Schlüssel der Einstellung und beim zweiten um den Vorgabewert, der verwendet wird, wenn die Einstellung nicht existiert (oder nicht gesetzt wurde). Sofern keine Einstellung existiert, soll der Vorgabewert hier null lauten.

→9: In dieser Zeile wird die Zeitvorgabe aus den Einstellungen übernommen. Dabei wird dasselbe Verfahren wie in Zeile 8 mit einem anderen Schlüssel verwendet.

→12: Diese Zeile setzt den Textwert der EditText-View und damit den Titel des neuen Termins. Dieser Wert wird nur gesetzt, wenn es sich bei dieser Einstellung nicht um einen leeren String gehandelt hat.

→15: Wenn es sich beim Wert der Einstellung nicht um einen leeren String gehandelt hat, inkrementiere ich in dieser Zeile die Zeit des lokalen Calendar-Objekts durch Aufruf der Methode add() mit dem Parameter Calendar.MINUTE. Die Konstante Calendar.MINUTE teilt dem Calendar-Objekt mit, dass es sich beim nächsten Parameter um eine Minutenangabe handelt, der entsprechend hinzuaddiert werden soll. Wenn dadurch Stunden- oder Tagesgrenzen überschritten werden, nimmt Ihnen das Calendar-Objekt die erforderlichen Aktualisierungen der anderen Felder automatisch ab.

Wird die Zeit beispielsweise am 31.12.2013 um 23:45 Uhr um 60 Minuten vorgesetzt, ergibt sich als neuer Wert für das Calendar-Objekt 01.01.2014 00:45. Da EditTextPreference alle Werte als Strings speichert, muss ich den String mit der Minutenangabe mit der Methode Integer.parseInt() in einen integer-Wert umwandeln. Zusammen mit den Werten des lokalen Calendar-Objekts werden auch die der Zeitauswahlelemente und der zugehörigen Schaltflächen geändert.

→17: In dieser Zeile wird der Text auf der Schaltfläche mit der Uhrzeit aktualisiert, damit sie der Zeit entspricht, die zum vorhandenen lokalen Calendar-Objekt hinzuaddiert wurde.

Wenn Sie die Anwendung starten, können Sie nun die Einstellungen setzen und sehen, wie sie sich beim Hinzufügen eines neuen Termins zur Liste auswirken. Löschen Sie versuchsweise die Einstellungen und erstellen Sie einen neuen Termin. Beachten Sie, dass die zuvor festgelegten Vorgaben aufgehoben wurden. Nun, das war leicht!

Werte von Einstellungen programmgesteuert setzen

Auch wenn diese Möglichkeit in der Terminplaner-Anwendung nicht genutzt wird, müssen Sie die Werte von Einstellungen doch zuweilen über den Code Ihrer Apps ändern. Stellen Sie sich vor, dass Sie eine Anwendung für ein Helpdesk-Ticketsystem entwickeln, bei dem Benutzer jeweils ihre aktuelle Abteilung eingeben müssen. Es gibt zwar eine Einstellung zur Vorgabe der Abteilung, aber die Benutzer wollen den Bildschirm mit den Einstellungen einfach nicht benutzen. Daher tragen sie die Abteilung immer wieder neu in Ihre Anwendung ein. Dann können Sie Routinen schreiben, die feststellen, dass die Benutzer immer wieder dieselbe Abteilung bei den einzelnen Helpdesk-Tickets eintragen. Nehmen wir an, dass es sich dabei um die Abteilung Buchhaltung handelt. Dann können Sie den Benutzer fragen, ob diese Abteilung als Vorgabe verwendet werden soll. Bestätigt der Anwender dies, können Sie diese Einstellung programmgesteuert vornehmen.

Um Einstellungen programmgesteuert ändern zu können, benötigen Sie eine Instanz von SharedPreferences. Wie Listing 15.4 demonstriert, erhalten Sie sie über PreferenceManager. Anschließend können Sie eine Instanz des Editor-Objekts dazu verwenden, um verschiedene Einstellungen zu ändern. Wenn Einstellungen geändert wurden, müssen sie noch gespeichert werden. Auch das zeigt Listing 15.4.

SharedPreferences prefs =

PreferenceManager.getDefaultSharedPreferences(this); →1

Editor editor = prefs.edit(); →2
editor.putString("default_department", "Buchhaltung"); →3
editor.commit(); →4

Listing 15.4: Einstellungen programmgesteuert bearbeiten

Die nummerierten Codezeilen werden nachfolgend erläutert:

→1: Vom PreferenceManager wird eine Instanz des SharedPreferences-Objekts übernommen.

→2: Durch den Aufruf der Methode edit() des SharedPreferences-Objekts wird eine Instanz des Editor-Objekts übernommen.

→3: In dieser Zeile wird durch Aufruf der Methode putString() des Editor-Objekts eine Einstellung mit dem Schlüsselwert default_department bearbeitet. Der Wert wird dabei auf "Buchhaltung" gesetzt. Normalerweise sollten Schlüsselwerte aus String-Ressourcen übernommen und String-Werte vom Programm ermittelt oder durch Benutzereingabe festgelegt werden. Hier sollte der Codeschnipsel aber einfach nur kurz und verständlich bleiben.

→4: Wenn Änderungen irgendwelcher Einstellungen vorgenommen wurden, müssen Sie die Methode commit() des Editor-Objekts aufrufen, um sie dauerhaft in SharedPreferences zu speichern. Der Aufruf von commit() ersetzt automatisch alle aktuell in SharedPreferences gespeicherten Werte mit dem im Aufruf von putString() angegebenen Schlüssel.

Wenn Sie die Methode commit() des Editor-Objekts nicht aufrufen, werden die Änderungen nicht dauerhaft gespeichert und Ihre Anwendung wird nicht wie erwartet funktionieren.

Teil IV

Tablets

In diesem Teil …

Teil IV versorgt Sie mit den speziellen Grundlagen für die Entwicklung von Apps für Android-Tablets. Android-Tablets unterscheiden sich letztlich doch noch einmal recht deutlich von Android-Mobiltelefonen und in diesem Teil werden Ihnen schrittweise die Änderungen vorgestellt, die Sie an unserem Terminplanungsprogramm vornehmen müssen, damit es auch auf Android-Tablets läuft.

Und sollten Sie sich auch in Bereiche außerhalb der üblichen Google-Gefilde der Android-Tablets vorwagen wollen, finden Sie alle notwendigen Informationen zur Unterstützung von Android-basierten Geräten, die sich wie beispielsweise das Amazon Kindle Fire außerhalb der üblichen Google-Pfade bewegen, in Kapitel 18.

16

Apps für Tablets entwickeln

In diesem Kapitel

Die wichtigsten Besonderheiten von Tablets

Für Tablets erforderliche Änderungen an Ihrer App vornehmen

Nutzung der Aktionsleiste

Support-Library und ActionBarSherlock einsetzen

Damit Ihre App auf Tablets und Telefonen wie gewünscht funktioniert, müssen Sie einige gängige Tricks beherrschen. In diesem Kapitel erhalten Sie erst einen Überblick über die Unterschiede zwischen Telefonen und Tablets und erfahren dann, wie Sie Ihre Terminplaner-App gestalten können, damit sie auf beiden Gerätekategorien läuft.

Berücksichtigung der Unterschiede zwischen Telefon und Tablet

Zwischen Android-Tablets und Android-Telefonen gibt es neben der direkt ins Auge fallenden unterschiedlichen Größe noch einige weitere Unterschiede:

Android-Tablets sind meist deutlich größer als ihre Telefonkollegen.

Das Tablet-Design ist darauf abgestimmt, dass die Geräte mit beiden Händen festgehalten werden, während Telefone nur in einer Hand gehalten werden.

Während die Bildschirmdiagonalen von Android-Tablets meist zwischen sieben und gut zehn Zoll (etwa 17 und 26 cm) liegen, sind die Anzeigen selbst der größten Telefone normalerweise nicht größer als etwa fünf Zoll (etwa 12,5 cm).

Die Grenze zwischen Tablets und Telefonen verwischt sich an der Fünf-Zoll-Grenze ein wenig. Einige dieser »Zwitter« werden als Telefone, andere mit beinahe denselben technischen Daten hingegen bereits als »Tablets« vermarktet.

Das Seitenverhältnis und die Ausrichtung der Bildschirme sind vom jeweiligen Einsatzzweck der Tablets abhängig, während die allermeisten Telefone standardmäßig das Hochformat verwenden.

Viele Android-Tablets wurden für die Betrachtung von Filmen im Kino-Breitwandformat entwickelt und daher bevorzugt im Querformat genutzt. Andere, wie das Nexus 7 und das Kindle Fire, wurden für den vorwiegenden Einsatz im Hochformat gestaltet. Das heißt natürlich nicht, dass Sie Apps nicht auch auf einem Querformat-Tablet im Hochformat (oder andersherum) nutzen könnten, Sie sollten deswegen aber nicht vergessen, dass viele Benutzer Ihre Apps möglicherweise nicht in der Geräteausrichtung nutzen, die Sie vorwiegend für Ihre Tests verwendet haben.

Tablets und Telefone weisen zudem einige Unterschiede im Hardwaredesign und in ihrem Betrieb auf, die Rückwirkungen auf die Anwendungsentwicklung haben. In der folgenden Liste werde diese aus der Tablet-Perspektive beschrieben:

Tablets verfügen häufig nicht über ständige 3G- oder 4G-Datenverbindungen.

Tablets sind meist größer, besitzen stärkere Batterien und profitieren von einer sehr viel längeren Batterielebensdauer als ihre Telefongegenstücke.

In Tablets werden häufiger weniger leistungsfähige oder gar keine Kameras verbaut, da diese in Tablets typischerweise weniger häufig und intensiv genutzt werden.

Bei Tablets fehlen häufiger so verbreitete Telefonfunktionen wie der GPS-Dienst für die Positionsbestimmung.

Weiterhin sollten Sie damit rechnen, dass Sie neue Apps so entwerfen oder alte so umgestalten müssen, dass sie auch neue Tablet-Funktionen unterstützen. Mit Android 3.x und später auch mit der Version 4.x unterstützen Android-Tablets Fragmente, das als Aktionsleiste bekannte Element der Benutzerschnittstelle und das Holo-Thema. Benutzer erwarten, dass diese neuen Möglichkeiten bei Aktualisierungen Ihrer Apps für diese Versionen mit in deren Funktionsumfang aufgenommen werden.

Optimierung des Terminplaners für Tablets

Zwecks besserer Anpassung an die Unterschiede können Sie einige Techniken nutzen, um die Terminplanungs-App so zu aktualisieren, dass sie auf Tablets und Telefonen tadellos läuft.

Nutzen Sie die hier vorgestellten Verfahren immer beim Design von Android-Apps, da wahrscheinlich immer Nutzer beider Gerätekategorien zur Zielgruppe Ihrer App zählen werden.

Variable Layouts für unterschiedlich große Bildschirme

Wenn Sie ein Layout für mehrere unterschiedliche Bildschirmgrößen entwickeln, sollten Sie ein variables Layout nutzen. Damit können Sie sich selbst als Entwickler und auch den Benutzern eine Menge Ärger und Frust ersparen.

Im Rahmen der iOS-Entwicklung haben Sie es mit iPhone und iPad im Grunde genommen nur mit drei Bildschirmabmessungen zu tun, um die Sie sich kümmern müssen. Für diese benötigen Sie noch Abbildungen in niedriger und hoher Auflösung, aber das lässt sich recht leicht bewerkstelligen, wenn Sie diese erst für das iPhone und dann für die iPads entwickeln. Danach müssen Sie diese Bilder nur noch an den passenden Positionen ablegen.

So einfach ist das Ganze bei der Entwicklung von Apps für Android nicht. Android-Layouts müssen variabel sein und sich eigenständig an die kleineren (und manchmal größeren) Unterschiede hinsichtlich der Breite und Höhe der Gerätebildschirme anpassen können. Während es bei iOS im Wesentlichen nur drei Bildschirmabmessungen gibt, haben Sie es bei Android mit Dutzenden oder gar Hunderten zu tun.

Das ähnelt der Entwicklung von Layouts für Webseiten. Dabei können Sie auch nicht davon ausgehen, dass alle Benutzer die Seiten in einer einheitlichen Größe (wie 800x600 Pixel) im Browserfenster betrachten werden. Die Benutzer werden die Seiten in größeren (und manchmal auch kleineren) Browserfenstern betrachten. Daher muss das von Ihnen entwickelte Layout flexibel genug sein, um bei allen denkbaren und halbwegs sinnvollen Abmessungen einen ansprechenden Eindruck zu hinterlassen. Denselben Anforderungen müssen Sie sich auch bei der Entwicklung von Apps für Android stellen.

Wie lässt sich dieses Zauberwerk aber verrichten? Erst einmal sollten Sie gar nicht erst versuchen, mit fest vorgegebenen Abmessungen (wie 10px oder 120dp) in Ihren Layouts zu arbeiten. Nutzen Sie stattdessen bevorzugt und möglichst weitgehend relative Abmessungen wie wrap_content und fill_parent. Die Zielsetzung lautet dabei, ein möglichst variables Layout zu erstellen, das sich selbstständig an das jeweilige Gerät anpasst.

Der folgende Code enthält ein Layout, das zu viele Eigenarten des Geräts, auf dem er ausgeführt werden soll, einfach voraussetzt:

<?xml version="1.0" encoding="utf−8"?>

<TextView xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="300px"

android:layout_height="match_parent"

android:lines="1"

android:text= "Vorkommen des Wortes 1Internet1

in der Gettysburg−Rede: 0 (unbestätigt)" />

Listing 16.1: Problematischer Code

Dabei treten mehrere Probleme auf:

Es wird eine TextView mit festen und nicht mit variablen Abmessungen wie wrap_ content und fill_parent verwendet.

Für die Abmessungen wird die Einheit Pixel (px) verwendet, die nicht automatisch für die verschiedenen Geräte skaliert wird, wie es bei dp (geräteunabhängigen Pixeln – device-independent pixels) der Fall wäre.

Die Anzahl der Zeilen wird für TextView mit 1 vorgegeben, ohne Android dabei mitzuteilen, was mit überschüssigem Text geschehen soll.

ScrollView wird nicht verwendet. Wenn das Layout höher als der Gerätebildschirm sein sollte, gibt es keine Möglichkeit, Elemente auf den Bildschirm zu holen, die sich außerhalb des Sichtfelds befinden.

Allgemein sollten viele Ihrer Layouts in ein einzelnes ScrollView eingebettet werden. Auf diese Weise können Sie unerwarteten Überlauf am unteren Bildschirmrand abfangen. Zu den Ausnahmen zählen Layout-Elemente, die bereits eigene Rollbalken mitbringen, wie beispielsweise ListView, und daher nicht in ein ScrollView-Element eingebettet werden müssen. Die Terminplanungs-App benötigt zwar nur ein einziges TextView-Element, komplexere Layouts erfordern aber schnell mehrere.

Da fill_parent mit Android 2.2 in match_parent umbenannt wurde, wird match_parent von älteren Geräten nicht unterstützt. Sofern Sie minSdkVersion jedoch auf einen Wert kleiner als 8 setzen, sollten Sie fill_parent anstelle von match_parent verwenden.

Abbildung 16.1 zeigt das TextView-Element aus Listing 16.1. Dabei wird der Text wegen der starren Größenvorgabe plötzlich mitten im Satz abgeschnitten. Wenn der Entwickler mit einem variablen Layout gearbeitet hätte, wäre das nicht passiert.

Abbildung 16.1: Ein statisches Layout

Die Fehler in diesem speziellen Beispiel lassen sich leicht dadurch beheben, dass die Breite (width) des TextView-Elements und android:lines="1" durch android:max Lines="3" ersetzt wird:

<?xml version="1.0" encoding="utf−8"?>

<TextView xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:maxLines="3"

android:text="Vorkommen des Wortes 1Internet1 in der  Gettysburg−Rede: 0 (unbestätigt)" />

Beim Entwickeln Ihrer Layouts sollten Sie immer die maximale Größe der darin vorhandenen verschiedenen Elemente berücksichtigen. Die App-Inhalte können unerwartet viel Raum für sich beanspruchen. Daher ist es wichtig, derartige Situationen zu antizipieren und mit einzuplanen, statt letztlich eine scheußlich aussehende App zu erstellen.

Weitere Fragmente hinzufügen

Viele Entwickler erwarten von Google, der Quelle aller Weisheit, scheinbar Allwissenheit. Zu Beginn der Entwicklung von Android konnte aber auch Google noch nicht wissen, wie beliebt Tablets binnen nur fünf Jahren werden würden. Sie haben sich aber äußerst schnell verbreitet und sie bieten eine Menge Platz auf ihren Bildschirmen, der erst einmal gefüllt werden will.

Mit Android 3.0 (Honeycomb) wurden Fragmente eingeführt, mit deren Hilfe der zusätzlich verfügbare Platz besser genutzt werden kann. Der Grundgedanke besteht dabei darin, dass sich die Aktivitäten bei einem Telefon typischerweise um ein, zwei oder drei eigenständige Gruppen von wiederverwendbaren Bildschirmelementen drehen. Wenn Sie diese Gruppen jeweils in eigene Fragmente einbetten, lassen sie sich leicht über verschiedene Aktivitäten hinweg wiederholt nutzen.

Genau das werden Sie in Kapitel 17 auch machen. Dort werden Sie die beiden Elemente ReminderEditFragment und ReminderListFragment aus den beiden Telefon-Activities in eine einzelne neue Aktivität einbetten, die Tablet-Benutzer eher ansprechen dürfte.

Abbildung 16.2 verdeutlicht das Layout der Reminder-Fragmente auf einem Tablet.

Abbildung 16.2: In Fragmente lassen sich auf einem Tablet zwei Activities einbetten.

Ohne Fragmente müssten Sie das Rad jeweils neu erfinden, wenn Sie eine Aktivität erstellen wollen, die mehrere Aufgaben gleichzeitig anzeigt. Unter Verwendung von Fragmenten schreiben Sie den Code nur ein einziges Mal und können ihn anschließend beliebig oft neuerlich nutzen.

Verschiedene Layouts für unterschiedliche Geräte erstellen

Fragmente sind zwar eine praktische Funktion für Entwickler, aber wie sollen die Fragmente gestaltet werden, damit sie die geeignete und zu einem Gerät passende Präsentation bieten? Die im Vergleich zum Telefon riesige Bildschirmfläche der Tablets kann ein oder mehrere Fragmente in einer einzigen Activity anzeigen.

Sie können ein Layout mit einer einzigen darin enthaltenen Activity für Ihr Telefon und ein anderes Layout mit mehreren darin enthaltenen Fragmenten für Ihr Tablet benutzen. Als Beispiel soll hier das Layout ReminderListActivity für Telefonbildschirme in der Terminplanungs-App dienen:

<?xml version="1.0" encoding="utf−8"?>

<fragment

xmlns:android="http://schemas.android.com/apk/res/android"

android:name="com.dummies.android.taskreminder.ReminderList
Fragment"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

/>

Diesen Code könnten Sie wie folgt ändern, damit es zwei Fragmente für ein zweispaltiges Layout auf einem Tablet nutzt:

<?xml version="1.0" encoding="utf−8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:orientation="horizontal"

android:layout_height="fill_parent">

<fragment

android:id="@+id/list_fragment"

android:name="com.dummies.android.taskreminder.ReminderListFragment"

android:layout_width="0dp"

android:layout_weight="1"

android:layout_height="fill_parent"

/>

<fragment

android:id="@+id/edit_fragment"

android:name="com.dummies.android.taskreminder.ReminderEditFragment"

android:layout_width="0dp"

android:layout_weight="1"

android:layout_height="fill_parent"

/>

</LinearLayout>

Erläuterungen zur Arbeitsweise dieses Codes finden Sie in Kapitel 17.

Nutzung der Aktionsleiste

Die Bedienoberflächen der Betriebssysteme ändern sich laufend und Android bildet hier keine Ausnahme. Ein praktisches Beispiel dazu stellt die Aktionsleiste dar, die auf allen Geräten mit Android 3.0 und 4.0 zum Standard zählt. Wenn Sie Apps für Tablets schreiben wollen, werden Sie dieses neue Element bestimmt nutzen.

Vor Android 3.0 besaßen alle Android-Telefone einen speziellen Schalter, über den das Menü aufgerufen werden konnte. (Mehr zum Menü-Schalter finden Sie in Kapitel 10.) Diesem Schalter begegnen Sie an Geräten für Android ab Version 3.0 nur noch selten. Die Elemente aus dem Menü befinden sich nun in der Aktionsleiste.

In der Aktionsleiste lassen sich diese Funktionen (beispielsweise zum Hinzufügen oder Entfernen einer Aufgabe oder den Zugriff auf die Einstellungen einer App) deutlich leichter finden. Die wichtigen Funktionen werden dabei ständig auf dem Bildschirm angezeigt, während die weniger wichtigen in ein Untermenü wie in Abbildung 16.3 ausgelagert werden.

Abbildung 16.3: Die startbereite Aktionsleiste mit mehreren in das Untermenü ausgelagerten Elementen

Als Entwickler müssen Sie festlegen, welche Aktionen die wichtigeren sind, die Android dann möglichst auf dem Bildschirm zu halten versucht. Sie können Ihren Menüelementen drei verschiedene Aktionen zuweisen:

android:showAsAction="ifRoom": Diese XML-Anweisung ist für unwichtige Elemente gedacht, die nur bei ausreichend verfügbarem Platz direkt in der Aktionsleiste angezeigt und ansonsten von Android in das Untermenü ausgelagert werden können.

android:showAsAction="always": Diese XML-Anweisung ist für die ersten paar Elemente gedacht, die möglichst ständig in der Aktionsleiste auf dem Bildschirm angezeigt werden sollen.

android:showAsAction="never": Diese XML-Anweisung verwenden Sie für Elemente, die immer in das Untermenü ausgelagert werden sollen.

Leicht könnten Sie der Versuchung unterliegen, für Ihre Aktionen android: showAsAction="ifRoom" oder "always" zu verwenden. Sie sollten die Aktionsleiste aber nicht mit zu vielen Aktionen überladen, sondern lieber aufgeräumt darstellen. Erwägen Sie besser, möglichst viele Elemente wie »Einstellungen« über die Option "never" in das Untermenü auszulagern.

Das Beste an der Aktionsleiste ist, dass Sie gar nichts machen müssen, um sie zu Ihrer App hinzuzufügen. Alle Menüs verwandeln sich bei der Ausführung auf Geräten mit Android ab Version 3.0 wie durch ein Wunder in Elemente der Aktionsleiste.

Die Support-Library und ActionBarSherlock nutzen

Natürlich verwenden die Geräte nicht durchweg die neuesten Technologien. Als dieses Buch verfasst wurde, liefen auf vielen Geräten noch ältere Android-Versionen (vor 4.0). Und diese sollten Sie nicht aus den Augen verlieren.

Sie sollten Ihr Zielpublikum kennen! Unter der Adresse http://developer.android.com/about/dashboards finden Sie die jüngsten Statistiken zu den genutzten Android-Versionen. Momentan befinden sich die älteren Android-Versionen noch auf sehr vielen Geräten und insbesondere Telefonen im Einsatz. Und denken Sie daran, dass es meist mindestens etwa drei bis sechs Monate dauert, bis neue Android-Versionen für den deutschen Markt angepasst wurden.

Bestimmt werden Sie Fragmente und die Aktionsleiste nutzen wollen. Das ist aber recht witzlos, wenn auf den meisten Geräten Ihrer Nutzer noch Android 2.x läuft, das diese neuen, schicken Funktionen nicht unterstützt. Aber was kann man als Entwickler schon dagegen machen?

Glücklicherweise können hier die beiden folgenden Werkzeuge für Abhilfe sorgen:

Android-Support-Library (von Google): Gestattet die Nutzung von Fragmenten auch in Apps für ältere Android-Versionen.

ActionBarSherlock von Jake Wharton: Stellt auch in Apps für ältere Android-Versionen die Aktionsleiste bereit. Weitere Einzelheiten dazu erfahren Sie unter http://actionbarsherlock.com.

17

Apps auf Android-Tablets portieren

In diesem Kapitel

Einen Tablet-Emulator herunterladen

Activities nutzen

Getrennte Fragmente erstellen

Ihre Telefon-App soll nun auch die Tablets erobern und damit wird es jetzt erst richtig aufregend. Deshalb soll Ihnen dieses Kapitel zeigen, wie Sie die in Teil III entwickelte Terminplaner-App für Android-Tablets optimieren können.

Einen Tablet-Emulator konfigurieren

Zunächst einmal benötigen Sie ein Tablet, um Ihre App testen zu können. Wenn Sie bereits ein solches besitzen, sind Sie fein raus, falls nicht, können Sie einen Emulator installieren, der ein Android-Gerät auf Ihrem Rechner simuliert. Google nennt diese Geräte AVD (Android Virtual Device). Mit den folgenden Schritten erhalten Sie ein AVD für das Google Nexus 7:

1. Klicken Sie das Symbol des AVD-Managers in der Eclipse-Werkzeugleiste an oder wählen Sie WINDOW:ANDROID VIRTUAL DEVICE MANAGER.

2. Klicken Sie NEW an.

Das Dialogfeld CREATE NEW ANDROID VIRTUAL DEVICE wird angezeigt (Abbildung 17.1).

3. Wählen Sie ANDROID 4.2 – API LEVEL 17 im Listenfeld TARGET aus.

Das Nexus 7 nutzt zwar ursprünglich Android 4.1, wenn Sie bisher dem Buch gefolgt sind, dürfte bei Ihnen aber lediglich API Level 17 installiert sein, das Sie deshalb auch hier auswählen.

4. Nehmen Sie ansonsten die Einstellungen entsprechend Abbildung 17.1 vor oder behalten Sie die Voreinstellungen bei.

In diesem Dialogfeld unterscheiden sich die in den verschiedenen Versionen angebotenen Optionen. Im Beispiel wird ein Tablet im Querformat erstellt.

5. Klicken Sie abschließend die Schaltfläche CREATE AVD oder OK an.

6. Nun können Sie die gerade erstellte AVD im Dialogfeld ANDROID VIRTUAL DEVICE MANAGER markieren und über die Schaltfläche START ausführen.

Skalieren Sie den Bildschirm der AVDs bei deren Start möglichst nicht. Wegen der geringeren Pixeldichte von Computerbildschirmen leidet darunter die Darstellungsqualität normalerweise erheblich.

Abbildung 17.1: Einen Tablet-Emulator erstellen

Die Datei AndroidManifest aktualisieren

Sie müssen Android mitteilen, dass Ihre App auch auf den großen Bildschirmen von Tablets funktionieren soll. Ohne diese Einstellungen läuft Ihre App nur im Kompatibilitätsmodus. Dabei läuft sie so wie auf einem kleineren Bildschirm und wird von Android lediglich auf die volle Bildschirmgröße hochskaliert. Darunter kann die Darstellung Ihrer App leiden.

Erweitern Sie die Datei AndroidManifest.xml um die folgenden Zeilen mit dem Element supports−screens im <manifest>-Element und vor dem <application>-Element:

<supports−screens

android:largeScreens="true"

android:normalScreens="true"

android:smallScreens="true"

android:xlargeScreens="true" />

Wenn Sie eines der Attribute auf false setzen, bedeutet das nicht unbedingt, dass Ihre App von Geräten mit entsprechenden Bildschirmen nicht mehr heruntergeladen werden kann. Bei xlargeScreens="false" kann Ihre App beispielsweise auch von Geräten mit besonders großen Bildschirmen heruntergeladen werden, läuft dort allerdings im Kompatibilitätsmodus und wird von Android hochskaliert. Andererseits lässt sich die Bildschirmdarstellung zwar recht leicht hochskalieren, das Herunterskalieren ist aber schwierig. Daher kann Ihre App von Geräten mit kleinen Bildschirmen nicht heruntergeladen werden, wenn Sie smallScreens="false" gesetzt haben.

Activities für Tablets programmieren

Nach der Aktualisierung der Datei AndroidManifest.xml programmieren Sie im nächsten Schritt die neuen Activities, die nur von der Tablet-Version Ihrer App unterstützt werden.

ReminderListAndEditorActivity erstellen

Die Tablet-Version Ihrer App benötigt eine neue Hauptaktivität. Die für Telefone eignet sich für Tablets nicht, da diese bestehen bleiben soll, damit Ihre Apps auch weiterhin noch auf Telefonen laufen. Erstellen Sie daher eine Datei namens ReminderListAndEditorActivity.java und fügen Sie in diese den folgenden Code ein:

package com.dummies.android.terminplaner;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity; →4

public class ReminderListAndEditorActivity extends FragmentActivity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.reminder_list_and_editor); →10

}

}

Dieser Code funktioniert so:

→4: Diese Zeile sorgt dafür, dass Sie die Datei FragmentActivity aus der SupportLibrary und nicht die in Android ab Version 3.x integrierte Bibliothek benutzen. Ohne diese Angabe würde Ihr Code nicht mehr unter älteren Android-Versionen laufen.

→10: R.layout.reminder_list_and_editor definiert das Layout für diese Activity. Diese Datei gibt es zwar bisher noch nicht, Sie werden sie aber weiter hinten in diesem Kapitel noch erstellen.

Fügen Sie zur Datei AndroidManifest.xml hinter der bereits existierenden ReminderEditActivity, die bereits für Telefone erstellt wurde, diese neue Activity hinzu:

<activity android:name=".ReminderListAndEditorActivity" android:

label="@string/edit_reminder_title"/>

Nun müssen Sie der Activity noch einen Titel geben. Öffnen Sie dazu strings.xml und fügen Sie einen String für edit_reminder_title hinzu, wie beispielsweise Terminplaner − Bearbeiten.

Die richtige Activity auswählen

Die Terminplaner-App besitzt nun zwei Hauptaktivitäten, eine für Telefone und eine für Tablets. Welche soll Android aber nun verwenden, wenn Benutzer Ihre App starten?

Momentan verwendet Android die Activity für Telefone, weil diese mit dem Intent-Filter android.intent.action.MAIN verknüpft ist. Da die App keine Möglichkeiten hat, die richtige Activity automatisch für Sie auszuwählen, müssen Sie selbst im Quelltext dafür sorgen, dass diese Erkennung beim Start der App erfolgt, und dann bei Bedarf zur Tablet-Version umschalten.

Fügen Sie die folgende Methode zur Klasse ReminderListActivity hinzu:

private Boolean isTablet() {

int sizeMask = getResources().getConfiguration().screenLayout &

Configuration.SCREENLAYOUT_SIZE_MASK;

boolean large = (sizeMask == Configuration.SCREENLAYOUT_SIZE_LARGE);

boolean xlarge = (sizeMask == 4);

return large || xlarge;

}

Dieser Code erkennt, ob das Gerät einen großen (large oder xlarge) Bildschirm hat. Dann wird true zurückgegeben.

Warum wird sizeMask == SCREENLAYOUT_SIZE_LARGE für large, aber sizeMask == 4 für den booleschen Wert xlarge verwendet? Unter späteren Android-Versionen ist SCREENLAYOUT_SIZE_XLARGE gleich 4, weshalb Sie annehmen könnten, dass Sie 4 einfach durch SCREENLAYOUT_SIZE_XLARGE ersetzen können. SCREENLAYOUT_SIZE_XLARGE gibt es in Android aber erst seit API Level 9, weshalb der Versuch, diese Konstante auf älteren Geräten zu verwenden, nur zum Geräteabsturz führt. Da die Terminplaner-App aber auch noch Geräte vor API Level 9 unterstützen soll, können Sie die Konstante SCREENLAYOUT_SIZE_XLARGE nicht direkt verwenden. Unter http://d.android.com/reference/android/content/res/Configuration.html finden Sie weitere Informationen zu SCREENLAYOUT_SIZE_XLARGE.

Beginnend mit Android 3.2 sollte SCREENLAYOUT_SIZE_MASK nicht mehr verwendet und durch ein neues System von Größenkennzeichnern ersetzt werden. Dieses System arbeitet zwar sehr viel zuverlässiger hinsichtlich der Unterscheidung von Layouts für Telefone und Tablets, würde aber den Rahmen dieses Buches sprengen, zumal es (selbst bei Verwendung der Support-Library) nicht vor Version 3.2 verfügbar ist. Unter http://developer.android.com/guide/practices/screens_support.html finden Sie nähere Einzelheiten zu den neuen Kennzeichnern.

Fügen Sie zu Ihrer Methode onCreate() den fett dargestellten Code vor dem Aufruf von setContentView() ein:

@Override
public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

// Wechsel zur Tablet−Activity und diese hier bei

// Ausführung auf einem Tablet beenden.

if (isTablet()) {

startActivity(new Intent(this, ReminderListAndEditorActivity.class));

finish();

return;

}

setContentView(R.layout.reminder_list);

}

Wird der Code auf einem Tablet ausgeführt, wird ReminderListActivity sofort beendet und stattdessen ReminderListAndEditorActivity gestartet. Auf diese Weise arbeitet Ihre App optimal auf Telefonen, wenn Benutzer Ihre App starten. Wenn Benutzer Ihre App hingegen auf einem Tablet starten, läuft sie auch auf diesen Geräten tadellos. Für derartige Umschaltvorgänge wird lediglich ein klein wenig zusätzlicher Verwaltungscode benötigt, von dessen Ausführung Benutzer nichts merken.

Das Layout der Activity erstellen

Die Activity haben Sie bereits erstellt. Nun müssen Sie noch ein dazu passendes Layout erstellen. Und Sie wissen im Grunde genommen auch bereits, wie dieses Layout aussehen soll: Auf dem Bildschirm sollen ein List- und ein Edit-Fragment nebeneinander angeordnet werden.

Erstellen Sie eine Datei namens reminder_list_and_editor.xml im Ordner res/ layout und fügen Sie den folgenden Code darin ein:

<?xml version="1.0" encoding="utf−8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/

android"

android:layout_width="fill_parent"

android:orientation="horizontal"

android:layout_height="fill_parent"> →5

<fragment

android:id="@+id/list_fragment" →8

android:name="com.dummies.android.terminplaner.ReminderListFragment"  →9

android:layout_width="0dp" →10

android:layout_height="fill_parent" →11

android:layout_weight="1" /> →12

<FrameLayout

android:id="@+id/edit_container" →15

android:layout_width="0dp"

android:layout_height="fill_parent"

android:layout_weight="1" /> →18

</LinearLayout>

Der Code arbeitet wie folgt:

→5: Diese Zeile packt Ihre zwei Fragmente in ein einzelnes, bildschirmfüllendes, horizontales LinearLayout, um sie nebeneinander anzeigen zu lassen.

→8 bis  →9: In diesen Zeilen wird das List-Fragment namens "@+id/list_fragment" als erstes Fragment festgelegt. Anschließend wird der vollständige Klassenname mit com.dummies.android.terminplaner.ReminderListFragment angegeben.

→10: Diese Zeile weist dem Layout eine Breite von 0dp zu. In Zeile 12 erfahren Sie, warum diese Einstellung von Vorteil ist.

→11: Diese Zeile sorgt dafür, dass das List-Fragment horizontal die Hälfte des Bildschirms und vertikal den kompletten Bildschirm einnimmt.

→12 bis  →18: LinearLayout unterstützt den speziellen Parameter "layout_ weight", der dazu verwendet werden kann, den Bildschirm flexibel auf zwei oder mehr Views aufzuteilen. Um ihn verwenden zu können, setzen Sie die Breite (layout_width) oder Höhe (layout_height) des Kindelements von LinearLayout auf den Wert 0dp und geben jeder View ein Gewicht (layout_weight). LinearLayout addiert alle Gewichtungen und weist den einzelnen Nachfahren den Bereich zu, der dem Gewichtswert des einzelnen Nachfahren entspricht.

Zeile 12 weist den Views list_fragment und edit_container jeweils den Gewichtswert 1 zu, damit sie beide die Hälfte des Bildschirms einnehmen.

Sie können beispielsweise auch list_container den Wert 1 und edit_container den Wert 2 zuordnen. Dann beansprucht list_fragment ein Drittel und edit_container die übrigen zwei Drittel des Bildschirms.

Diese Vorgehensweise ist zwar recht praktisch, Sie sollten dabei aber beachten, dass sie sich nur für LinearLayout-Elemente eignet.

→15: In dieser Zeile wird ein Platzhalter ("@+id/edit_container") für ReminderEditFragment definiert. Ein solcher Platzhalter sollte bevorzugt werden, da die List-View zwar immer auf dem Bildschirm angezeigt wird, die Edit-View aber nur dargestellt wird, wenn Benutzer ein Element bearbeiten. Wenn Elemente nicht bearbeitet werden sollen, wäre es für den Benutzer irritierend, wenn ein leeres Edit-Fragment auf dem Bildschirm angezeigt werden würde.

Starten Sie Ihre App auf dem Telefon- und dem Tablet-Emulator. ReminderListAndEditorActivity sollte zwar auf dem Tablet-, nicht aber auf dem Telefon-Emulator angezeigt werden.

Bei Tablet-Apps mit Fragmenten arbeiten

Sie erstellen eine neue Activity für Ihre Tablet-App und müssen nun Fragmente hinzufügen. Sie müssen keine neuen Fragmente schreiben, um sie mit der Tablet-Activity nutzen zu können. Sie müssen aber einige Änderungen an den Fragmenten vornehmen, damit sie auch in der neuen Umgebung korrekt arbeiten.

Kommunikation zwischen Fragmenten

Wenn Sie einen Termin mit Ihrer Tablet-App hinzufügen wollen, wird das Edit-Fragment momentan noch nicht wie erwartet neben dem List-Fragment angezeigt. Stattdessen wird dann eine neue Activity angezeigt.

Dabei macht das Edit-Fragment nur immer noch genau das, was es tun sollte. Es zeigt weiterhin das Telefon-Verhalten, bei dem für jedes Fragment eine neue Activity gestartet wird.

Für dieses Verhalten sorgt diese Codezeile in ReminderListFragment:

startActivity(new Intent(this, ReminderEditActivity.class).putExtra(

ReminderProvider.COLUMN_ROWID, id));

Aber das sollte sich doch leicht ändern lassen, oder? Nicht so schnell. Wenn Sie diese Zeile so ändern, dass sie sich für Tablets eignet, »zerschießen« Sie damit die bereits vorhandene Telefon-App. Das liegt daran, dass eine (die bereits funktionierende) Funktionsweise für Telefone und eine andere für Tablets realisiert werden soll.

Zur Lösung dieses Problems erstellen Sie eine abstrakte Methode namens editReminder(), die sich auf Telefonen und Tablets unterschiedlich verhält. Dann ersetzen Sie den bereits vorhandenen Aufruf von startActivity() mit dieser aktualisierten Methode.

Sie sollten jetzt nicht einfach schnell die neue Methode editReminder() in ReminderListFragment einfügen. Das Fragment weiß nicht, ob es auf einem Telefon oder einem Tablet ausgeführt wird. Es gibt aber mit editReminder() eine Methode, die es aufrufen kann, über die Benutzer einen Erinnerungstermin bearbeiten können.

Sie müssen erkennen, das Sie, wenn Sie eine Version von editReminder() für Telefone und eine für Tablets benötigen, editReminder() an zwei Stellen definieren müssen, von denen eben eine auf Telefonen und die andere auf Tablets ausgeführt wird. Und wo läuft eine Klasse auf Telefonen und eine andere Klasse auf Tablets? Natürlich in der Activity. Also erstellen Sie editReminder() in den Activity-Klassen von Telefon und Tablet.

Mit Fragmenten kommunizieren

Wenn Fragmente miteinander kommunizieren können sollen, wickelt man diese Kommunikation am besten über Activities ab. Unter http://developer.android.com/guide/components/fragments.html finden Sie weitere Details über dieses wichtige Schema. Immer wenn ein Fragment mit einem anderen interagieren soll, sollten Sie dazu eine Methode in der Activity des Fragments benutzen und nicht direkt auf das andere Fragment zugreifen. Von einem Fragment auf ein anderes zuzugreifen ist nur dann sinnvoll, wenn Sie wissen, dass Sie Ihr Fragment nicht in einer anderen Activity wiederverwenden werden. Das Leben ist aber immer voller Überraschungen, weshalb Sie beim Schreiben von Fragmenten eigentlich immer davon ausgehen sollten, dass Sie sie wiederholt verwenden und nicht erneut codieren werden.

Führen Sie die nachfolgenden Schritte aus, um die Callback-Funktion editReminder() zu schreiben, die Ihr Fragment aufrufen kann:

1. Erstellen Sie eine neue Datei namens OnEditReminder.java als Schnittstelle und fügen Sie dort die Methode editReminder() ein:

package com.dummies.android.terminplaner;

public interface OnEditReminder {

public void editReminder(long id);

}

2. Implementieren Sie diese Methode in ReminderListActivity für Telefone.

Ändern Sie ReminderListActivity und fügen Sie die fett dargestellten Passagen hinzu:

package com.dummies.android.terminplaner;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class ReminderListActivity extends FragmentActivity

implements onEditReminder {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.reminder_list);

// Wechsel zu Tablet−Activity und diese hier bei

// Ausführung auf einem Tablet beenden.

if (isTablet()) {

startActivity(new Intent(this,

ReminderListAndEditorActivity.class));

finish();

return;

}

}

@Override

public void editReminder(long id) {

startActivity(new Intent(this,

ReminderEditActivity.class).putExtra(

ReminderProvider.COLUMN_ROWID, id));

}

}

Der Aufruf von editReminder()ist Zeile für Zeile mit dem in ReminderListFragment identisch, nur dass er sich hier in ReminderListActivity befindet.

3. Rufen Sie diese Methode vom Fragment aus auf.

Öffnen Sie ReminderListFragment und ändern Sie die Methoden onOptionsItem Selected() und onListItemClick() wie folgt:

@Override
public boolean onOptionsItemSelected(MenuItem item) {

switch (item.getItemId()) {

case R.id.menu_insert:

((OnEditReminder) getActivity()).editReminder(0);

return true;

case R.id.menu_settings:

startActivity(new Intent(getActivity(), TaskPreferences .class));

return true;

}

return super.onOptionsItemSelected(item);

}

@Override
public void onListItemClick(ListView l, View v, int position, long id) {

super.onListItemClick(l, v, position, id);

((OnEditReminder) getActivity()).editReminder(id);

}

Nun wird startActivity() in den einzelnen Methoden nicht mehr direkt aufgerufen. Die App ruft stattdessen getActivity() auf, um die Activity zu ermitteln, wandelt den Typ in OnEditReminder um und ruft dann editReminder() auf.

Die Typumwandlung wird in Java normalerweise wenig geschätzt, weil sie oft unsicher ist. Es ist jedoch sicher, das Ergebnis von getActivity() in OnEditReminder umzuwandeln, weil ReminderListFragment immer entweder in ReminderListActivity oder ReminderListAndEditorActivity sein wird und beide OnEditReminder implementieren. Sollten Sie das Fragment jemals zu einer anderen Activity hinzufügen, sorgen Sie dafür, dass diese ebenfalls OnEditReminder implementiert.

4. Implementieren Sie eine identische OnEditReminder-Schnittstelle in Ihrer ReminderListAndEditorActivity für Tablets.

Fügen Sie den fett dargestellten Code zu ReminderListAndEditorActivity hinzu:

public class ReminderListAndEditorActivity extends FragmentActivity implements OnEditReminder {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.reminder_list_and_editor);

}

@Override

public void editReminder(long id) {

// Noch zu erstellen

}

}

Im nächsten Abschnitt erfahren Sie genau, wie Sie editReminder() für die Tablet-App implementieren können.

Zuvor müssen Sie sich jedoch noch um eine etwas subtilere Zusammenarbeit der Fragmente kümmern. Wenn Sie OnClickListener für die Schaltfläche mConfirmButton in ReminderEditFragment betrachten, finden Sie dort diese Zeile:

getActivity().finish();

Das ist nicht gut. Wenn getActivity().finish() von der Telefon-App aus aufgerufen wird, führt dies Benutzer zurück zur List-Activity. Wird diese Methode von der Tablet-App aus aufgerufen, würde die App hingegen geschlossen. Aus der Tablet-App muss offenbar das Edit-Fragment entfernt werden, ohne gleich die gesamte Activity zu schließen.

Um dieses Problem zu beheben, führen Sie diese Schritte aus:

1. Erstellen Sie eine neue Schnittstelle namens OnFinishEditor und fügen Sie dort diesen Code ein:

package com.dummies.android.terminplaner;

public interface OnFinishEditor {

public void finishEditor();

}

2. Ändern Sie ReminderEditFragment so, dass diese Schnittstelle und nicht mehr direkt die Methode finish() aufgerufen wird.

Ersetzen Sie getActivity().finish() an den zwei Stellen, an denen es dort vorkommt, durch diese Zeile:

((OnFinishEditor) getActivity()).finishEditor();

3. Ändern Sie die beiden Activities von ReminderEditFragment, um diese Schnittstelle hinzuzufügen, und implementieren Sie die Methode finishEditor() in ReminderEditActivity.

Fügen Sie den folgenden Code zu ReminderEditActivity hinzu:

public class ReminderEditActivity extends

FragmentActivity implements OnFinishEditor {

...

// Am Ende einfügen

@Override

public void finishEditor() {

finish();

}

}

Das ist derselbe Code, der auch in OnClickListener und onLoadFinished() aufgerufen wird.

4. Fügen Sie den folgenden Code zu ReminderListAndEditorActivity hinzu:

public class ReminderListAndEditorActivity extends FragmentActivity

implements OnEditReminder, OnFinishEditor {

@Override

public void finishEditor() {

FragmentManager fragmentManager = getSupportFragmentManager();  →6

FragmentTransaction transaction =

fragmentManager.beginTransaction();  →7

Fragment previousFragment = fragmentManager.findFragmentByTag(

ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG);  →9

transaction.remove(previousFragment); →10

transaction.commit(); →11

}

}

Wie bereits zuvor verwenden Sie FragmentManager und FragmentTransaction, um das Hinzufügen und Entfernen von Fragmenten aus der Activity zu verwalten. Der Code macht Folgendes:

→6: Übernimmt den FragmentManager von der FragmentActivity.

→7: Startet FragmentTransaction durch einen Aufruf von beginTransaction().

→9: Fordert den FragmentManager auf, nach dem vorherigen Fragment namens DEFAULT_EDIT_FRAGMENT_TAG zu suchen, sofern ein solches überhaupt existiert. Dieser Name muss mit dem Namen übereinstimmen, den Sie verwendet haben, als Sie das Fragment anfangs in editReminder() hinzugefügt haben.

→10: Entfernt das Fragment. Wenn previousFragment den Wert null hat, macht diese Zeile gar nichts.

→11: Führt die Transaktion aus. Denken Sie daran, dass jeder Aufruf von beginTransaction() mit einem Aufruf von commit() beendet werden muss.

Fragment-Transaktionen hinzufügen

Im Grunde genommen sollte editReminder() nun ein Edit-Fragment für den vom Benutzer angetippten Termin anzeigen. Oder es sollte ein leeres Edit-Fragment anzeigen, wenn der Benutzer die Option HINZUFÜGEN in der Aktion auswählt, um einen neuen Termineintrag zu erstellen. Wenn ein Benutzer mehrere Termine nacheinander antippt, sollte editReminder() den angezeigten Termineintrag jeweils durch einen neuen ersetzen, der dem zuletzt angetippten Eintrag entspricht.

In Ihrem Layout reminder_list.xml haben Sie bereits mit XML-Anweisungen Fragmente in Activities eingebunden. Da die Fragmente dynamisch hinzugefügt und entfernt werden sollen, werden Sie diesmal aber Java anstelle von XML verwenden. Die erforderliche Vorgehensweise ist nicht allzu komplex und unterscheidet sich lediglich ein wenig von der mit XML.

Wenn Sie Fragmente in Java hinzufügen oder entfernen wollen, müssen Sie den FragmentManager benutzen, um eine FragmentTransaction einzuleiten. Dann führen Sie Ihre Änderungen durch und rufen, ähnlich wie bei der Interaktion mit einer Datenbanktransaktion, die Methode commit() der FragmentTransaction auf.

Fügen Sie in die Methode editReminder() in der Datei ReminderListAndEditorActivity den folgenden Code ein:

/**

* Edit−Fragment setzen und das bisherige Fragment ersetzen,

* wenn ein solches bereits existiert.

*/

@Override
public void editReminder(long id) {

ReminderEditFragment fragment = new ReminderEditFragment(); →7

Bundle arguments = new Bundle(); →8

arguments.putLong(ReminderProvider.COLUMN_ROWID, id); →9

fragment.setArguments(arguments); →10

FragmentTransaction transaction = getSupportFragmentManager()

.beginTransaction(); →13

transaction.replace(R.id.edit_container, fragment,

ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG); →15

transaction.addToBackStack(null); →16

transaction.commit(); →17

}

Der Code funktioniert wie folgt:

→7: Erstellt ein neues ReminderEditFragment-Element.

Konstruktoren von Fragmenten dürfen keine Argumente verwenden. Alle Argumente werden in einem Bundle untergebracht.

→8 bis  →10: Teilt dem Fragment mit, welcher Termin bearbeitet wird. Bei der ID 0 wird ein neues Fragment erzeugt.

→13: Übernimmt den FragmentManager durch einen Aufruf von FragmentActivity.getSupportFragmentManager() und ruft dann beginTransaction() auf, um eine neue Fragment-Transaktion einzuleiten.

→15: Ruft FragmentTransaction.replace() auf, um das vorhandene Fragment durch das neue zu ersetzen. Die edit_container-View bestimmt, wo das Fragment platziert werden soll, bei fragment handelt es sich um das zu verwendende Fragment und ReminderEditFragment.DEFAULT_EDIT_FRAGMENT_TAG gibt Aufschluss über den Fragmentnamen.

→16: Ruft addToBackStack() auf und übergibt den Wert null für den optionalen Statusnamen.

Diese Zeile erfordert ein paar Erklärungen. Stellen Sie sich vor, was immer dann passiert, wenn Sie eine neue Activity starten. Diese wird zum Activity-Stack hinzugefügt. Wenn Benutzer dann die Zurück-Schaltfläche antippen, kehren sie zur vorherigen Activity zurück. Dieses Standardverhalten erwarten Benutzer von fast allen Activities.

Das Standardverhalten von Fragmenten verkörpert jedoch das Gegenteil. Standardmäßig wandert ein Fragment nicht auf den Zurück-Stapel, wenn es zu einer Activity hinzugefügt wird. Wenn Benutzer also die Zurück-Schaltfläche antippen, wird nicht etwa das gerade hinzugefügte Fragment entfernt, sondern die Activity beendet. Das könnte aber unerwünscht sein. Wenn beim Antippen der Zurück-Schaltfläche das Fragment entfernt werden soll, müssen Sie die Methode addToBackStack() aufrufen.

Beim dynamischen Hinzufügen von Fragmenten sollten Sie sich daher überlegen, welches Verhalten Benutzer Ihrer Anwendung wohl beim Antippen der Zurück-Schaltfläche von Ihrer App erwarten. Wenn Benutzer wie hier einen Listeneintrag antippen, um ein Edit-Fragment anzuzeigen, werden sie wohl erwarten, dass beim Antippen der Zurück-Schaltfläche das Fragment mit dem Bearbeitungsbereich geschlossen wird.

→17: Jeder Aufruf von beginTransaction() muss von einem Aufruf von commit() begleitet werden. Dann erst erledigt Android die eigentliche Arbeit und fügt Fragmente zu Ihrer Activity hinzu oder entfernt sie.

Herzlichen Glückwunsch. Damit sollten Sie nun eine vollständig implementierte Version Ihrer Telefon-App auf Ihrem Tablet laufen haben. Die Quelltexte zum Buch enthalten auch eine Version der Quelltexte, die aus der amerikanischen Originalauflage stammt und noch um einige Details erweitert wurde, deren Darstellung im Buch wohl den verfügbaren Rahmen gesprengt hätte.

18

Jenseits von Google

In diesem Kapitel

Welche Funktionen mit dem Kindle zusammenarbeiten

Konfigurieren und Testen im Emulator

Apps in den Amazon-Store hochladen

Google mag ja für Android der größte Anbieter sein, ist aber nicht der einzige. Da Google jede neue Android-Version öffentlich über das Android Open Source Project bereitstellt, erstellen viele Unternehmen ihre eigenen angepassten Versionen des Android-Quellcodes.

Eine Version ist Ihnen vielleicht bekannt. Dabei handelt es sich um Amazon, das sich bei seinem Tablet, dem Kindle Fire, für Android entschieden hat.

Auf dem Android-basierten Kindle können Apps mit geringfügigen oder gar keinen Änderungen ausgeführt werden. Das Gerät kann jedoch nicht auf den Google Play Store zugreifen, weshalb Sie Ihre Apps im Amazon Appstore für Android anbieten müssen, wenn Benutzer des Kindle Fire diese herunterladen können sollen. In diesem Kapitel werden Sie erfahren, wie Sie Apps auf das Kindle Fire portieren und über Amazon veröffentlichen können.

Ein Grund für die Portierung auf das Kindle Fire könnte sein, dass Sie so potenziell mehr Benutzer erreichen können. Aber nur Sie selbst können entscheiden, ob sich der erforderliche Mehraufwand lohnt. Machen Sie Ihre Hausaufgaben und lesen Sie die relevanten Statistiken zu den Nutzerzahlen neuer Plattformen, bevor Sie entsprechende Anstrengungen unternehmen.

Google-Funktionen umgehen

Da es sich beim Kindle Fire um kein »echtes« Android-Gerät handelt (es nutzt nicht den offiziellen Android-Quellcode von Google, sondern seine eigene Version), bietet es keinen Zugang zu Google-Diensten, deren Quellcode nicht veröffentlicht wird, die Sie aber vielleicht bereits nutzen. Zudem verfügt das Gerät selbst vielleicht nicht über bestimmte Funktionen, an die Sie sich bereits gewöhnt haben:

Google Maps: Wenn Sie die Bibliothek von Google Maps nutzen, um Karten in Ihre Android-Apps einzubinden, können Sie diese auf dem Kindle Fire nicht nutzen.

Standortdienste: Auf dem Kindle Fire können Sie neben Maps auch die Standortdienste nicht nutzen. Das Gerät kann weder auf GPS noch auf WLAN-basierte Standortdienste zurückgreifen, weshalb es keine Möglichkeit zur Ermittlung des aktuellen Gerätestandorts gibt.

App-interne Einkäufe über den Google Play Store: Wenn Benutzer aus Ihrer App heraus Programmerweiterungen oder andere Apps erwerben können, lässt sich diese API nicht auf dem Kindle Fire nutzen. Amazon stellt für derartige Zwecke aber seine eigene Variante bereit, die Sie auf dem Kindle Fire nutzen können.

Kamera, Mikrofon, Bluetooth, 3G und externer Speicher: Das Kindle Fire stellt keine dieser Ausstattungsoptionen bereit. Wenn Ihre Apps auf diese zurückgreifen, müssen Sie diese Beschränkungen entweder irgendwie umgehen oder Ihre App nicht für das Kindle Fire veröffentlichen.

Honeycomb, Ice Cream Sandwich und Jelly Bean: Amazon verwendet eine Version des Android-Quellcodes aus der Zeit vor der Veröffentlichung von Honeycomb. Das Kindle Fire kann daher auch keine der Funktionen dieser neueren Versionen nutzen. Insbesondere wird Ihnen auffallen, dass das Kindle Fire einem einzigartigen Bedienkonzept folgt, das sich von anderen Android-Tablets unterscheidet. Ein wenig über die Funktionen dieser Android-Versionen erfahren Sie in Kapitel 1.

Aber selbst ohne diese Funktionen und Dienste funktionieren viele Android-Apps trotzdem noch ohne Änderungen auf dem Kindle Fire. Wenn das auch auf Ihre App zutrifft, sollten Sie weiterlesen.

Ihr Kindle Fire oder einen passenden Emulator einrichten

Wenn Sie für das Kindle Fire entwickeln wollen, benötigen Sie entweder ein echtes Kindle Fire oder einen geeignet eingerichteten Emulator als Ersatz. Da das Kindle Fire eine andere Android-Variante verwendet, können Sie nicht dasselbe ADB wie für andere Android-Geräte verwenden, ohne ein paar Konfigurationsänderungen vorzunehmen.

Einen Kindle-ähnlichen Emulator erstellen

Wenn Sie auf kein echtes Kindle Fire zurückgreifen können, müssen Sie einen geeigneten Emulator erstellen. Die Vorgehensweise unterscheidet sich ein klein wenig von der üblichen, weil das Kindle Fire ein eigenes Android benutzt. Führen Sie diese Schritte aus:

1. Installieren Sie über den SDK-Manager das SDK für Android 2.3.3 (API Level 10).

Mehr zum SDK-Manager erfahren Sie in Kapitel 2.

2. Wählen Sie im SDK-Manager TOOLS:MANAGE ADD-ON SITES.

Daraufhin wird das in Abbildung 18.1 dargestellte Dialogfeld angezeigt.

3. Klicken Sie auf der Registerkarte USER DEFINED SITES die Schaltfläche NEW an.

4. Geben Sie die URL http://kindle-sdk.s3.amazonaws.com/addon.xml ein und klicken Sie erst OK und dann CLOSE an.

Nun wird das in Abbildung 18.2 dargestellte Dialogfeld ANDROID SDK MANAGER angezeigt.

Abbildung 18.1: Ein SDK hinzufügen

Abbildung 18.2: Installieren des Emulators für das Kindle Fire

5.Rollen Sie nach unten zu ANDROID 2.3.3 (API 10), wählen Sie dort KINDLE FIRE und klicken Sie dann die Schaltfläche INSTALL PACKAGES an.

6.Akzeptieren Sie die Lizenzbedingungen und klicken Sie dann INSTALL an.

7.Starten Sie den AVD-Manager und klicken Sie die Schaltfläche NEW an.

Das in Abbildung 18.3 dargestellte Dialogfeld CREATE NEW ANDROID VIRTUAL DEVICE wird angezeigt. Wenn hier unter Target die Option KINDLE FIRE (AMAZON) – API LEVEL 10 nicht angezeigt wird, starten Sie Eclipse neu und probieren es erneut.

Abbildung 18.3: Eingabe eines Namens für den Emulator

8.Wählen Sie unter TARGET die Option KINDLE FIRE (AMAZON) – API LEVEL 10.

9.Tragen Sie den Namen für Ihren Emulator ein und klicken Sie die Schaltfläche CREATE AVD oder OK an.

10. Wählen Sie die Option 2_3_3_KINDLE_FIRE AVD aus, die Sie gerade erstellt haben, klicken Sie START und dann die Schaltfläche LAUNCH an, um den neuen Emulator auszuführen.

Der Emulator für das Kindle Fire wird nun in einem Fenster ausgeführt.

Nun können Sie Ihre Android-App von Eclipse aus in Ihrem Emulator für das Kindle Fire ausführen.

ADB konfigurieren (Mac)

Wenn Sie ein echtes Kindle Fire und keinen Emulator benutzen, können Sie das Gerät nicht direkt anschließen und ADB verwenden. Erst einmal müssen Sie einige Änderungen vornehmen. Führen Sie dazu diese Schritte aus:

1. Auf Ihrem Mac müssen Sie die Datei adb_usb.ini im Ordner .android Ihres Stammverzeichnisses bearbeiten.

Fügen Sie die folgenden Zeilen an das Ende der Datei an:

0x1949

0x0006

2. Schließen Sie Ihr Kindle Fire an und starten Sie ADB neu:

adb kill−server
adb devices

Nun wird Ihr Kindle Fire in der Ausgabe des Befehls adb devices angezeigt.

ADB konfigurieren (Windows)

Wie beim Mac können Sie ein Kindle Fire auch unter Windows nicht einfach nur anschließen und ADB benutzen, ohne erst einige Änderungen vorzunehmen.

1. Bearbeiten Sie auf Ihrem Windows-Rechner die Datei adb_usb.ini im Ordner .android, der sich in Ihrem privaten Stammverzeichnis befindet.

Fügen Sie die folgenden Zeilen an das Ende der Datei an:

0x1949

2. Bearbeiten Sie die Datei android_winusb.inf.

Fügen Sie die folgenden Zeilen jeweils in die beiden Abschnitte Google.NTx86 und Google.NTamd64 der Datei ein:

;Kindle Fire

%SingleAdbInterface% = USB_Install, USB\VID_1949&PID_0006

%CompositeAdbInterface% = USB_Install, USB\VID_1949&PID_0006&MI_01

3. Schließen Sie Ihr Kindle Fire an und führen Sie eine manuelle Installation des Treibers durch.

4. Aktivieren Sie dazu den Ordner mit der Datei android_winusb.inf, den Sie gerade bearbeitet haben, und installieren Sie sie.

5. Starten Sie ADB durch Aufruf der folgenden Befehle in einem Fenster der Eingabeaufforderung:

adb kill−server
adb devices

Nun wird Ihr Kindle Fire in der Ausgabe des Befehls adb devices angezeigt.

Viele Geräte erfordern ähnlich wie das Kindle Fire eine spezielle Vorgehensweise, um für die Android-Entwicklung genutzt zu werden. Wenn Sie über eine Suchmaschine eine Abfrage nach dem Muster »ADB Android Treiber« starten und die Modellbezeichnung des zu verwendenden Geräts hinzufügen, sollten Sie fündig werden. Bei Bedarf verwenden Sie beim zweiten Versuch dann im Suchbegriff »ADB Android Driver«.

Android-Apps bei Amazon veröffentlichen

Die Veröffentlichung von Android-Apps im Amazon-Appstore ähnelt naturgemäß der im Google Play Store. Sie erstellen also ein Konto und müssen möglicherweise eine Gebühr entrichten, um sich als Entwickler registrieren zu lassen.

Anders als im Google Play Store werden die Android-Apps für den Amazon Appstore erst geprüft. Daher sollten Sie damit rechnen, dass zwischen der Übertragung Ihrer App und deren Verfügbarkeit bei Amazon einige Tage ins Land gehen.

Nachdem mittlerweile das Kindle Fire auch in Deutschland erhältlich ist, ist Amazon 2012 unter dem Namen Amazon App-Shop für Android an den Start gegangen. Unter http://www.amazon.de/gp/help/customer/display.html?nodeId=200917710 finden Sie dort auch die Information, dass Sie zum Hochladen von Apps (zumindest momentan noch) den Weg über die amerikanische Seite gehen müssen.

Daher müssen Sie zum Hochladen von Android-Apps die folgenden Schritte ausführen:

1. Suchen Sie https://developer.amazon.com/welcome.html https://developer.amazon.com/welcome.html.

2. Melden Sie sich über Ihr Konto bei Amazon an oder erstellen Sie ein neues Konto.

3. Klicken Sie die Schaltfläche ACCEPT AND CONTINUE an, um die Lizenzvereinbarung für Entwickler zu bestätigen.

4. Tragen Sie die erforderlichen Kontodaten ein, wenn Sie Ihre Apps verkaufen und nicht kostenlos anbieten wollen (Abbildung 18.4). Klicken Sie dann SAVE an.

5. Klicken Sie die Schaltfläche ADD A NEW APP an.

6. Tragen Sie die Angaben zu Ihrer App und Ihre Kontaktdaten auf der Registerkarte GENERAL INFORMATION ein. Klicken Sie zum Abschluss SAVE an.

Füllen Sie alle Felder aus, von denen Sie sich irgendwelche Vorteile versprechen (Abbildung 18.5).

Abbildung 18.4: Geben Sie an, ob Sie Ihre Apps verkaufen wollen.

Abbildung 18.5: Hier tragen Sie Einzelheiten zu Ihrer App ein.

7. Legen Sie fest, in welchen Ländern Ihre App angeboten werden und was sie kosten soll.

8. Wählen Sie eine Kategorie für Ihre App, geben Sie die verwendete Sprache an und tragen Sie eine kurze und eine lange Beschreibung ein. Klicken Sie dann SAVE an (Abbildung 18.6).

Abbildung 18.6: Wählen Sie eine Kategorie und eine Sprache für Ihre App aus.

9.Laden Sie ein kleines und ein großes Symbol, Screenshots und eine Abbildung zu Werbezwecken neben der App-Beschreibung hoch. Klicken Sie dann SAVE an.

10. Nehmen Sie die Angaben für Inhaltsbewertung und Altersbeschränkung über die entsprechenden Optionsfelder vor. Klicken Sie dann SAVE an.

11. Laden Sie den Binärcode für Ihre App hoch. Klicken Sie dann SAVE an.

In Kapitel 8 erfahren Sie mehr zum Erstellen und Hochladen von APK-Dateien Ihrer App.

12. Klicken Sie die Schaltfläche SUBMIT APP an.

Die Prüfung Ihrer App dauert manchmal Stunden, manchmal Tage und zuweilen auch Wochen. Wenn Ihre App dann jedoch in den Appstore oder den deutschsprachigen App-Shop aufgenommen wurde, finden Sie sie dort neben anderen Apps.

Teil V

Der Top-Ten-Teil

In diesem Teil …

Teil V enthält einige der besten und größten Android-Schätze, auf die man normalerweise erst dann stößt, wenn man bereits längere Zeit Android-Apps entwickelt hat. Zunächst stelle ich in Kapitel 19 einige der besten Beispielanwendungen vor, die Ihnen als Sprungbrett auf Ihrem Weg beim Erstellen der nächsten App mit durchschlagendem Erfolg dienen können. Darunter befinden sich datenbankorientierte Apps, interaktive Spiele und Apps, die mit Web-APIs (Application Programming Interfaces – Anwendungsprogrammierschnittstellen) zusammenarbeiten.

Teil V schließt dann mit einer Liste von Profiwerkzeugen und Bibliotheken, mit denen Sie Ihre Apps optimieren, den Prozess Ihrer Anwendungsentwicklung produktiver gestalten und sich Ihr Leben als Entwickler erheblich erleichtern können.

19

Zehn tolle kostenlose Beispielanwendungen und SDKs (mit Code!)

In diesem Kapitel

Soziales Netzwerken

Bilder bearbeiten

Spielen

Musik streamen

Demoprogramme ausprobieren

Als Android-Entwickler werden Sie möglicherweise gelegentlich mit Ihrem Quellcode nicht mehr so recht vorankommen. Vielleicht müssen Sie Daten mit dem API eines Dritten austauschen, das die Daten im JSON-Format übergibt. Vielleicht geht es aber auch um die Kollisionserkennung bei Objekten in einem Spiel. In derartigen Fällen können Sie üblicherweise das Web nach Beispielcode durchsuchen, denn wahrscheinlich hat bereits jemand etwas Entsprechendes programmiert. Dann müssen Sie den Code nur noch sichten, bei Bedarf anpassen und mit der Entwicklung Ihrer App fortfahren.

Beispielcode ist meist nicht perfekt. Das stimmt zwar, aber durch die Sichtung von Beispielen erfahren Sie ganz nebenbei, wie Android-Programme aussehen müssen. Natürlich liegen dem Android-SDK etliche Beispiele bei, wie die in Kapitel 2 erwähnten API-Demos. Aber die wirklich tollen Beispiele finden Sie unter der Vielzahl der kostenlos im Web erhältlichen Quelltexte echter Apps! Im Internet sind dank der Quelloffenheit von Android qualitativ gute Quellcodes verfügbar, die Ihnen als Vorbild dienen können.

Bei den meisten der folgenden hervorragenden Quellcodebeispiele handelt es sich um echte Android-Apps, die Sie über Google Play installieren können. Probieren Sie die Apps erst auf Ihrem Gerät aus und wenden Sie sich dann dem Quellcode zu, um zu sehen, wie die verschiedenen Rädchen bei den jeweiligen Apps ineinandergreifen.

Google I/O 2012

Alljährlich treffen sich Scharen von Google-Entwicklern im Moscone Center in San Francisco zu einer mehrtägigen Konferenz, um dort alle Themen rund um Google zu diskutieren. Die offizielle Konferenz-App wurde für Android-Geräte geschrieben und dient als Beispiel für die Programmierung einer guten Anwendung für diese Plattform. Mehr darüber erfahren Sie unter http://code.google.com/p/iosched.

LOLCat Builder

Hierbei handelt es sich um ein hervorragendes Beispiel, wenn Sie sich für Bildbearbeitung unter Android interessieren. Sie erfahren, wie Sie Bilder mit der Gerätekamera aufnehmen, sie mit Titeln versehen und die daraus resultierende Datei auf SD-Speicherkarte ablegen können. Sie finden zudem heraus, wie Sie Zielsetzungen wie den Versand von Bildern als MMS (Multimedia-Mitteilungen) oder als E-Mail-Anhang umsetzen können. Den Quellcode finden Sie unter http://code.google.com/p/apps−for−android.

Amazed

Das Spiel Amazed kann Ihnen zeigen, wie Sie eine zweidimensionale Glaskugel über den geräteinternen Beschleunigungssensor durch mit Hindernissen gespickte Labyrinthe steigenden Schwierigkeitsgrads manövrieren können. Wenn Sie sich für Anwendungen interessieren, die den Beschleunigungssensor nutzen, könnte Ihnen der Quellcode dieser App enorm hilfreich sein. Darüber hinaus verdeutlicht die App auch andere Grundkonzepte der Spielentwicklung, wie zum Beispiel Kollisionserkennung und den Schleifenaufbau von Spielen. Den Quellcode finden Sie unter http://code.google.com/p/apps−for−android.

API-Demos

Das Android-SDK versorgt Sie mit verschiedenen Beispielanwendungen wie API-Demos. Diese App verdeutlicht den Einsatz der verschiedenen Android-APIs über kleine, leicht verdauliche, funktionierende Beispiele. Sie finden im Quellcode der API-Demos im Ordner samples Unmengen einfacher Beispiele, die direkt auf den Punkt kommen. Da es unter den API-Demos entsprechende Beispiele gibt, können Sie in Ihren Projekten leicht Animationen nutzen oder Audiodateien wiedergeben. Wenn Sie zwar eine Menge Ideen, aber nur wenig Zeit haben, sollten Sie die zahlreichen gelungenen Beispiele definitiv einmal auf Ihrem Gerät installieren und ein wenig genauer ansehen.

HoneycombGallery

Wenn Ihre App auf Mobiltelefonen und Tablets reibungslos laufen soll, können Sie sich einmal das Beispiel HoneycombGallery aus dem Android-SDK reinziehen, denn es kann Ihnen viele Stunden der Fehlersuche und Positionierung von Elementen der Benutzeroberfläche ersparen helfen. Die funktionsfähige Beispielanwendung zeigt, wie Sie mehrere Bildschirmgrößen und Auflösungen unterstützen und Fragmente auf unterschiedlich dimensionierten Geräten nutzen können. Sie finden die entsprechenden Quelltexte im Ordner samples des Android-SDKs.

K-9 Mail

Bei K-9 Mail handelt es sich um einen beliebten E-Mail-Client für Android, der zusammen mit Android ausgeliefert wurde, bevor er zu einer eigenständigen App wurde. K-9 Mail ist eine außerordentlich umfassende Open-Source-Anwendung mit Funktionen wie Suchen, Versenden, Synchronisieren, Kennzeichnen und Signaturen. Den Quelltext finden Sie unter http://code.google.com/p/k9mail/.

Agit
https://github.com/rtyley/agit

Bei Git handelt es sich um ein beliebtes quelloffenes verteiltes Versionskontrollsystem (DVCS – Distributed Version Control System). Mit Agit können Sie auch unterwegs bequem all Ihre bevorzugten Git-Repositories auf GitHub.com im Auge behalten. Die App demonstriert neben der Nutzung des GitHub-APIs auch die des RoboGuice-Frameworks.

Facebook-SDK für Android

Sie sind ehrgeizig? Wie wäre es dann mit der Entwicklung der nächsten beliebten Facebook-App? Sie wissen nicht, wo Sie anfangen sollen? Mit dem Android-SDK für Facebook können Sie leicht Facebook-Funktionen in Ihre Apps integrieren. Sie können mit diesem API Benutzer autorisieren, API-Abfragen absetzen und viel mehr! Sie können alle Facebook-Leckerbissen ohne allzu viel Aufwand nutzen. Den Quellcode finden Sie unter http://github.com/facebook/facebook−android−sdk.

Replica Island

Vielleicht wollen Sie ein 2D-Spiel für die Android-Plattform erstellen, bei dem der Hintergrund seitlich über den Bildschirm verschoben wird, wissen aber nicht, wie Sie das Problem angehen sollen. Sie sind ein Glückspilz, denn bei Replica Island handelt es sich um genau ein solches Spiel, in dem kein anderer als der bei Entwicklern beliebte kleine grüne Android (Bugdroid) über den Bildschirm flitzt. Das kostenlose Spiel ist nicht nur auf dem Android-Marktplatz beliebt, sondern auch komplett quelloffen und eine hervorragende Lernhilfe für Spielentwickler! Den Quellcode finden Sie unter http://code.google.com/p/repli caisland.

Notepad Tutorial

Wenn Sie sich für die Grundlagen von SQLite ohne den Wust dieser ganzen anderen Dienste, Hintergrundaufgaben und so weiter interessieren, dann ist dies die geeignete App für Sie. Sie lässt sich zwar einfach starten und nutzen, mit dem Quellcode und dem damit einhergehenden Tutorial lassen sich aber die Grundlagen von SQLite leichter verstehen. Den Quellcode und das Tutorial finden Sie unter http://d.android.com/guide/tutorials/notepad/index.html.

20

Zehn Tools zur Erleichterung Ihres Entwicklerlebens

In diesem Kapitel

Code aus Bibliotheken nutzen

Programmlayouts schnell erstellen

Bilder bearbeiten

Den eigenen Code testen und bereinigen

Umgang mit Versionskontrollsystemen

Es liegt in der Natur der Sache, dass Sie als Entwickler Werkzeuge entwickeln werden, mit denen Sie selbst produktiver werden, wie beispielsweise Hilfsmethoden zur Unterstützung der asynchronen Kommunikation, zum Parsen von XML und JSON, für Datum und Uhrzeit. Bevor Sie aber haufenweise Hilfsklassen oder Frameworks für die Nutzung von Elementen schreiben, sollten Sie sich besser erst einmal im Internet nach bereits existierenden Tools umsehen. In diesem Kapitel habe ich zehn Tools und Utilities zusammengestellt, die Ihnen Ihr Entwicklerleben erleichtern, Ihre Produktivität steigern und dafür sorgen können, dass Ihre Apps voll auf der Höhe sind.

Droid-Fu und ignition

Droid-Fu ist eine quelloffene Bibliothek mit einer Handvoll Methoden, die Ihre Entwicklungszeiten drastisch reduzieren können. Droid-Fu besteht aus Hilfsklassen, die Ihnen all die Schwerarbeit abnehmen und sich der asynchronen Hintergrundanforderungen annehmen, Bilder aus dem Web übernehmen und insbesondere den Lebenszyklus der Applikation verlängern. Sie müssen sich nie mehr um Zustandsänderungen kümmern, da Droid-Fu nicht nur diesen Job übernimmt. Warten Sie nicht länger und nutzen Sie Droid-Fu! Den Quellcode finden Sie unter http://github.com/kaeppler/droid−fu.

Beobachten Sie auch ignition, das eine Alternative zu Droid-fu ist und von denselben Autoren stammt, sich aber momentan noch im Alpha-Stadium befindet. ignition finden Sie unter https://github.com/kaeppler/ignition.

RoboGuice

Auch wenn es sich irgendwie nach einem Energiedrink anhört, handelt es sich bei RoboGuice doch um ein Framework, dass die Guice-Bibliothek von Google dazu benutzt, um Dependency Injection zum Kinderspiel werden zu lassen. Dependency Injection übernimmt die rechtzeitige Initialisierung Ihrer Variablen, so dass Sie sich nicht selbst darum kümmern müssen. Sie verringert die Menge des Codes, den Sie letztlich selbst schreiben müssen, und erleichtert deutlich die spätere Wartung Ihrer App. Den Quellcode finden Sie unter http://code.google.com/p/roboguice.

Translator Toolkit

Wenn Sie die Anzahl derer steigern wollen, die Ihre App nutzen können, gibt es kaum eine bessere Möglichkeit, als sie in andere Sprachen zu übersetzen. Dazu können Sie mit Google nach Helfern suchen, die Ihnen diese Aufgabe erleichtern. Die Übersetzungen sind zwar weniger gut, als wenn ein Muttersprachler diese Aufgabe erledigt hätte, eignen sich aber zumindest für preiswerte Ausgangslösungen recht gut. Vielleicht nehmen Sie ja die ersten Übersetzungen mit Googles Translator Toolkit vor und suchen dann innerhalb Ihrer Nutzergemeinde nach Freiwilligen, die diese dann für Sie überarbeiten. Alternativ können Sie natürlich auch über einschlägige Webseiten nach externen Mitarbeitern für die Übersetzung suchen. Das Translator Toolkit von Google finden Sie unter http://translate.google.com/toolkit.

Draw 9-patch

Draw 9-patch ist ein Hilfsprogramm, mit dem Sie leicht skalierbare Bilder für Android erstellen können. Sie können dieses Dienstprogramm dazu verwenden, um Anweisungen in Ihre Bilder einzubetten, die dem Betriebssystem mitteilen, wie es Ihre Abbildungen skalieren soll, damit sie unabhängig von der Größe und Auflösung des Gerätebildschirms klar und deutlich angezeigt werden. Näheres zu Draw 9-patch erfahren Sie über die englischsprachige Website http://developer.android.com/tools/help/draw9patch.html.

Hierarchy Viewer

Wenn Sie beim Erstellen einer Benutzeroberfläche in Ihrer Layoutdatei mit verschiedenen Ansichten arbeiten, dann ist dies oft nicht ganz einfach. Mit dem Hierarchy Viewer, der sich im Ordner tools des Android-SDKs befindet, können Sie genau sehen, wie Ihre Steuerelemente grafisch auf dem Bildschirm dargestellt werden. Sie können deutlich die Ränder der einzelnen Steuerelemente erkennen und wissen so, was genau sich auf dem Bild-schirm abspielt. Hierarchy Viewer ist das ultimative Werkzeug zum Erstellen pixelgenauer Benutzeroberflächen, wobei Sie die Bildschirmdarstellung vergrößern und prüfen können, ob Ihre Bilder und Benutzeroberflächen bei allen Bildschirmgrößen und Auflösungen fehlerfrei angezeigt werden. Mehr über dieses Programm erfahren Sie unter http://developer.android.com/tools/help/hierarchy-viewer.html.

UI/Application Exerciser Monkey

UI/Application Exerciser Monkey können Sie benutzen, um Ihre App einem Intensivtest zu unterziehen. Das Programm wird dabei seinem Namen gerecht, verhält sich affenartig und simuliert zufällige Berührungen, Klicks und andere Anwenderereignisse und versucht, Ihre App unter abnormen Umständen zum Absturz zu bringen. Mit einer Art Affen können Sie Ihre Apps entweder im Emulator oder auf Ihrem eigenen Gerät testen. Weitere Informationen zu diesem Programm finden Sie unter http://developer.android.com/tools/help/monkey.html.

zipalign
zipalign richtet alle unkomprimierten Daten in einem APK aus. Durch die Nutzung von zipalign können Sie den Laufzeit-Speicherbedarf Ihrer App minimieren. Wie in Kapitel 8 demonstriert, wird Ihre App beim Export einer signierten Anwendung immer ausgerichtet, wenn Sie das ADT in Eclipse verwenden. Weitere Informationen zu diesem Programm finden Sie unter http://developer.android.com/tools/help/zipalign.html.

layoutopt
layoutopt ist ein Befehlszeilenwerkzeug, das Ihre Layouts analysiert und auf möglicherweise problematische oder ineffiziente Stellen hinweist. Da es Bremsen und andere mögliche Probleme in Ihrer App erkennen kann, können Sie mit diesem hervorragenden Werkzeug all Ihre Layouts und Ressourcenverzeichnisse prüfen. Sie finden es unter http://developer.android.com/tools/help/layoutopt.html.

Git

Git ist ein äußerst schnelles, kostenloses und quelloffen vertriebenes Versionskontrollsystem. Git sorgt für die effiziente Verwaltung Ihrer Repositories und schnelle und problemlose Sicherungen Ihrer Projekte. Sorgen Sie dafür, dass Ihre nächste spektakuläre App auch vor Systemabstürzen sicher ist! Mit Git können Sie effizient mehrere Projektversionen verwalten und es lässt sich leicht in Ihre Arbeitsabläufe integrieren. Mit den angebotenen Plugins lassen sich Ihre Git-Repositories auch aus der Eclipse-IDE heraus leicht verwalten. Sie können Git eigenständig nutzen, Ihre Git-Repositories aber auch andernorts sicher speichern. Kostenlose private Git-Repositories können Sie unter http://projectlocker.com oder http://unfuddle.com ablegen. Für quelloffenen Quellcode ist dies auch bei Github.com möglich. Mehr über Git erfahren Sie unter http://git−scm.com.

Paint.NET und GIMP

Irgendwann werden Sie im Rahmen Ihrer Android-Entwicklungen mit Bildern arbeiten müssen. Profis nutzen zur Bildbearbeitung meist Adobe Photoshop, es gibt aber auch Alternativen für den kleinen Geldbeutel. Zwei völlig kostenlose Optionen sind Paint.NET und GIMP.

Paint.NET ist ein kostenloses Bildbearbeitungsprogramm, das das .NET-Framework nutzt. Es wird weltweit von vielen Entwicklern benutzt. Sie finden dieses Windows-Programm unter www.getpaint.net.

Das quelloffene Programm GIMP ähnelt Photoshop. GIMP gibt es in Versionen für Windows, Linux und den Mac. Sie finden es direkt in den allermeisten Linux-Distributionen und unter www.gimp.org.

Stichwortverzeichnis

A

Abmessung

Accelerometer
acquire() 1, 2,

Action bar

ACTION_EDIT

ACTION_MAIN

ACTION_VIEW

ActionBarSherlock 1, 2,

Activity 1, 2,

Activity (Klasse)

Activity Manager

Activity.setContentView()

activity_main.xml 1, 2, 3,
adapter

AdapterContextMenuInfo

ADB (Android Debug Bridge)

ADB (Android Debugging Bridge)

konfigurieren (Mac)

konfigurieren (Windows)

adb_usb.ini

Add Repository (Dialogfeld)

add()

addPreferencesFromResource()

addToBackStack()

AdMob

Adobe Photoshop

Adresse

AdSense

ADT (Android Development Tools) 1, 2,

ADT-Export-Assistent

Agit

Aktionsleiste 1, 2, 3, 4,

Aktivität

Zustand

Aktivitätsgruppe

Aktivitätsstapel

Alarm-Ereignis

ALARM_SERVICE

Alarmdienst

AlarmManager 1, 2, 3,

AlarmManager (Klasse)

Alert-Dialogfeld
erstellen

AlertDialog

AlertDialog.Builder 1, 2,

Amazed

Amazon Appstore

Android AVD Manager

Android Debug Monitor

Android Device Chooser

Android Open Source Project

Android Support Library 1, 2,

Android Virtual Device Manager

Android-ADT

Android-App
bei Amazon veröffentlichen

ANDROID-Bildschirm

Android-NDK

Android-Paketdatei

Android-Projekt

Android-SDK 1, 2,

Ordnerstruktur
android.jar
android.R (Klasse)

android.support.v4.app.* 1, 2, 3, 4,
android.util.Log
android.view
android.widget 1, 2,

Kompatibilität

Logo

Marktanteil

Paket

Plattform

Quellcode

Wurzeln
android_winusb.inf

AndroidManifest.xml 1, 2, 3, 4, 5, 6, 7, 8, 9,

ANR (Application Not Responding)

ANR-Dialogfeld

ANR-Fehler

Ant
veröffentlichen

Anwendungsfokus

Anwendungsframework

Anwendungsname

Anwendungsstarter

Apache Ant

API-Demos

Beispiele

Demos

APK (Android Package)

APK-Datei
erstellen

App-Widget

App-Widget-Framework
zum Startbildschirm hinzufügen
veröffentlichen

Apple iPhone

Application Launcher

Application Name

Application Not Responding

AppWidget.java

AppWidgetProvider 1, 2, 3, 4,

Arbeitsbereich
arrays.xml

Asset

Asset Manager
assets (Ordner) 1, 2,

AsyncTask (Klasse) 1, 2,

AUDIO_SERVICE

Audiodienst

AudioManager

Aufgabenplanung 1, 2,

Auflösung

Ausnahmebehandlung

Ausnahmefehler 1, 2,

Auswahlmenü 1, 2,

Autom. vervollständigen

AVD (Android Virtual Device) 1, 2,
background (Eigenschaft)

B

Backup-Manager

Barnes & Noble

Basisklasse

Bedienoberfläche

Bedienschnittstelle

Beginnauszeichner
beginTransaction()

Begrüßungsbildschirm

Beispielcode

Benachrichtigung
aktualisieren
entfernen
erstellen

Benachrichtigungsbereich

Benachrichtigungsleiste 1, 2,

Benachrichtigungssymbol

Benachrichtigungsverwaltung

Benutzereinstellung
speichern

Benutzereinstellungen
ermitteln

Benutzereinstellungen
programmgesteuert setzen

Benutzerkontensteuerung

Benutzeroberfläche

Berechtigung 1, 2, 3,

Berührungsereignis

Beschleunigungssensor 1, 2,

Betreffzeile

Bewertungssystem

Bibliothek

Bild
anzeigen 1, 2,

Bildbearbeitungsprogramm

Bildschirmdichte

Bildschirmfoto 1, 2,

Bildschirmtastatur
anpassen

Bildschirmtastatur
japanische Optionen

Blackberry

Blogger-Konto

Bluetooth

Android-Version

Bool

Breakpoint

Broadcast-Ereignis

Broadcast-Intent

BroadcastReceiver 1, 2, 3,

Bugdroid
buildUpdate()

Button (Datentyp)

Button (Widget)

C

Cache

Calendar.getTimeInMillis()

Callback-Funktion

Callback-Methode
catch

CheckBoxPreference
checkIfPhoneIsSilent() 1, 2,

Choose Packages

(Dialogfeld)

Chooser 1, 2, 3,
clickable (Eigenschaft)

Client-Server-Computing

Cloud-Computing

Codevervollständigung
colors.xml 1, 2,
commit()

CONTENT_URI

ContentProvider 1, 2, 3,

ContentResolver

ContentUris.parseId()

ContentView

Context
cron 1, 2,

CRUD

CSS (Cascading Style Sheet) 1, 2,

Cursor 1, 2,

CursorLoader

D

Dalvik Virtual Machine

Danger

Darstellbare Ressource

DatabaseHelper 1, 2,

DATE_FORMAT

Datensatz aktualisieren

Datensatz einfügen

Löschkriterien

Werte speichern

Datenbankabfrage

Datenbankaufbau
ändern

Datenbanktabelle
erstellen

Datenbankzeiger

Datentyp
umwandeln

DatePicker

DatePickerDialog

DatePickerDialogFragment

Datum
ändern
auswählen

DDMS (Dalvik Debug Monitor Server) 1, 2,

DDMS-Meldung
betrachten
ddms.bat

Debug

Debug-Konfiguration

Debug-Perspektive

Debug-Schlüssel

Debuggable

Debugger 1, 2,
delete() 1, 2,

Design

Dialogfeld
auswählen

DialogFragment (Klasse)

Dienst

DigitsKeyListener
dimens.xml

Dimension

Donut
doReminderWork() 1, 2,
dp (density-independent pixels)

dpi (dots per inch)

Draw 9-patch

Drawable 1, 2, 3,

Droid-Fu

DVM (Dalvik Virtual Machine)

E

Eclipse 1, 2,

Eclipse-IDE

Installation

Konfiguration
edit_container
edit_reminder_title
editReminder() 1, 2, 3, 4,

EditText

EditText (Widget)

EditTextPreference

Eigenschaftenbereich

Eingabeprüfung

Einschlafsperre 1, 2,

Einstellungen für Sprache und Tastatur

Einstellungsbildschirm

Empfänger

Emulator 1, 2, 3,
einrichten

Kindle-ähnlicher

Endauszeichner

Entwicklungsumgebung

Environment (Objekt)

Ereignisbehandlungsroutine

Ereignisüberwachungsfunktion

Ergebnismenge

Event-Listener
execSQL()

Exerciser Monkey

Export Android Application

EXTRA_SUBJECT

EXTRA_TEXT

Extract (Android) String

F

Facebook-SDK

Farbdatei

Farbe

FC )Force Close)

Feature Detection

Fehlersuchmodus

Feldnamen
speichern
fill_parent 1, 2, 3,
findPreference()

findViewById() 1, 2, 3,
finish()

finishEditor()

FLAG_ONE_SHOT

Flickr

Flugmodus

Force Close 1, 2,

Format

Formatvorlage

Fragment 1, 2, 3,

Fragment-Transaktion
hinzufügen

Fragment.onCreateView()

Fragment.setArguments()

FragmentActivity 1, 2,

FragmentTransaction

FrameLayout

Froyo

Fürchterlicher Fehler

G

G1
gen (Ordner) 1, 2, 3,

Geolokation

Geräteemulator

Geräteneustart

Geste
angepasste

Geste

Multitouch
getActivity()

getArguments()

getCacheDir()

getContentResolver() 1, 2,
getDefaultSharedPreferences()

getEditText()

getExternalFilesDir()

getExternalStorageState()

getItemId()

getListView()

getLoaderManager()

getLock()

getLongExtra()

getMenuInfo()

getSharedPreferences()

getString()

getSystemService() 1, 2, 3,
getTimeInMillis()

getWritableDatabase()

GIMP 1, 2,

Git 1, 2,
github

Globalisierung 1, 2,

Gmail-Konto

Google

Google AdSense

Google Maps

APIs

Google Play

Google Play Store 1, 2,

Google TV

Google-API

Google-Konto

Google.NTamd64

Google.NTx86

GPS (Global Positioning System) 1, 2, 3, 4,

GPS-Koordinate

GPX (GPS eXchange Format)

Gradle

Grafisches Oberflächenelement

Graphical Layout (Registerkarte)

gravity

GridLayout

Gruppe

H

Hallo Android

Haltepunkt
setzen

Händlerkonto 1, 2,

Hardwarekomponente

HDPI (High Dots Per Inch)

Hierarchy Viewer

Hintergrunddienst

Hintergrundfarbe
ändern

Hintergrundtask

Hochformat

Holo

Honeycomb 1, 2,

HoneycombGallery

Hotkey

HTC

HTC Sense
ic_launcher.png

I

Ice Cream Sandwich

Icon Design Guidelines
icon.png
id (Attribut)

IDE (Integrated Development Environment)

if
ignition

ImageButton (Widget)

ImageView
import 1, 2, 3,
inflate()

Inhaltsrichtlinie
initialLayout
initLoader() 1, 2,
inputType
insert()

insertOrThrow()

Integer-Array

Intent 1, 2,

Intent-Filter

Intent-Receiver

Intent-Resolution

Aktion

Daten

IntentService
iPhone 1, 2,
isAfterLast()

isFinishing()

iTwitter

J

Jarsigner

Java 1, 2,

Java Development Kit

Java Platform (JDK)

Java SE Downloads

Java Virtual Machine

Java-JDK

Java-SDK

Java-Tutorial

JDK (Java Development Kit) 1, 2, 3,

JDK (Java Development Kit)

Installation

Jelly Bean 1, 2,

JTwitter

JUnit

JVM (Java Virtual Machine)

K

K-9 Mail

Kaskadierende Formatvorlage

Keystore

Keytool 1, 2,

Kindle Fire 1, 2,

Kindle-ähnlicher Emulator

KISS-Prinzip

Klick

Klingel

Status ermitteln

KML (Keyhole Markup Language)

Knopf

Kollisionserkennung

Kompatibilitätsmodus

Kontext-Aktionsleiste

Kontextmenü 1, 2, 3,

Kurz-Druck

L

Ländercode

Lang-Druck

Lang-Klick

Langdruck

Laufzeitfehler

Laufzeitumgebung

Launch-Konfiguration
layout (Ordner)

variables
layout_-Konvention
layout_gravity (Eigenschaft)

layout_height
layout_width

Layoutdatei
layoutopt

LayoutParams
layouts (Ordner)

Layouttyp

Layoutwerkzeug

LDPI (Ligh Dots Per Inch)

Lebenszeit

Lebenszyklus

LG

Library
libs (Ordner)

Lichtsignal

Linux-Kernel
list_menu.xml 1, 2,

ListActivity

ListFragment 1, 2,

ListPreference

ListView Rows (Widget)

Loader 1, 2, 3,

LoaderCallback

LoaderManager

Location Manager

LOCATION_SERVICE

Log

LogCat 1, 2,

Lokalisierung

LOLCat Builder

M

MainActivity 1, 2,

MainActivity.java 1, 2,

Manifest-Datei 1, 2, 3,

Maps-API

MapView

Mashup
match_parent 1, 2, 3,
mAudioManager

Maven
mCalender

MDPI (Medium Dots Per Inch)

Media Frameworks

Mehrbenutzerverwaltung

Menü
erstellen
erweitertes
laden
menu_insert

MenuItem (Widget)

Merchant Account

Message

Metadaten

Methode

MIME-Typ 1, 2,

Min Required SDK
minHeight

Minimum Required SDK
minLines
minSdkVersion 1, 2,
minWidth

Modul
importieren
monitor.bat

Monkey

Motorola
moveToFirst()

moveToNext()

Multitouch

N

Nachrichtenbus-Architektur

Nachrichtensystem

Navigation

Netzwerkspeicher

Netzwerkstapel

Nexus 7

Nexus One

No resource found ...

no_reminders

Nook

Notepad Tutorial

Notification (Klasse)

Notification.Builder

NotificationManager

NotificationManager (Klasse)

notify()

notifyChange()

Nutzdaten

Nutzerkonto

NVidia

O

OHA (Open Handset Alliance)

onActivityCreated() 1, 2,

OnAlarmReceiver (Klasse)

OnAlarmReceiver.java

OnBootReceiver (Klasse)

onCreate (Methode)

onCreate() 1, 2, 3, 4,
onCreateContextMenu() 1, 2, 3,
onCreateLoader() 1, 2,
onCreateOptionsMenu()

onCreateView() 1, 2, 3,

OnDateSetListener 1, 2,
onDestroy()

onHandleIntent() 1, 2,
onKeyDown()

onListItemClick() 1, 2,
onLoaderReset()

onLoadFinished() 1, 2,
onOptionsItemSelected()

onPause (Methode)

onPause()

onReceive()

onStart()

onStop()

OnTimeSetListener 1, 2,
onUpgrade() 1, 2,

Open GL

Open Handset Alliance

Open Source
orientation

P

Paint.NET

Paket

Namenskonventionen

Paketname
parseId()

parseInt()

PendingIntent 1, 2, 3, 4,

PendingIntent.getBroadcast()

Permission

Persönlicher Ordner

Persönlichkeitsrecht
zu Tools setzen

Pfadangabe

Pixeldichte

Play Store

Point

Portnummer

Portweiterleitung

Preference

Preference-Framework

PreferenceActivity (Klasse) 1, 2,

PreferenceCategory

PreferenceFragment

PreferenceScreen
previousFragment

ProgressBar (Klasse)

ProgressDialog

ProgressDialog (Klasse)

project.properties 1, 2,

Projectlocker
anlegen
erstes

Ordner

Projekteinstellung

Properties (Registerkarte)

Properties-Bereich

Properties-Editor

Prozessverwaltung
pt (Punkt)

Punkt

Punktdichte
putSerializable()

putString()

px (Pixel)

Q

Querformat
query() 1, 2,

R

R (Klasse) 1, 2,

R.colors (Klasse)

R.dimens (Klasse)

R.java 1, 2, 3,

R.strings (Klasse)

R.styles (Klasse)

Raw asset file

RECEIVE_BOOT_COMPLETED 1, 2,

Rechtsklick
registerForContextMenu() 1, 2, 3,

Registrierungsgebühr

RelativeLayout 1, 2,
release()

reminder_edit.xml
reminder_edit_activity.xml
reminder_list.xml
reminder_row.xml

ReminderEditActivity

ReminderEditActivity.java

ReminderEditFragment 1, 2,

ReminderEditFragment.java

ReminderListActivity 1, 2, 3,

ReminderListAndEditorActivity.java

ReminderListFragment 1, 2,

ReminderListFragment.java

ReminderManager (Klasse) 1, 2,

ReminderProvider (Klasse)

ReminderProvider.java 1, 2,

RemindersDbAdapter

ReminderService (Klasse)

ReminderService.java

RemoteView

Replica Island
res
res (Ordner) 1, 2,
res/layout (Ordner)

Research In Motion

Ressource
darstellbare

RingtonePreference

RoboGuice

RoboGuice-Framework

Routenplaner

Rückruffunktion

Ruhezustand

Run Configurations

Run-Konfiguration

Run-time exception
samples (Ordner)

S

Samsung
savedInstanceState 1, 2,

Schaltfläche
hinzufügen

Schema

Schließen erzwingen

Schlüssel
öffentlicher

Schlüssel
privater

SCREENLAYOUT_SIZE_LARGE

SCREENLAYOUT_SIZE_MASK
scrollbars

ScrollView 1, 2,

SD Card

SD-Speicherkarte

SDK (Software Development Kit)

SDK (Software Development Kit)

Beispiele

SDK-Manager

SDK-Tool

Secure Sockets Layer
selectionArgs
sendBroadcast()

Service
setClickable()

setEmptyText()

setError()

setHasOptionsMenu()

setKeyListener()

setLatestEventInfo()

setLatestInfo()

setNotificationUri()

ShareCompat
showAsAction
showTimePicker()

Sicherheitsmodell 1, 2,

Signierung

SimpleCursorAdapter 1, 2,

SimpleDateFormat

Skin

SlidingDrawer

Sony Ericsson

Sortierreihenfolge

Soziales Netz
sp (scale-independent pixels)

externer
interner

Speichermedium

Speicherplatz

Speicherverwaltung

Spiel

Spinner (Widget)

Spracherkennung

Sprint

SQL (Structured Query Language)

SQLite 1, 2, 3,

Datenbank

Datenbank erstellen

Website

SQLiteOpenHelper 1, 2,
src (Ordner)

SSL (Secure Sockets Layer)

Standortdienst
startActivity() 1, 2,

Startbildschirm 1, 2,

Startbildschirm-Widget

App-Widgets hinzufügen

Startsymbol
hinzufügen

Statusleiste 1, 2,

Steuerelement 1, 2,
strings.xml 1, 2, 3, 4, 5,

Style 1, 2,

Style Sheets
styles.xml
super.onCreate()

Symbolgröße

Symbolmenü

Systemdienst

Systemnachricht 1, 2, 3,

Systemprotokoll

Systemspeicher

Systemvariable
path

T

T-Mobile 1, 2,

Tabellenobjekt

TabHost

TableLayout

Tablet 1, 2,

Unterschied zu Telefon

Tag

Target SDK

Task-Planer
task_preferences.xml

TaskPreferences (Klasse)

TaskPreferences.java

Tastatur

Tastaturereignis

Tastenkombination

Telefon

Unterschied zu Tablet

Telephony Manager

Template

Terminerinnerung

Terminplaner

Texas Instruments

Texteingabefeld

TextView

Schriftart

TextWatcher

Thema 1, 2,

Theme

Thread

TIME_FORMAT

TimePicker

TimePickerDialog

TimePickerDialogFragment 1, 2,

TimePickerDialogFragment.java

Toast

Toast-Meldung

Toast.LENGTH_LONG

Toast.LENGTH_SHORT

Toast.makeText()

Toggle Breakpoint
toggleUi()

Tonsignal

Tool

Pfad setzen
tools (Namensraum)

tools (Ordner) 1, 2,

Translator Toolkit

Treibermodell

Treppeneffekt
try

Twitter

Twitter-API

Typisiertes Array

U

Uhrzeit
auswählen 1, 2,

UI (User Interface) 1, 2,

UI/Application Exerciser Monkey

Unbekannte Herkunft

Unfuddle

Untermenü
update()

UriMatcher.NO_MATCH

URL

USB-Treiber 1, 2,

Installation
usb_driver
uses-permission
uses-sdk
values
values (Ordner)

V

Verbose

Versionscode 1, 2,

Versionskontrollsystem

Versionsname 1, 2,

VGA (Video Graphics Array)

Vibration

View 1, 2, 3,

View System

ViewPager

Virtual Device Manager

Virtuelles Android-Gerät

Visueller Designer

Vorgabeaktivität

Vorlage

Vorschläge anzeigen

W

WAKE_LOCK

WakeReminderIntentService

WakeReminderIntentService (Klasse)

WakeReminderIntentService.java

WebKit

WebTV

Wecker

Werbung

Wertressource

Widget 1, 2,
widget.xml
importieren

Wildfire Communications

Workspace

Workspace Launcher
wrap_content 1, 2,

X

XHDPI (Extra High Dots Per Inch)

XML (eXtensible Markup Language) 1, 2,

XML-Datei

XML-Layoutdatei

Z

Zertifikat

Zielplattform 1, 2,

Auswahl

Zielpublikum

Internetadresse
zipalign