andforge.net

The android Blog! News, Tutorials and Reports!

Live Wallpaper Tutorial für Android 2.1

Posted by Mohammed El Batya January - 26 - 2010 - Tuesday

Wie viele von euch sicherlich bereits mitbekommen haben hat Google vor ein paar Wochen das Nexus One veröffentlicht und damit auch die neue Android-Version 2.1. Eines der tollen neuen Features nennt sich “Live Wallpaper” (auch manchmal “Living Wallpaper” genannt). Das sind coole dynamische Desktop-Hintergründe, welche schicke Animationen darstellen. Oft reagieren diese auch auf Benutzerinteraktionen.

Ich möchte mit diesem Artikel einen grundlegenden Überblick über die Programmierung eines solchen Live Wallpapers vermitteln.
Dieser Artikel enthält keinen voll funktionstüchtigen Code, da bereits ein gutes Beispiel im SDK 2.1 (namens “Cube”) mitgeliefert wird.

Vom Prinzip her ist so ein Live Wallpaper ( ab jetzt LWP abgekürzt ) das selbe wie eine normale Activity, welche auf einem 2D Surface einfache Dinge wie Kreise und und Linien zeichnet. Es ist vergleichbar mit der Programmierung eines kleinen 2D Spiels.

Voraussetzungen für dieses Tutorial:

  • Du hast schon mal eine normale Activity programmiert.
  • Du hast das SDK 2.1 installiert.

0. Grundlagen

Ein LWP besteht im Grunde aus 5 Teilen:

  1. einem WallpaperService
  2. einer “Engine”, welche sich um das Zeichnen kümmert
  3. einer “Einstellungen”-Activity zur Anpassung des Wallpapers durch den User (z.B. Farben)
  4. einer XML-Datei um zu konfigurieren wie das LWP aufgelistet wird (z.B. Icon)
  5. das übliche Android-Manifest

1. Der WallpaperService

Zunächst benötigst du eine Klasse, welche von “WallpaperService” erbt. In dieser Klasse musst du nur die Methode “onCreateEngine()” überschreiben und deine eigene Engine zurückgeben.

public class AndforgeWallpaper extends WallpaperService {
	@Override
	public Engine onCreateEngine() {
		return new AndforgeLWPEngine();
	}
}

2. Die Engine

Die Engine übernimmt das eigentliche Zeichnen und muss innerhalb deines WallpapersServices definiert werden. Also definieren wir als nächstes eine innere Klasse, welche von WallpaperService.Engine erbt.

public class AndforgeWallpaper extends WallpaperService {
	@Override
	public Engine onCreateEngine() {
		return new AndforgeLWPEngine();
	}

	 class AndforgeLWPEngine extends Engine{
		// TODO: ausprogrammieren
 	}
}

Die Engine besitzt einen SurfaceHolder, welcher wiederum ein Canvas besitzt. Auf diesem Canvas kann man letztendlich seine Animationen zeichnen. Mit der Methode getSurfaceHolder() bekommt man in der Engine das Surface. Wendet man auf das Surface die Methode lockCanvas() an bekommt man das Canvas zum zeichnen.

Achtung: Es ist wichtig das Canvas über die Methode lockCanvas() zu erhalten, da es sonst zu seltsamen Fehlern kommen kann. Dies hat unter anderem damit zu tun, dass “DoubleBuffering” eingesetzt wird.

DoubleBuffering?
Kurz gesagt: Es werden zwei Bilder generiert, eines auf dem gerade gezeichnet wird und eines, welches auf dem Bildschirm zu sehen ist. Diese Bilder werden nach jedem Zeichenvorgang ausgetauscht, was bedeutet, dass man immer eine “alte” Version des Bildes zum Zeichnen bekommt. Ruft man nun lockCanvas() auf bekommt man das Canvas, welches gerade nicht angezeigt wird um darauf zu zeichnen. Ist man fertig mit dem Zeichnen kann man mit unlockCanvasAndPost(canvas) das Canvas wieder freigeben und ein Austauschen des angezeigten Canvas mit dem frisch bemalten Canvas anstoßen.

Das Zeichnen sieht dann ungefähr so aus:


Canvas canvas = getSurfaceHolder().lockCanvas();

//TODO: auf dem canvas zeichnen

getSurfaceHolder().unlockCanvasAndPost(canvas);

Wo stecken wir nur unseren Code zum Zeichnen hin? Theoretisch kann man jetzt eine beliebige Methode der Engine überschreiben und dort etwas zeichnen (was nicht immer sinnvoll ist ^^). Es ist wichtig zu verstehen, dass die Engine einen Lifecycle (Lebenszyklus) durchlebt. Das bedeutet, dass es z.B. Momente gibt in denen es gar keinen SufaceHolder gibt ( also der SurfaceHolder null ist). Hier sind die Methoden onSurfaceDestroyed(), onSurfaceCreated(), onSurfaceChanged(), onDestroy(), onCreate(), usw. entsprechend zu beachten.

