动态壁纸探究

 

随着三星 Oscar的上市,流畅的操作,华丽的界面,OPhone 2.0的不俗表现不禁让人眼前一亮。作为OPhone 2.0一个新特性,动态壁纸(Live Wallpapers)为用户带来了更炫体验。本文主要通过一个完整的时间壁纸(TimeWall)为大家介绍如何开发 Live Wallpapers。还没开发环境?赶紧去下载OPhone SDK 2.0吧!


1、 Live Wallpapers是什么?
在oscar上有一个动态壁纸叫“天空草地”,用过一段时间,可以发现,随着时间的变化,壁纸的天空就会由蓝蓝青天变成繁星满天。看看效果:

(图)OPhone动态壁纸探究

为什么壁纸还有这么神奇的变化,这中间到底是什么在起作用?其实,一个Live Wallpaper就是一个apk!也就是说,动态壁纸的实质是一个apk在后台不断地重绘壁纸,所以我们可以让小草长高,小鸟飞翔。

来看一下AndoridManifest.xml:

Xhtml代码 复制代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!--   
  3. /*   
  4. **   
  5. ** Copyright 2009, The Android Open Source Project   
  6. **   
  7. ** Licensed under the Apache License, Version 2.0 (the "License");   
  8. ** you may not use this file except in compliance with the License.   
  9. ** You may obtain a copy of the License at   
  10. **   
  11. **     http://www.apache.org/licenses/LICENSE-2.0   
  12. **   
  13. ** Unless required by applicable law or agreed to in writing, software   
  14. ** distributed under the License is distributed on an "AS IS" BASIS,   
  15. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   
  16. ** See the License for the specific language governing permissions and   
  17. ** limitations under the License.   
  18. */   
  19. -->  
  20. <manifest  
  21.     xmlns:android="http://schemas.android.com/apk/res/android"  
  22.     package="com.example.android.livecubes">  
  23.     <uses-sdk android:minSdkVersion="7" />  
  24.     <uses-feature android:name="android.software.live_wallpaper" />  
  25.     <application  
  26.         android:label="@string/wallpapers"  
  27.         android:icon="@drawable/ic_launcher_wallpaper">  
  28.         <service  
  29.             android:label="@string/wallpaper_cube1"  
  30.             android:name=".cube1.CubeWallpaper1"  
  31.             android:permission="android.permission.BIND_WALLPAPER">  
  32.             <intent-filter>  
  33.                 <action android:name="android.service.wallpaper.WallpaperService" />  
  34.             </intent-filter>  
  35.             <meta-data android:name="android.service.wallpaper" android:resource="@xml/cube1" />  
  36.         </service>  
  37.         <service  
  38.             android:label="@string/wallpaper_cube2"  
  39.             android:name=".cube2.CubeWallpaper2"  
  40.             android:permission="android.permission.BIND_WALLPAPER">  
  41.             <intent-filter>  
  42.                 <action android:name="android.service.wallpaper.WallpaperService" />  
  43.             </intent-filter>  
  44.             <meta-data android:name="android.service.wallpaper" android:resource="@xml/cube2" />  
  45.         </service>  
  46.         <activity  
  47.             android:label="@string/cube2_settings"  
  48.             android:name=".cube2.CubeWallpaper2Settings"  
  49.             android:theme="@android:style/Theme.Light.WallpaperSettings"  
  50.             android:exported="true">  
  51.         </activity>  
  52.     <!--android:exported 是否可被其他程序调用  android:permission="android.permission.BIND_WALLPAPER" 桌面服务绑定设置-->  
  53.     </application>  
  54. </manifest>  

原来如此简单,动态壁纸仅仅有一个service就够了。其中
Android :permission="android.permission.BIND_WALLPAPER"

是让该service有能设置为壁纸的权限,没有的话该壁纸只能被预览。
<uses-sdk android:minSdkVersion="7" />

告诉我们,如果你想开发一个live wallpaper,必须是OPhone 2.0或者更高的版本。当然这也需要手机硬件的支持。

