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, 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

你可能感兴趣的:(android)