Android动态壁纸的制作教程

来源:互联网 发布:微波炉哪款好用 知乎 编辑:程序博客网 时间:2024/06/05 18:21

动态壁纸是在Android 2.1新增的一个功能。动态壁纸可以添加到Android的桌面,具有交互式的动画背景效果。在本教程中,我们将教会你如何去制作一个交互式的动态壁纸。

动态壁纸是一个Android应用程序,包括一个服务(WallpaperService)。该服务必须包括一个引擎(WallpaperService.Engine)。该引擎是连接用户、桌面、系统之间的桥梁。它也可以绘制桌面壁纸。

首先,必须由内在的Engine类创建一个WallpaperService类。该服务必须在AndroidManifest.xml中声明为"android.service.wallpaper.WallpaperService",这样它才会作为动态壁纸被手机识别。而且还要在服务配置中附加"android.permission.BIND_WALLPAPER"的权限许可: 

01<service
02    android:name="LiveWallpaperService"
03    android:enabled="true"
04    android:icon="@drawable/icon"
05    android:label="@string/app_name"
06    android:permission="android.permission.BIND_WALLPAPER">
07    <intent-filter android:priority="1" >
08        <action android:name="android.service.wallpaper.WallpaperService" />
09    </intent-filter>
10    <meta-data
11      android:name="android.service.wallpaper"
12      android:resource="@xml/wallpaper" />
13</service>

创建一个XML文件,放置在应用程序目录下的/res/xml/中。它用来描述你的动态壁纸。 

1<?xml version="1.0" encoding="UTF-8"?>
2<wallpaper
3    xmlns:android="http://schemas.android.com/apk/res/android" 
4    android:thumbnail="@drawable/thumbnail"
5    android:description="@string/description"
6    android:settingsActivity="PreferenceActivity"/>

再创建一个xml的属性文件 attrs.xml ,代码如下: 

01<declare-styleable name="Wallpaper">
02    <!-- Component name of an activity that allows the user to modify
03         the current settings for this wallpaper. -->
04    <attr name="settingsActivity" />
05  
06    <!-- Reference to a the wallpaper's thumbnail bitmap. -->
07    <attr name="thumbnail" format="reference" />
08  
09    <!-- Name of the author of this component, e.g. Google. -->
10    <attr name="author" format="reference" />
11  
12    <!-- Short description of the component's purpose or behavior. -->
13    <attr name="description" />
14</declare-styleable>

动态壁纸的服务代码如下: 