2、怎样实现WallpaperService?
WallpaperService与其他的service唯一的不同就是,你必须要增加一个方法onCreateEngine(),它会返回一个 WallpaperService.Engine,这个engine才是负责绘制壁纸以及响应与用户交互事件的核心部件。这个service代码结构如下:

Java代码 复制代码
  1. /*  
  2.  * Copyright (C) 2009 The Android Open Source Project  
  3.  *  
  4.  * Licensed under the Apache License, Version 2.0 (the "License");  
  5.  * you may not use this file except in compliance with the License.  
  6.  * You may obtain a copy of the License at  
  7.  *  
  8.  *      http://www.apache.org/licenses/LICENSE-2.0  
  9.  *  
  10.  * Unless required by applicable law or agreed to in writing, software  
  11.  * distributed under the License is distributed on an "AS IS" BASIS,  
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13.  * See the License for the specific language governing permissions and  
  14.  * limitations under the License.  
  15.  */  
  16. package com.example.android.livecubes.cube2;   
  17. import android.content.SharedPreferences;   
  18. import android.graphics.Canvas;   
  19. import android.graphics.Paint;   
  20. import android.graphics.Rect;   
  21. import android.os.Handler;   
  22. import android.os.SystemClock;   
  23. import android.service.wallpaper.WallpaperService;   
  24. import android.view.MotionEvent;   
  25. import android.view.SurfaceHolder;   
  26. /*  
  27.  * This animated wallpaper draws a rotating wireframe shape. It is similar to  
  28.  * example #1, but has a choice of 2 shapes, which are user selectable and  
  29.  * defined in resources instead of in code.  
  30.  */  
  31. public class CubeWallpaper2 extends WallpaperService {   
  32.     public static final String SHARED_PREFS_NAME="cube2settings";   
  33.     static class ThreeDPoint {   
  34.         float x;   
  35.         float y;   
  36.         float z;   
  37.     }   
  38.     static class ThreeDLine {   
  39.         int startPoint;   
  40.         int endPoint;   
  41.     }   
  42.     @Override  
  43.     public void onCreate() {   
  44.         super.onCreate();   
  45.     }   
  46.     @Override  
  47.     public void onDestroy() {   
  48.         super.onDestroy();   
  49.     }   
  50.     @Override  
  51.     public Engine onCreateEngine() {   
  52.         return new CubeEngine();   
  53.     }   
  54.     class CubeEngine extends Engine    
  55.         implements SharedPreferences.OnSharedPreferenceChangeListener {   
  56.         private final Handler mHandler = new Handler();   
  57.         ThreeDPoint [] mOriginalPoints;   
  58.         ThreeDPoint [] mRotatedPoints;   
  59.         ThreeDLine [] mLines;   
  60.         private final Paint mPaint = new Paint();   
  61.         private float mOffset;   
  62.         private float mTouchX = -1;   
  63.         private float mTouchY = -1;   
  64.         private long mStartTime;   
  65.         private float mCenterX;   
  66.         private float mCenterY;   
  67.         private final Runnable mDrawCube = new Runnable() {   
  68.             public void run() {   
  69.                 drawFrame();   
  70.             }   
  71.         };   
  72.         private boolean mVisible;   
  73.         private SharedPreferences mPrefs;   
  74.         CubeEngine() {   
  75.             // Create a Paint to draw the lines for our cube   
  76.             final Paint paint = mPaint;   
  77.             paint.setColor(0xffffffff);   
  78.             paint.setAntiAlias(true);   
  79.             paint.setStrokeWidth(2);   
  80.             paint.setStrokeCap(Paint.Cap.ROUND);   
  81.             paint.setStyle(Paint.Style.STROKE);   
  82.             mStartTime = SystemClock.elapsedRealtime();   
  83.             mPrefs = CubeWallpaper2.this.getSharedPreferences(SHARED_PREFS_NAME, 0);   
  84.             mPrefs.registerOnSharedPreferenceChangeListener(this);   
  85.             onSharedPreferenceChanged(mPrefs, null);   
  86.         }   
  87.         public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {   
  88.             String shape = prefs.getString("cube2_shape""cube");   
  89.             // read the 3D model from the resource   
  90.             readModel(shape);   
  91.         }   
  92.         private void readModel(String prefix) {   
  93.             // Read the model definition in from a resource.   
  94.             // get the resource identifiers for the arrays for the selected shape   
  95.             int pid = getResources().getIdentifier(prefix + "points""array", getPackageName());   
  96.             int lid = getResources().getIdentifier(prefix + "lines""array", getPackageName());   
  97.             String [] p = getResources().getStringArray(pid);   
  98.             int numpoints = p.length;   
  99.             mOriginalPoints = new ThreeDPoint[numpoints];   
  100.             mRotatedPoints = new ThreeDPoint[numpoints];   
  101.             for (int i = 0; i < numpoints; i++) {   
  102.                 mOriginalPoints[i] = new ThreeDPoint();   
  103.                 mRotatedPoints[i] = new ThreeDPoint();   
  104.                 String [] coord = p[i].split(" ");   
  105.                 mOriginalPoints[i].x = Float.valueOf(coord[0]);   
  106.                 mOriginalPoints[i].y = Float.valueOf(coord[1]);   
  107.                 mOriginalPoints[i].z = Float.valueOf(coord[2]);   
  108.             }   
  109.             String [] l = getResources().getStringArray(lid);   
  110.             int numlines = l.length;   
  111.             mLines = new ThreeDLine[numlines];   
  112.             for (int i = 0; i < numlines; i++) {   
  113.                 mLines[i] = new ThreeDLine();   
  114.                 String [] idx = l[i].split(" ");   
  115.                 mLines[i].startPoint = Integer.valueOf(idx[0]);   
  116.                 mLines[i].endPoint = Integer.valueOf(idx[1]);   
  117.             }   
  118.         }   
  119.         @Override  
  120.         public void onCreate(SurfaceHolder surfaceHolder) {   
  121.             super.onCreate(surfaceHolder);   
  122.             setTouchEventsEnabled(true);   
  123.         }   
  124.         @Override  
  125.         public void onDestroy() {   
  126.             super.onDestroy();   
  127.             mHandler.removeCallbacks(mDrawCube);   
  128.         }   
  129.         @Override  
  130.         public void onVisibilityChanged(boolean visible) {   
  131.             mVisible = visible;   
  132.             if (visible) {   
  133.                 drawFrame();   
  134.             } else {   
  135.                 mHandler.removeCallbacks(mDrawCube);   
  136.             }   
  137.         }   
  138.         @Override  
  139.         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {   
  140.             super.onSurfaceChanged(holder, format, width, height);   
  141.             // store the center of the surface, so we can draw the cube in the right spot   
  142.             mCenterX = width/2.0f;   
  143.             mCenterY = height/2.0f;   
  144.             drawFrame();   
  145.         }   
  146.         @Override  
  147.         public void onSurfaceCreated(SurfaceHolder holder) {   
  148.             super.onSurfaceCreated(holder);   
  149.         }   
  150.         @Override  
  151.         public void onSurfaceDestroyed(SurfaceHolder holder) {   
  152.             super.onSurfaceDestroyed(holder);   
  153.             mVisible = false;   
  154.             mHandler.removeCallbacks(mDrawCube);   
  155.         }   
  156.         @Override  
  157.         public void onOffsetsChanged(float xOffset, float yOffset,   
  158.                 float xStep, float yStep, int xPixels, int yPixels) {   
  159.             mOffset = xOffset;   
  160.             drawFrame();   
  161.         }   
  162.         /*  
  163.          * Store the position of the touch event so we can use it for drawing later  
  164.          */  
  165.         @Override  
  166.         public void onTouchEvent(MotionEvent event) {   
  167.             if (event.getAction() == MotionEvent.ACTION_MOVE) {   
  168.                 mTouchX = event.getX();   
  169.                 mTouchY = event.getY();   
  170.             } else {   
  171.                 mTouchX = -1;   
  172.                 mTouchY = -1;   
  173.             }   
  174.             super.onTouchEvent(event);   
  175.         }   
  176.         /*  
  177.          * Draw one frame of the animation. This method gets called repeatedly  
  178.          * by posting a delayed Runnable. You can do any drawing you want in  
  179.          * here. This example draws a wireframe cube.  
  180.          */  
  181.         void drawFrame() {   
  182.             final SurfaceHolder holder = getSurfaceHolder();   
  183.             final Rect frame = holder.getSurfaceFrame();   
  184.             final int width = frame.width();   
  185.             final int height = frame.height();   
  186.             Canvas c = null;   
  187.             try {   
  188.                 c = holder.lockCanvas();   
  189.                 if (c != null) {   
  190.                     // draw something   
  191.                     drawCube(c);   
  192.                     drawTouchPoint(c);   
  193.                 }   
  194.             } finally {   
  195.                 if (c != null) holder.unlockCanvasAndPost(c);   
  196.             }   
  197.             mHandler.removeCallbacks(mDrawCube);   
  198.             if (mVisible) {   
  199.                 mHandler.postDelayed(mDrawCube, 1000 / 25);   
  200.             }   
  201.         }   
  202.         void drawCube(Canvas c) {   
  203.             c.save();   
  204.             c.translate(mCenterX, mCenterY);   
  205.             c.drawColor(0xff000000);   
  206.             long now = SystemClock.elapsedRealtime();   
  207.             float xrot = ((float)(now - mStartTime)) / 1000;   
  208.             float yrot = (0.5f - mOffset) * 2.0f;   
  209.             rotateAndProjectPoints(xrot, yrot);   
  210.             drawLines(c);   
  211.             c.restore();   
  212.         }   
  213.         void rotateAndProjectPoints(float xrot, float yrot) {   
  214.             int n = mOriginalPoints.length;   
  215.             for (int i = 0; i < n; i++) {   
  216.                 // rotation around X-axis   
  217.                 ThreeDPoint p = mOriginalPoints[i];   
  218.                 float x = p.x;   
  219.                 float y = p.y;   
  220.                 float z = p.z;   
  221.                 float newy = (float)(Math.sin(xrot) * z + Math.cos(xrot) * y);   
  222.                 float newz = (float)(Math.cos(xrot) * z - Math.sin(xrot) * y);   
  223.                 // rotation around Y-axis   
  224.                 float newx = (float)(Math.sin(yrot) * newz + Math.cos(yrot) * x);   
  225.                 newz = (float)(Math.cos(yrot) * newz - Math.sin(yrot) * x);   
  226.                 // 3D-to-2D projection   
  227.                 float screenX = newx / (4 - newz / 400);   
  228.                 float screenY = newy / (4 - newz / 400);   
  229.                 mRotatedPoints[i].x = screenX;   
  230.                 mRotatedPoints[i].y = screenY;   
  231.                 mRotatedPoints[i].z = 0;   
  232.             }   
  233.         }   
  234.         void drawLines(Canvas c) {   
  235.             int n = mLines.length;   
  236.             for (int i = 0; i < n; i++) {   
  237.                 ThreeDLine l = mLines[i];   
  238.                 ThreeDPoint start = mRotatedPoints[l.startPoint];   
  239.                 ThreeDPoint end = mRotatedPoints[l.endPoint];   
  240.                 c.drawLine(start.x, start.y, end.x, end.y, mPaint);   
  241.             }   
  242.         }   
  243.         void drawTouchPoint(Canvas c) {   
  244.             if (mTouchX >=0 && mTouchY >= 0) {   
  245.                 c.drawCircle(mTouchX, mTouchY, 80, mPaint);   
  246.             }   
  247.         }   
  248.     }   
  249. }  