Aber wo kommt jetzt endlich der Code zum Zeichnen hin?

Eine mögliche (aber nicht besonders sinnvolle) Stelle wäre die onSurfaceCreated() Methode:

@Override
		public void onSurfaceCreated(SurfaceHolder arg0) {

			Canvas canvas = getSurfaceHolder().lockCanvas();

			// Zeichne einen Kreis
			canvas.drawCircle(200, 200, 10, color);

			getSurfaceHolder().unlockCanvasAndPost(canvas);
		}

Dies hat zur Folge, dass ein Kreis gezeichnet wird sobald der SurfaceHolder erzeugt wird, also einmal und dann nie wieder.

Was wir aber für bewegte Animationen benötigen ist eine Möglichkeit das Zeichnen regelmäßig anzustoßen. Das Cube Beispiel aus dem SDK verwendet hierfür eine recht elegante Lösung unter Verwendung eines Handlers, welcher sich selbst immer wieder auf die “Queue” schmeißt. Ich möchte hier nicht weiter auf die Implementierung der Engine eingehen und auf das Cube Beispiel verweisen, da dies sonst den Rahmen des Artikels sprengen würde. Ich bin mir aber sicher, dass man mit dem hier gelernten Wissen das Cube Beispiel relativ gut verstehen kann. (Falls jemand Interesse an einem ausführlicheren Artikel zur Implementierung eines Wallpapers hat, kann er/sie gerne einen Kommentar hinterlassen.)

3. Die “Einstellungen”-Activity

Dies ist eine ganz normale Activity, welche dem Nutzer ermöglichen soll das Wallpaper zu personalisieren. Vorstellbar wären z.B. Farbe, Geschwindigkeit und diverse Effekte des Wallpapers. Hierfür sollte man wie im Cube Beispiel auf SharedPreferences zurückgreifen um die Einstellungen auch beim Zeichnen mitzubekommen. Zusammengefasst: Diese SettingsActivity lässt den Nutzer die SharedPreferences editieren, welche wiederum in der Engine beim Zeichnen berücksichtigt werden.

Hier sollte man beachten, dass diese Activity normalerweise nicht im AppMenü von Android auftauchen soll. Daher sollte man das Manifest (siehe Punkt 5) entsprechend anpassen. Diese App wird gestartet wenn der Nutzer in der LWP-Liste neben dem LWP auf Einstellungen klickt.

4. LWP lwp_config.xml

Diese XML Datei dient dazu, zu konfigurieren wie das LWP im LWP-Menü aufgelistet wird. Sie muss sich im Verzeichnis res/xml befinden, wenn dieses Verzeichnis nicht existiert muss man eins erstellen. Eine typische Konfiguration würde folgende Werte enthalten:

  • android:thumbnail = mini Vorschaubild in der LWP-Liste
  • android:description = kurze Beschreibung für die LWP-Liste
  • android:settingsactivity = Packagepfad zu der Settings-Activity


5. Das Manifest

Im Folgenden wird der WallpaperService und die SettingsActivity eingerichtet.
Man beachte, dass kein Menüeintrag erstellt wird.



	

		
			
				
			
			
		

		
		

	
	


Ich hoffe ich konnte einen Überblick und ein Grundverständnis über die Erstellung eines Android Live Wallpapers bieten. Ich habe bewusst ein paar Details vorenthalten und versucht mich auf das Wesentliche zu konzentrieren. Bei offenen Fragen einfach einen Kommentar hinterlassen.

Share and Enjoy:
  • Twitter
  • Facebook
  • Tumblr
  • del.icio.us
  • Digg
  • Sphinn
  • LinkedIn
  • Mixx
  • Google Bookmarks
  • Blogplay
  • RSS
  • Print

5 Responses to “Live Wallpaper Tutorial für Android 2.1”

  1. John says:

    Dear sirs,
    could you share the eclipse project of this, so I could study and understand it better?
    I am interested to see how the circle is drawn.

    Thanks

  2. Israel Soffa says:

    can i see more of this please

  3. Fr4gg0r says:

    Hi,
    der Tag muss hinter dem stehen, ansonsten wurde mein LWP in der Liste mit folgender Begründung nicht angezeigt: “No android.service.wallpaper meta-data”
    Gruß

  4. Fr4gg0r says:

    wow
    anscheinend hat das System die Tags als Html Anweisungen interpretiert…
    So sollte es heißen:
    der Tag “” muss hinter dem “”

  5. Fr4gg0r says:

    ich werd bekloppt xD
    meta-data muss hinter intent-filter stehen…
    Vll. kann der Admin ja die 3 Posts zusammenführen

Leave a Reply