Android動態壁紙的制作教程
動態壁紙是在Android 2.1新增的一個功能。動態壁紙可以添加到Android的桌面,具有交互式的動畫背景效果。在本教程中,我們將教會你如何去制作一個交互式的動態壁紙。
動態壁紙是一個Android應用程序,包括一個服務(WallpaperService)。該服務必須包括一個引擎(WallpaperService.Engine)。該引擎是連接用戶、桌面、系統之間的橋梁。它也可以繪制桌面壁紙。
首先,必須由內在的Engine類創建一個WallpaperService類。該服務必須在AndroidManifest.xml中聲明為"android.service.wallpaper.WallpaperService",這樣它才會作為動態壁紙被手機識別。而且還要在服務配置中附加"android.permission.BIND_WALLPAPER"的權限許可:
<service
android:name="LiveWallpaperService"
android:enabled="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:permission="android.permission.BIND_WALLPAPER">
<intent-filter android:priority="1" >
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/wallpaper" />
</service>創建一個XML文件,放置在應用程序目錄下的/res/xml/中。它用來描述你的動態壁紙。
<?xml version="1.0" encoding="UTF-8"?>
<wallpaper
xmlns:android="http://schemas.android.com/apk/res/android"
android:thumbnail="@drawable/thumbnail"
android:description="@string/description"
android:settingsActivity="PreferenceActivity"/>再創建一個xml的屬性文件 attrs.xml ,代碼如下:
<declare-styleable name="Wallpaper">
<!-- Component name of an activity that allows the user to modify
the current settings for this wallpaper. -->
<attr name="settingsActivity" />
<!-- Reference to a the wallpaper's thumbnail bitmap. -->
<attr name="thumbnail" format="reference" />
<!-- Name of the author of this component, e.g. Google. -->
<attr name="author" format="reference" />
<!-- Short description of the component's purpose or behavior. -->
<attr name="description" />
</declare-styleable>動態壁紙的服務代碼如下:
package net.androgames.blog.sample.livewallpaper;
import android.content.SharedPreferences;
import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
/**
* Android Live Wallpaper Archetype
* @author antoine vianey
* under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
*/
public class LiveWallpaperService extends WallpaperService {
@Override
public Engine onCreateEngine() {
return new SampleEngine();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
public class SampleEngine extends Engine {
private LiveWallpaperPainting painting;
SampleEngine() {
SurfaceHolder holder = getSurfaceHolder();
painting = new LiveWallpaperPainting(holder,
getApplicationContext());
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
// register listeners and callbacks here
setTouchEventsEnabled(true);
}
@Override
public void onDestroy() {
super.onDestroy();
// remove listeners and callbacks here
painting.stopPainting();
}
@Override
public void onVisibilityChanged(boolean visible) {
if (visible) {
// register listeners and callbacks here
painting.resumePainting();
} else {
// remove listeners and callbacks here
painting.pausePainting();
}
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
painting.setSurfaceSize(width, height);
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
// start painting
painting.start();
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
boolean retry = true;
painting.stopPainting();
while (retry) {
try {
painting.join();
retry = false;
} catch (InterruptedException e) {}
}
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xStep, float yStep, int xPixels, int yPixels) {
}
@Override
public void onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
painting.doTouchEvent(event);
}
}
}當壁紙的顯示、狀態或大小變化是,會調用Engine的onCreate, onDestroy, onVisibilityChanged, onSurfaceChanged, onSurfaceCreated 和 onSurfaceDestroyed方法。有了這些方法,動態壁紙才能展現出動畫效果。而通過設置setTouchEventsEnabled(true),并且調用onTouchEvent(MotionEvent event)方法,來激活觸摸事件。
我們在繪畫墻紙的時候,也會使用一個單獨的繪畫線程:
package net.androgames.blog.sample.livewallpaper;
import android.content.Context;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
/**
* Android Live Wallpaper painting thread Archetype
* @author antoine vianey
* GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
*/
public class LiveWallpaperPainting extends Thread {
/** Reference to the View and the context */
private SurfaceHolder surfaceHolder;
private Context context;
/** State */
private boolean wait;
private boolean run;
/** Dimensions */
private int width;
private int height;
/** Time tracking */
private long previousTime;
private long currentTime;
public LiveWallpaperPainting(SurfaceHolder surfaceHolder,
Context context) {
// keep a reference of the context and the surface
// the context is needed if you want to inflate
// some resources from your livewallpaper .apk
this.surfaceHolder = surfaceHolder;
this.context = context;
// don't animate until surface is created and displayed
this.wait = true;
}
/**
* Pauses the live wallpaper animation
*/
public void pausePainting() {
this.wait = true;
synchronized(this) {
this.notify();
}
}
/**
* Resume the live wallpaper animation
*/
public void resumePainting() {
this.wait = false;
synchronized(this) {
this.notify();
}
}
/**
* Stop the live wallpaper animation
*/
public void stopPainting() {
this.run = false;
synchronized(this) {
this.notify();
}
}
@Override
public void run() {
this.run = true;
Canvas c = null;
while (run) {
try {
c = this.surfaceHolder.lockCanvas(null);
synchronized (this.surfaceHolder) {
currentTime = System.currentTimeMillis();
updatePhysics();
doDraw(c);
previousTime = currentTime;
}
} finally {
if (c != null) {
this.surfaceHolder.unlockCanvasAndPost(c);
}
}
// pause if no need to animate
synchronized (this) {
if (wait) {
try {
wait();
} catch (Exception e) {}
}
}
}
}
/**
* Invoke when the surface dimension change
*/
public void setSurfaceSize(int width, int height) {
this.width = width;
this.height = height;
synchronized(this) {
this.notify();
}
}
/**
* Invoke while the screen is touched
*/
public void doTouchEvent(MotionEvent event) {
// handle the event here
// if there is something to animate
// then wake up
this.wait = false;
synchronized(this) {
notify();
}
}
/**
* Do the actual drawing stuff
*/
private void doDraw(Canvas canvas) {}
/**
* Update the animation, sprites or whatever.
* If there is nothing to animate set the wait
* attribute of the thread to true
*/
private void updatePhysics() {
// if nothing was updated :
// this.wait = true;
}
}如果桌面壁紙是可見狀態下,系統服務通知有新的東西,這個類會優先把它繪制在畫布上。如果沒有動畫了,updatePhysics會通知線程去等待。通常SurfaceView在有兩個畫布交替繪制的時候,會在畫布上繪制上一次......
如果要讓你的動態墻紙有配置功能,只要創建一個PreferenceActivity,并將它在wallpaper.xml文件中聲明。同時讓SharedPreference對象可以找到你的配置選項。
教程就寫到這里,如果還有什么不懂,你可以通過Eclipse來瀏覽完整的源代碼:SampleLiveWallpaper