• 周五. 4 月 24th, 2026

物嫩软件资讯网

软件资讯来物嫩

动态壁纸探究

admin@wunen

5 月 10, 2025

随着

三星

Oscar的上市,流畅的操作,华丽的界面,

OPhone

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

SDK

2.0吧!


1、 Live Wallpapers是什么?

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

为什么壁纸还有这么神奇的变化,这中间到底是什么在起作用?其实,一个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的过程。下面是一个最简生命周期:

也就是说只要我们实现上面四个方法,一个基本的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


    >

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注