Android动态壁纸的制作教程

fmms 12年前

动态壁纸是在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, onSurfaceCreatedonSurfaceDestroyed方法。有了这些方法,动态壁纸才能展现出动画效果。而通过设置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