001package net.androgames.blog.sample.livewallpaper;
002  
003import android.content.SharedPreferences;
004import android.service.wallpaper.WallpaperService;
005import android.view.MotionEvent;
006import android.view.SurfaceHolder;
007  
008/**
009 * Android Live Wallpaper Archetype
010 * @author antoine vianey
011 * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
012 */
013public class LiveWallpaperService extends WallpaperService {
014  
015    @Override
016    public Engine onCreateEngine() {
017        return new SampleEngine();
018    }
019  
020    @Override
021    public void onCreate() {
022        super.onCreate();
023    }
024  
025    @Override
026    public void onDestroy() {
027        super.onDestroy();
028    }
029  
030    public class SampleEngine extends Engine {
031  
032        private LiveWallpaperPainting painting;
033  
034        SampleEngine() {
035            SurfaceHolder holder = getSurfaceHolder();
036            painting = new LiveWallpaperPainting(holder,
037                getApplicationContext());
038        }
039  
040        @Override
041        public void onCreate(SurfaceHolder surfaceHolder) {
042            super.onCreate(surfaceHolder);
043            // register listeners and callbacks here
044            setTouchEventsEnabled(true);
045        }
046  
047        @Override
048        public void onDestroy() {
049            super.onDestroy();
050            // remove listeners and callbacks here
051            painting.stopPainting();
052        }
053  
054        @Override
055        public void onVisibilityChanged(boolean visible) {
056            if (visible) {
057                // register listeners and callbacks here
058                painting.resumePainting();
059            else {
060                // remove listeners and callbacks here
061                painting.pausePainting();
062            }
063        }
064  
065        @Override
066        public void onSurfaceChanged(SurfaceHolder holder, int format,
067                int width, int height) {
068            super.onSurfaceChanged(holder, format, width, height);
069            painting.setSurfaceSize(width, height);
070        }
071  
072        @Override
073        public void onSurfaceCreated(SurfaceHolder holder) {
074            super.onSurfaceCreated(holder);
075            // start painting
076            painting.start();
077        }
078  
079        @Override
080        public void onSurfaceDestroyed(SurfaceHolder holder) {
081            super.onSurfaceDestroyed(holder);
082            boolean retry = true;
083            painting.stopPainting();
084            while (retry) {
085                try {
086                    painting.join();
087                    retry = false;
088                catch (InterruptedException e) {}
089            }
090        }
091  
092        @Override
093        public void onOffsetsChanged(float xOffset, float yOffset,
094                float xStep, float yStep, int xPixels, int yPixels) {
095        }
096  
097        @Override
098        public void onTouchEvent(MotionEvent event) {
099            super.onTouchEvent(event);
100            painting.doTouchEvent(event);
101        }
102  
103    }
104  
105}

当壁纸的显示、状态或大小变化是,会调用Engine的onCreateonDestroyonVisibilityChanged,onSurfaceChangedonSurfaceCreated 和 onSurfaceDestroyed方法。有了这些方法,动态壁纸才能展现出动画效果。而通过设置setTouchEventsEnabled(true),并且调用onTouchEvent(MotionEvent event)方法,来激活触摸事件。

我们在绘画墙纸的时候,也会使用一个单独的绘画线程: 

001package net.androgames.blog.sample.livewallpaper;
002  
003import android.content.Context;
004import android.graphics.Canvas;
005import android.view.MotionEvent;
006import android.view.SurfaceHolder;
007  
008/**
009 * Android Live Wallpaper painting thread Archetype
010 * @author antoine vianey
011 * GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
012 */
013public class LiveWallpaperPainting extends Thread {
014  
015    /** Reference to the View and the context */
016    private SurfaceHolder surfaceHolder;
017    private Context context;
018  
019    /** State */
020    private boolean wait;
021    private boolean run;
022  
023    /** Dimensions */
024    private int width;
025    private int height;
026  
027    /** Time tracking */
028    private long previousTime;
029    private long currentTime;
030  
031    public LiveWallpaperPainting(SurfaceHolder surfaceHolder,
032            Context context) {
033        // keep a reference of the context and the surface
034        // the context is needed if you want to inflate
035        // some resources from your livewallpaper .apk
036        this.surfaceHolder = surfaceHolder;
037        this.context = context;
038        // don't animate until surface is created and displayed
039        this.wait = true;
040    }
041  
042    /**
043     * Pauses the live wallpaper animation
044     */
045    public void pausePainting() {
046        this.wait = true;
047        synchronized(this) {
048            this.notify();
049        }
050    }
051  
052    /**
053     * Resume the live wallpaper animation
054     */
055    public void resumePainting() {
056        this.wait = false;
057        synchronized(this) {
058            this.notify();
059        }
060    }
061  
062    /**
063     * Stop the live wallpaper animation
064     */
065    public void stopPainting() {
066        this.run = false;
067        synchronized(this) {
068            this.notify();
069        }
070    }
071  
072    @Override
073    public void run() {
074        this.run = true;
075        Canvas c = null;
076        while (run) {
077            try {
078                c = this.surfaceHolder.lockCanvas(null);
079                synchronized (this.surfaceHolder) {
080                    currentTime = System.currentTimeMillis();
081                    updatePhysics();
082                    doDraw(c);
083                    previousTime = currentTime;
084                }
085            finally {
086                if (c != null) {
087                    this.surfaceHolder.unlockCanvasAndPost(c);
088                }
089            }
090            // pause if no need to animate
091            synchronized (this) {
092                if (wait) {
093                    try {
094                        wait();
095                    catch (Exception e) {}
096                }
097            }
098        }
099    }
100  
101    /**
102     * Invoke when the surface dimension change
103     */
104    public void setSurfaceSize(int width, int height) {
105        this.width = width;
106        this.height = height;
107        synchronized(this) {
108            this.notify();
109        }
110    }
111  
112    /**
113     * Invoke while the screen is touched
114     */
115    public void doTouchEvent(MotionEvent event) {
116        // handle the event here
117        // if there is something to animate
118        // then wake up
119        this.wait = false;
120        synchronized(this) {
121            notify();
122        }
123    }
124  
125    /**
126     * Do the actual drawing stuff
127     */
128    private void doDraw(Canvas canvas) {}
129  
130    /**
131     * Update the animation, sprites or whatever.
132     * If there is nothing to animate set the wait
133     * attribute of the thread to true
134     */
135    private void updatePhysics() {
136        // if nothing was updated :
137        // this.wait = true;
138    }
139  
140}

如果桌面壁纸是可见状态下,系统服务通知有新的东西,这个类会优先把它绘制在画布上。如果没有动画了,updatePhysics会通知线程去等待。通常SurfaceView在有两个画布交替绘制的时候,会在画布上绘制上一次......

如果要让你的动态墙纸有配置功能,只要创建一个PreferenceActivity,并将它在wallpaper.xml文件中声明。同时让SharedPreference对象可以找到你的配置选项。

教程就写到这里,如果还有什么不懂,你可以通过Eclipse来浏览完整的源代码:SampleLiveWallpaper。