动态壁纸是在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