Hier findet ihr die deutsche Übersetzung meiner Präsentation "Android Beginners Workshop", die ich auf dem Mobile Monday m2d2 Developer Day in Düsseldorf am 23.02.2010 gehalten hab. Dieser Workshop richtet sich an Anfänger, die hier ihre erste "Hello World"-App erstellen werden.
Die englische Originalpräsentation findet ihr hier: Android Beginners Workshop.
Die deutsche Übersetzung als PDF findet ihr hier: Android Anfänger Workshop.
Im folgenden Bild seht ihr drei Activities der Contacts/Dialer App. Gut zu sehen sind die zwei Einstiegspunkte: Die App kann über das Icon "Contacts" sowie über das Icon "Dialer" gestartet werden. Die App startet dann mit der entsprechenden Activity.
Activity
View
Event
Intent
Resourcen auf dem Mobiltelefon sind limitiert. Daher muss das Android-Betriebssystem in der Lage sein, nicht aktive Activities (d. h. nicht im Vordergrund sichtbar), jederzeit zu beenden.
Jede Activity hat einen sogenannten Lifecycle ("Lebenszyklus"), der vom Betriebssystem gesteuert wird.
Um Datenverlust oder den Verlust des Zustands einer Activity zu vermeiden, müssen die Daten/der Zustand der Activity gesichert werden, wenn diese pausiert (nicht mehr im Vordergrund, aber noch sichtbar) oder gestoppt (nicht mehr sichtbar) wird.
Zunächst wird Eclipse benötigt. Die Installation von Eclipse wird hier jedoch nicht behandelt.
Lade das SDK Starter Package für Dein Betriebssystem unter http://developer.android.com/sdk/index.html herunter und installiere es.
Starte des SDK Starter Package (als Administrator unter Windows Vista / Windows 7). Beim ersten Start siehst Du eine Auswahl verfügbarer Pakete, die Du alle installieren solltest (alle SDK Plattformen und Dokumentation).
Zusätzlich solltest Du noch "Usb Driver Package" auswählen - damit kannst Du nachher Deine App aus Eclipse heraus direkt auf Deinem Android-Handy starten und debuggen:
Die folgende Beschreibung ist für Eclipse 3.5 (Galileo) und Eclipse 3.6 (Helios). Instruktionen für Eclipse 3.4 (Ganymede) gibt es unter http://developer.android.com/sdk/eclipse-adt.html.
Mit dem ADT-Assistenten wird ein neues Projekt erstellt:
Wähle "File" → "New" → "Project..."; im "New Project"-Dialog wähle "Android Project" aus dem "Android" Order.
Füll den "New Android Project"-Dialog wie folgt aus:
Klicke auf den "Finish"-Button.
Der "New Project"-Assistent hat folgende Dateien und Ordner erstellt:
| HelloWorkshopActivity.java: | Klasse der Haupt-Activity |
| R.java: | Resourcen-ID-Definitionen (automatisch erstellt, nicht editieren!) |
| /assets: | Ordner für Binär-Dateien (alles, was Android nicht selbst verwaltet) |
| /res: | Ordner für Resource-Dateien (Bilder, Layouts, Strings, ...) |
| /res/drawable-hdpi/icon.png: | Logo in hoher Auflösung (72 x 72 Pixel – 240 dpi) |
| /res/drawable-ldpi/icon.png: | Logo in niedriger Auflösung (36 x 36 Pixel – 120 dpi) |
| /res/drawable-mdpi/icon.png: | Logo in mittlerer Auflösung (48 x 48 Pixel – 160 dpi) |
| /res/layout/main.xml: | Layout-Definition für View der Haupt-Activity |
| /res/values/strings.xml: | String-Definitionen |
| AndroidManifest.xml: | "Manifest"-Datei, definiert Infos wie Name, Logo und Haupt-Activity |
| default.properties: | Projekt-Eigenschaften |
Damit Handys mit hohen Auflösungen (z. B. Motorola Milestone) auch die hdpi-Resourcen verwenden, muss folgender Eintrag in die AndroidManifest.xml-Datei eingefügt werden:
<supports-screens android:anyDensity="true" />
Anonsten würden bei hohen Auflösungen die mdpi-Resourcen verwendet und hoch-skaliert werden.
Damit die App auf Handys mit großem Screen (Archos 5) oder kleinem Screen (HTC Tattoo) den kompletten Bildschirm ausfüllt bzw. überhaupt läuft müssen folgende Attribute dem zuvor eingefügten Element hinzugefügt werden:
<supports-screens android:anyDensity="true"
android:largeScreens="true"
android:smallScreens="true" />
Ansonsten werden Handys mit großem Screen die App nur in einem kleinen Bereich anzeigen und Handys mit kleinem Screen werden die App nicht starten.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, HelloWorkshopActivity!</string>
<string name="app_name">Hello Workshop</string>
</resources>
Definiert Strings, die in den Manifest- und Layout-Dateien referenziert wurden (könnten auch im Code referenziert werden):
android:label="@string/app_name"
ist äquivalent zu
android:label="Hello Workshop"
Natürlich nur solange wir keine strings.xml-Dateien in anderen Sprachen hinzufügen. Wenn Du Deine App lokalisieren willst, solltest Du Deine Strings immer in Resourcen-Dateien auslagern!
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout>
Definiert ein (einfaches) Layout namens "main" (bloß ein Name, hat keine Bedeutung), welcher später in der Activity referenziert wird.
Das Layout sieht wie folgt aus:
Automatisch erstellt – definiert Integer-Konstanten für jede Resource. Diese Datei niemals editieren – man braucht sie eignetlich nicht mal einzusehen.
package de.test.hello;
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;
}
}
HelloWorkshop2Activity.java
package de.test.hello;
import android.app.Activity;
import android.os.Bundle;
public class HelloWorkshopActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
Wichtig ist hier die letzte Zeile:
setContentView(R.layout.main);
Diese setzt das angegebene Layout als View dieser Activity. Die Konstante aus der R-Klasse referenziert das Layout, welches in der Datei main.xml definiert wurde.
Das wollen wir heute erreichen:
Füge ein „Enter your name“-Textfeld unter die ersten TextView-Komponente ein:
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/enter_your_name"
/>
Eclipse wird jetzt eine Fehlermeldung anzeigen: „No resource found that matches the given name (at 'text' with value '@string/enter_your_name').
Füge die fehlende Resource zu der Datei strings.xml hinzu:
<string name="enter_your_name">Enter your name:</string>
Die Fehlermeldung in main.xml wird jetzt verschwinden.
Füge ein Eingabefeld unter der „Enter your name“-Komponente ein:
<EditText
android:id="@+id/name_field"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
Die erste Zeile weist der Komponente die ID „name_field“ zu.
Unter dem Namens-Feld wollen wir die Buttons nebeneinander anordnen. Dazu benötigen wir ein LinearLayout mit horizontaler Orientierung und darin die zwei Buttons.
Inneres LinearLayout mit zwei Buttons:
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:id="@+id/hi_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/hi_button"
/>
<Button
android:id="@+id/hello_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/hello_button"
/>
</LinearLayout>
Füge die Button-Texte in die strings.xml-Datei ein:
<string name="hi_button">Say Hi!</string> <string name="hello_button">Say Hello!</string>
Schauen wir uns das Layout einmal an!
Auf der "main.xml"-Seite in Eclipse, klicke auf den "Layout"-Reiter am unteren Rand und Du wirst folgendes sehen:
Nun erweitern wir unsere Activity. Öffne "HelloWorkshopActivity.java" und füge folgende Felder am Beginn der Klasse ein:
private Button hiButton; private Button helloButton;
Am Ende der onCreate-Methode, füge folgenden Code ein:
hiButton = (Button)findViewById(R.id.hi_button); hiButton.setOnClickListener(this); helloButton = (Button)findViewById(R.id.hello_button); helloButton.setOnClickListener(this);
Eclipse meldet nun dass HelloWorkshopActivity das Interface OnClickListener nicht implementiert. Klick auf die Fehlermeldung und wähle "Let 'HelloWorkshopActivity' implement 'OnClickListener'."
Nun zeigt Eclipse an, dass HelloWorkshopActivity die OnClickListener-Methoden nicht definiert. Klick auf die Fehlermeldung und wähle "Add unimplemented methods".
Wenn der User auf einen der Buttons klickt, müssen wir zunächst prüfen, ob er seinen Namen eingegeben hat. Füge folgenden Code in die „onClick“-Methode ein:
EditText nameField = (EditText) findViewById(R.id.name_field);
String name = nameField.getText().toString();
if (name.length() == 0) {
new AlertDialog.Builder(this)
.setMessage(R.string.error_name_missing)
.setNeutralButton(R.string.error_ok, null)
.show();
return;
}
Füge die zwei oben referenzierten Strings sowie zwei weitere in die strings.xml-Datei ein:
<string name="error_name_missing">Please enter your name.</string> <string name="error_ok">OK</string> <string name="hi_greeting">Hi %s!</string> <string name="hello_greeting">Hello %s!</string>
Der User hat seinen Namen eingetragen. Nun zeigen wir die Begrüßung an:
if (v == hiButton || v == helloButton)
{
int resourceId = v == hiButton ? R.string.hi_greeting
: R.string.hello_greeting;
String greeting = getResources().getString(resourceId, name);
Toast.makeText(this, greeting, Toast.LENGTH_LONG).show();
}
Schließlich wollen wir die Begrüßung am oberen Bildschirmrand ändern. Um den Text der Komponente zu ändern, müssen wir ihr zunächst eine ID zuweisen. Bearbeite die main.xml-Datei und füge wie folgt eine ID in das erste TextView-Element ein:
<TextView
android:id="@+id/greeting_field"
… />
Die zuvor erstellte Methode wird nun erweitert:
if (v == hiButton || v == helloButton)
{
int resourceId = v == hiButton ? R.string.hi_greeting
: R.string.hello_greeting;
String greeting = getResources().getString(resourceId, name);
Toast.makeText(this, greeting, Toast.LENGTH_LONG).show();
TextView greetingField = (TextView) findViewById(R.id.greeting_field);
greetingField.setText(greeting);
}
Und nun? Wir sind fertig – starte das Projekt!
Starte das Projekt wie zuvor (am einfachsten mit Strg + F11), hier sind ein paar Screenshots:
In Android 1.6 wurden unterschiedliche Bildschirmauflösungen und Pixeldichten eingefügt. Für Android 1.5 und älter sind die Ordner „drawable-hdpi“, drawable-ldpi“, „drawable-mdpi“ unbekannt.
In der AndroidManifest.xml-Datei, ändere die Zeile
<uses-sdk android:minSdkVersion="4" />
in
<uses-sdk android:minSdkVersion="3" targetSdkVersion="4" />
HelloWorkshopActivity.java:
package de.test.hello;
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class HelloWorkshopActivity extends Activity
implements OnClickListener {
private Button hiButton;
private Button helloButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
hiButton = (Button) findViewById(R.id.hi_button);
hiButton.setOnClickListener(this);
helloButton = (Button) findViewById(R.id.hello_button);
helloButton.setOnClickListener(this);
}
public void onClick(View v) {
EditText nameField = (EditText) findViewById(R.id.name_field);
String name = nameField.getText().toString();
if (name.length() == 0) {
new AlertDialog.Builder(this).setMessage(
R.string.error_name_missing).setNeutralButton(
R.string.error_ok,
null).show();
return;
}
if (v == hiButton || v == helloButton) {
int resourceId = v == hiButton ? R.string.hi_greeting
: R.string.hello_greeting;
String greeting = getResources().getString(resourceId, name);
Toast.makeText(this, greeting, Toast.LENGTH_LONG).show();
TextView greetingField = (TextView) findViewById(R.id.greeting_field);
greetingField.setText(greeting);
}
}
}
main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/greeting_field" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:text="@string/enter_your_name" /> <EditText android:id="@+id/name_field" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" > <Button android:id="@+id/hi_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hi_button" android:layout_weight="1" /> <Button android:id="@+id/hello_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_button" android:layout_weight="1" /> </LinearLayout> </LinearLayout>
strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, HelloWorkshopActivity!</string>
<string name="app_name">Hello Workshop</string>
<string name="enter_your_name">Enter your name:</string>
<string name="hi_button">Say Hi!</string>
<string name="hello_button">Say Hello!</string>
<string name="error_name_missing">Please enter your name.</string>
<string name="error_ok">OK</string>
<string name="hi_greeting">Hi %s!</string>
<string name="hello_greeting">Hello %s!</string>
</resources>