Java代码 复制代码
  1. /*  
  2.  * Copyright (C) 2009 Google Inc.  
  3.  *   
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not  
  5.  * use this file except in compliance with the License. You may obtain a copy of  
  6.  * the License at  
  7.  *   
  8.  * http://www.apache.org/licenses/LICENSE-2.0  
  9.  *   
  10.  * Unless required by applicable law or agreed to in writing, software  
  11.  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT  
  12.  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the  
  13.  * License for the specific language governing permissions and limitations under  
  14.  * the License.  
  15.  */  
  16. package com.example.android.livecubes.cube2;   
  17. import com.example.android.livecubes.R;   
  18. import android.content.SharedPreferences;   
  19. import android.os.Bundle;   
  20. import android.preference.PreferenceActivity;   
  21. public class CubeWallpaper2Settings extends PreferenceActivity   
  22.     implements SharedPreferences.OnSharedPreferenceChangeListener {   
  23.     @Override  
  24.     protected void onCreate(Bundle icicle) {   
  25.         super.onCreate(icicle);   
  26.         getPreferenceManager().setSharedPreferencesName(   
  27.                 CubeWallpaper2.SHARED_PREFS_NAME);   
  28.         addPreferencesFromResource(R.xml.cube2_settings);   
  29.         getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(   
  30.                 this);   
  31.     }   
  32.     @Override  
  33.     protected void onResume() {   
  34.         super.onResume();   
  35.     }   
  36.     @Override  
  37.     protected void onDestroy() {   
  38.         getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(   
  39.                 this);   
  40.         super.onDestroy();   
  41.     }   
  42.     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,   
  43.             String key) {   
  44.     }   
  45. }  

