Dans cet article nous allons apprendre à réaliser un Live Wallpaper sous Android, autrement dit, un fond d’écran animé et interactif.
Sous Android, une classe permettant de développer des wallpapers est la classe «WallpaperService». Cette classe est disponible depuis la version 2.1 d’Android.

Mon premier Live Wallpaper

Le but est de créer un live wallpaper contenant une image qui se déplace aléatoirement, cette image pourra aussi être déplacé en touchant l’écran.

Pour commencer, nous allons créer un nouveau projet Android qu’on nommera:«MyLiveWallpaper». Le projet possédera les propriétés suivantes :

  • Version du SDK : 2.3
  • Application Name : My First Live Wallpaper
  • Package Name : com.tuto.android
  • Min SDK version : 10
  • Décocher la cache : Create Activity

On va tout d’abord créer dans le dossier res/xml un fichier «mywallpaper.xml» qui servira à décrire le wallpaper :

<?xml version="1.0" encoding="UTF-8"?>
<wallpaper 
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:thumbnail="@drawable/android"
	android:description="@string/wallpaper_description"/>

android:description et android:thumbnail permettent d’affecter respectivement une description et une image à votre wallpaper. Cette description est disponible lors de la selection du wallpaper par l’utilisateur.

Maintenant nous allons créer une classe «MyLiveWallpaper» qui hérite de la classe «WallpaperService», puis on va surcharger la méthode onCreateEngine() qui retourne une instance de la classe «Engine», cet instance gère :

  • Le dessin,
  • Les animations,
  • Le cycle de vie,
  • L’interaction utilisateur.
public class MyLiveWallpaper extends WallpaperService {
	@Override
	public Engine onCreateEngine() {
		return new LiveWallpaperEngine();
	}
}

Une fois cette étape effectuée, on crée une classe membre «LiveWallpaperEngine» qui étend la classe «Engine» puis on surcharge ses méthodes comme ci-dessous:

private class LiveWallpaperEngine extends Engine {

	private final Handler handler = new Handler();
	private final Runnable drawer = new Runnable() {
		@Override
		public void run() {
			draw();
		}
	};

	private boolean visible = true;
	private int width;
	private int height;

	public LiveWallpaperEngine() {
		handler.post(drawer);
	}

	@Override
	public void onCreate(SurfaceHolder surfaceHolder) {
	    super.onCreate(surfaceHolder);
	    setTouchEventsEnabled(true);
	}

	@Override
	public void onDestroy() {
	    super.onDestroy();
	    handler.removeCallbacks(drawer);
	}

	@Override
	public void onSurfaceCreated(SurfaceHolder holder) {
		super.onSurfaceCreated(holder);
	}

	@Override
	public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		this.width = width;
		this.height = height;
		super.onSurfaceChanged(holder, format, width, height);
	}

	@Override
	public void onSurfaceDestroyed(SurfaceHolder holder) {
		super.onSurfaceDestroyed(holder);
		this.visible = false;
		handler.removeCallbacks(drawer);
	}

	@Override
	public void onVisibilityChanged(boolean visible) {
		this.visible = visible;
		if (visible) {
			handler.post(drawer);
		} else {
			handler.removeCallbacks(drawer);
		}
	}

	@Override
	public void onTouchEvent(MotionEvent event) {
		super.onTouchEvent(event);
	}
}

Passons à quelques explications :

  • La méthode onCreate(SurfaceHolder surfaceHolder) initialise l’engine et active en même temps les évènements tactile via la méthode setTouchEventsEnabled(true).
  • La méthode onDestroy() : Après l’appel à cette méthode, l’engine n’est plus valide.
  • La méthode onSurfaceCreated(SurfaceHolder holder) est appelé quand la surface de dessin est créée.
  • La méthode onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) est appelé quand  la surface de dessin subit un changement.
  • La méthode onSurfaceDestroyd(SurfaceHolder holder) est appelé quand la surface de dessin est détruite.
  • La méthode onVisibilityChanged(boolean visible) nous permet de savoir si le wallpaper est visible ou non. Quand le wallpaper est visible, le dessin continu de s’effectuer sur le fond d’écran. Si une application passe en foreground le dessin s’arrête, cela permet d’économiser la batterie et minimiser l’impact du wallpaper sur les performances du système.
  • La méthode onTouchEvent(MotionEvent event) gère l’interaction utilisateur avec le wallpaper.
  • Un live wallpaper a un contenu dynamique, ce qui permet d’avoir des animations. C’est donc à vous de gérer les différentes étapes de dessin sur le wallpaper. Pour cela, on utilise les deux classes Runnable et Handler.

    Le handler envoi le runnable (ici drawer) dans le messageQueue associé à l’UI Thread afin qu’il soit exécuté. Ce runnable se charge de dessiner sur le wallpaper grâce à la méthode draw() qu’on expliquera plus tard. Répéter plusieurs fois ce mécanisme nous permet d’avoir une animation.

    Quand le wallpaper doit s’arrêter, la variable visible (qui stocke la visibilité actuelle du wallpaper) passe à false dans les méthodes onSurfaceDestroyed() et  onVisibilityChanged() ce qui stoppera l’animation (méthode handler.removeCallbacks(drawer).)

    Quand le wallpaper devient visible, la variable visible passe à true dans la méthode  onVisibilityChanged() , ce qui relance l’animation du wallpaper (méthode handler.post(drawer).)

    Voyons maintenant comment on dessine sur le wallpaper, la méthode facilitant cette opération est la méthode draw() :

    private void draw() {
    	SurfaceHolder holder = getSurfaceHolder();
    	Canvas canvas = null;
    	try {
    		canvas = holder.lockCanvas();
    		if (canvas != null) {
    			float x = (width * random.nextFloat());
    			float y = (height * random.nextFloat());
    			drawImage(canvas, x, y);
    		}
    	} finally {
    		if (canvas != null)
    			holder.unlockCanvasAndPost(canvas);
    	}
    	handler.removeCallbacks(drawer);
    	if (visible) {
    		//On re-poste le runnable après un petit laps de temps
    		handler.postDelayed(drawer, 4000);
    	}
    }

    La méthode qui permet de dessiner l’image dans le canvas est décrite ci-dessous :

    private void drawImage(Canvas canvas, float x, float y) {
    	canvas.drawColor(Color.WHITE);
    	canvas.drawBitmap(androidPic, x-(androidPic.getWidth()/2), y-(androidPic.getHeight()/2), null);
    }
    

    Sans oublier de déclarer la variable qui représente l’image qu’on veut dessiner :

    Bitmap androidPic = BitmapFactory.decodeResource(getResources(), R.drawable.android);
    

    Explications :

    Nous avons utilisé un Canvas, ce qui nous permet de dessiner de manière répétitive sur la Surface du wallpaper. Cette surface est fournie par la classe « SurfaceView ».

    Afin de manipuler cette surface on utilise l’interface SurfaceHolder. On obtient le canvas grace à la méthode lockCanvas(), puis on dessine sur un point du canvas avec la méthode drawImage().Ce point est obtenu aléatoirement grâce à ses coordonnés (x,y).

    Enfin la méthode unlockCanvasAndPost(canvas) est appelé pour que le canvas soit déssiné sur la surface du wallpaper.

    Gestion des événements tactiles :

    Cela se fait grâce à la méthode onTouchEvent(MotioEvent event), cette méthode posséde le même comportement que la méthode draw(), sauf que cette fois les coordonnées du point où s’effectue le dessin sont obtenus à l’endroit ou l’interaction utilisateur est effectuée.

    @Override
    public void onTouchEvent(MotionEvent event) {
    	float X = event.getX();
    	float Y = event.getY();
    	SurfaceHolder holder = getSurfaceHolder();
    	Canvas canvas = null;
    	try {
    		canvas = holder.lockCanvas();
    		if (canvas != null) {
    			canvas.drawColor(Color.WHITE);
    			drawImage(canvas, X, Y);
    		}
    	} finally {
    		if (canvas != null)
    			holder.unlockCanvasAndPost(canvas);
    	}
    	handler.removeCallbacks(drawer);
    	if (visible) {
    		handler.postDelayed(drawer, 4000);
    	}
    	super.onTouchEvent(event);
    }
    

    Ce qui donnera :

    import java.util.Random;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.os.Handler;
    import android.service.wallpaper.WallpaperService;
    import android.view.MotionEvent;
    import android.view.SurfaceHolder;
    
    public class MyLiveWallpaper extends WallpaperService {
    
    	private static Random random =  new Random() ;
    
    	@Override
    	public Engine onCreateEngine() {
    		return new LiveWallpaperEngine();
    	}
    
    	private class LiveWallpaperEngine extends Engine {
    
    		private final Handler handler = new Handler();
    		private final Runnable drawer = new Runnable() {
    			@Override
    			public void run() {
    				draw();
    			}
    		};
    
    		private boolean visible = true;
    		private int width;
    		private int height;
    		Bitmap androidPic = BitmapFactory.decodeResource(getResources(), R.drawable.android);
    
    		public LiveWallpaperEngine() {
    			handler.post(drawer);
    		}
    
    		@Override
    	    public void onCreate(SurfaceHolder surfaceHolder) {
    	        super.onCreate(surfaceHolder);
    	        setTouchEventsEnabled(true);
    	        }
    
    	    @Override
    	    public void onDestroy() {
    	        super.onDestroy();
    	        handler.removeCallbacks(drawer);
    	    }
    
    		@Override
    		public void onSurfaceCreated(SurfaceHolder holder) {
    			super.onSurfaceCreated(holder);
    		}
    
    		@Override
    		public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    			this.width = width;
    			this.height = height;
    			super.onSurfaceChanged(holder, format, width, height);
    		}
    
    		@Override
    		public void onSurfaceDestroyed(SurfaceHolder holder) {
    			super.onSurfaceDestroyed(holder);
    			this.visible = false;
    			handler.removeCallbacks(drawer);
    		}
    
    		@Override
    		public void onVisibilityChanged(boolean visible) {
    			this.visible = visible;
    			if (visible) {
    				handler.post(drawer);
    			} else {
    				handler.removeCallbacks(drawer);
    			}
    		}
    
    		@Override
    		public void onTouchEvent(MotionEvent event) {
    			float X = event.getX();
    			float Y = event.getY();
    			SurfaceHolder holder = getSurfaceHolder();
    			Canvas canvas = null;
    			try {
    				canvas = holder.lockCanvas();
    				if (canvas != null) {
    					canvas.drawColor(Color.WHITE);
    					drawImage(canvas, X, Y);
    				}
    			} finally {
    				if (canvas != null)
    					holder.unlockCanvasAndPost(canvas);
    			}
    			handler.removeCallbacks(drawer);
    			if (visible) {
    				handler.postDelayed(drawer, 4000);
    			}
    			super.onTouchEvent(event);
    		}
    
    		private void draw() {
    			SurfaceHolder holder = getSurfaceHolder();
    			Canvas canvas = null;
    			try {
    				canvas = holder.lockCanvas();
    				if (canvas != null) {
    					float x = (width * random.nextFloat());
    					float y = (height * random.nextFloat());
    					drawImage(canvas, x, y);
    				}
    			} finally {
    				if (canvas != null)
    					holder.unlockCanvasAndPost(canvas);
    			}
    			handler.removeCallbacks(drawer);
    			if (visible) {
    				//On re-poste le runnable après un petit laps de temps
    				handler.postDelayed(drawer, 4000);
    			}
    		}
    
    		// Permet de dessiner l'image dans le canvas
    		private void drawImage(Canvas canvas, float x, float y) {
    			canvas.drawColor(Color.WHITE);
    			canvas.drawBitmap(androidPic, x-(androidPic.getWidth()/2), y-(androidPic.getHeight()/2), null);
    		}
    	}
    }

    Dernière étape, on déclare notre service dans l’AndroidManifest.xml avec l’action « android.service.wallpaper.WallpaperService » et la permission « android.permission.BIND_WALLPAPER » qui autorise l’utilisation du Live Wallpaper.
    Notons aussi l’ajout de la balise uses-feature , qui indique à Google Play que votre application contient un Live Wallpaper, pour que celle–ci soit visible qu’aux utilisateurs ayant un device supportant les live wallpapers.

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.tuto.android"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk android:minSdkVersion="10" />
        <uses-feature android:name="android.software.live_wallpaper"></uses-feature>
    
        <application
            android:label="@string/app_name"
            android:icon="@drawable/ic_launcher" >
            
            <service 
                android:label="@string/my_live_wallpaper"
                android:name=".MyLiveWallpaper"
    			android:permission="android.permission.BIND_WALLPAPER">
    			<intent-filter>
    				<action android:name="android.service.wallpaper.WallpaperService"></action>
    			</intent-filter>
    			<meta-data android:name="android.service.wallpaper"
    				android:resource="@xml/mywallpaper"></meta-data>
    		</service>
        </application>
    
    </manifest>
    

    Sans oublier le String.xml :

    <?xml version="1.0" encoding="UTF-8"?>
    <resources>
    
        <string name="app_name">Live Wallpaper</string>
        <string name="my_live_wallpaper">My Live Wallpaper</string>
        <string name="wallpaper_description">My first live wallpaper</string> 
    
    </resources>

    Remarque :

    Pour gérer les préférences de votre wallepaper, créez une PréférenceActivity qui permettra de définir les configurations de votre fond d’écran. La récupération des valeurs de vos préférences s’effectue à l’aide des SharedPreference. Pour cela, ajoutez les lignes suivantes à votre « wallpaper.xml »:

    <android:settingsActivity="MyPreferenceActivity"/>

    sans oublier de déclarer votre activité dans l’AndroidManifest.xml.

    Lancez maintenant votre application, sélectionnez le Live Wallpaper que vous avez créé afin d’obtenir le résultat suivant :

    Conclusion

    Voila, j’espère que cet article vous à permis de mieux comprendre comment fonctionnent les live wallpapers,le code du projet est disponible ici.
    Maintenant place à votre imagination pour créer vos propre fond d’écran animés.

Categories: Tutoriels

18 Responses so far.


  1. Sweetear dit :

    La ressortir en mp4 plutot

  2. Sweetear dit :

    Salut, j’aimerais savoir si tu fais des applications? J’aimerais en faite, que tu puisse mettre une application sur le marcher qui permet de créer des égaliseur audio animé, dans cette application, il y aurais un large choix pour créer son propre égaliseur, soit graphique ou encore en vagues, et qu’après avoir créer son propre thème, pouvoir l’aapliquer en fond d’ecran animé et aussi, que dans cette applications, que l on puisse la ressortir en mp3 avec l activite de l egaliseur, ce serait génial crois moi :)

  3. David dit :

    Pas bien contruit du tout tuto qui pars dans tout les sens ^^ et fichier téléchargeable erroné. =)

  4. loick dit :

    Bonjour je souhaiterai savoir comment puis-je faire pour mettre une image en fond au lieu d’une couleur?
    Merci

Leave a Reply


Notifiez-moi des commentaires à venir via email. Vous pouvez aussi vous abonner sans commenter.