类TimeEngine才是处理壁纸的核心类,我们会在类TimeEngine中加上自己的逻辑以完成壁纸的绘制、变化以及销毁。Engine的生命周期与大多数OPhone应用程序 组件,比如activity类似,都是从onCreate()开始,在销毁时调用onDestory()方法。不同的是WallpaperService会提供一个surface用来绘制壁纸,所以在生命周期中多一个onSurfaceCreated与onSurfaceDestroyed的过程。下面是一个最简生命周期:

(图)OPhone动态壁纸探究

也就是说只要我们实现上面四个方法,一个基本的LiveWallpaper就可以完成了。让我们逐个看一下这几个方法的实现。

nCreate方法里,我们
setTouchEventsEnabled(true);

作用是使壁纸能响应touch event,默认是false。TimeWall会在用户点击屏幕的时候画一个十字架,所以我们需要设置其为true。

可以看到我们在这四个方法里面做的事情非常简单,就是在create时候发一个message,执行画面的绘制,在destory时remove这个消息。

从上面可以看出,动态壁纸实际上就是不断刷新的静态壁纸,越华丽越流畅,CPU就消耗越大,对于现在的本来电量就不怎么地的智能机来说,耗电也是很可观的。但是偶尔向朋友们炫一下还是绝对可行的。drawTime()与 drawCross()的内容可以由家自己实现,在TimeWall里,它们比较简单。drawTime()是计算下一处Time String应该移动到的坐标,以及画出这个String。drawCross()的作用是在用户触发onTouchEvent时画一个十字架。因为 TimeWall比较简单,如果大家自己实现的画图比较复杂,可以另外开启一个线程来刷新UI ,否则有可能主线程被阻塞掉。

cube2.xml

Java代码 复制代码
  1. <wallpaper xmlns:android="http://schemas.android.com/apk/res/android"  
  2.         android:settingsActivity="com.example.android.livecubes.cube2.CubeWallpaper2Settings"  
  3. />  

cube2_setting

Xhtml代码 复制代码
  1. <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"  
  2.         android:title="@string/cube2_settings"  
  3.         android:key="cube2wallpaper_settings">  
  4.     <ListPreference  
  5.             android:key="cube2_shape"  
  6.             android:title="@string/cube2_settings_title"  
  7.             android:summary="@string/cube2_settings_summary"  
  8.             android:entries="@array/cube2_shapenames"  
  9.             android:entryValues="@array/cube2_shapeprefix" />  
  10. </PreferenceScreen>  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值