通常手机屏幕的壁纸就是一张静态的图片,看上去已经挺不错的了,但它不会有变化。
让我们揭开它神秘的面纱。看看这些神奇的应用是如何做到的。
图一
创建我们的CS——LiveWallpaper项目
在这个例子中,我们将创造一个活生生的墙纸,使用OpenGL显示一个旋转的立方体。最后的程序运行结果就如图一所示。首先创建一个新的android项目,在其的向导wizard中使用这些值:
Project name: CS_Livewallpaper
Build Target: Android 2.2
Application name: Wallpaper
Package name: classroom.studio
Min SDK Version: 8
Activity的名字可以先空着。关闭Activity项左边的复选框。如下图二所示。
图二
为新项目中的androidmanifest.xml文件中添加新的属性。它看起来就像下面这样。
1
<
manifest
xmlns:android
="http://schemas.android.com/apk/res/android"
2
package
="classroom.studio"
3
android:versionCode
="1"
4
android:versionName
="1.0"
>
5
<
application
android:label
="@string/app_name"
>
6
<
service
android:name
=".Wallpaper"
7
android:label
="@string/service_name"
8
android:permission
="android.permission.BIND_WALLPAPER"
>
9
<
intent-filter
>
10
<
action
android:name
=
11
"android.service.wallpaper.WallpaperService"
/>
12
</
intent-filter
>
13
<
meta-data
android:name
="android.service.wallpaper"
14
android:resource
="@xml/wallpaper"
/>
15
</
service
>
16
</
application
>
17
<
uses-sdk
android:minSdkVersion
="8"
android:targetSdkVersion
="8"
/>
18
</
manifest
>
19
背景上并且对事件有响应的Android服务。 android:permission= attribute的意思是任何程序调用我们的服务都需要有一个指定的权限。 Android的Home程序已经具备了该权限,所以它会工作得很好。
< intent-filter >这个标签告诉Android它是个什么样的服务,并且< meta-data >标签让它知道在哪能找到关于wallpaper的额外信息。 android:resource被设为"@xml/wallpaper"这个值最好改成res/xml/wallpaper.xml 文件。以下便是该xml文件的内容。
1
<?
xml version="1.0" encoding="utf-8"
?>
2
<
wallpaper
xmlns:android
="http://schemas.android.com/apk/res/android"
3
android:author
="@+string/author"
4
android:description
="@string/description"
5
android:thumbnail
="@drawable/thumbnail"
6
/>
这个 wallpaper的metadata(元数据)指定了这个程序的作者的名字、该程序的介绍以及一个缩略图。所有这些分别存储在string.xml以及drawable文件中。他们将在使用者选择该使用哪个动态壁纸应用的时候以列表的形式显示出来。因此我们还需要创建一个string.xml文件。如下所示。
string.xml
我们可以AVD中看到这个文件实际所起的作用。如下图三所示
图三
同时我们也需要删除掉一个Layout中的文件 res/layout/main.xml。因为我们不会在这个程序中使用它。在我们具体了解了Android的一些服务之后我们将填写Wallpaper类。
书接前文,我们先要介绍Android的services。Android的一个与众不同的特征就是具有可在背景中运行程序的能力。为了将他们与前景的activities分开,Android把这些程序叫做services。
Service类由一个service的main java类继承.Services像 activities一样 也有个生命周期,但较之要简单得多。 在这个周期中,当我们第一次创建service类时, 需要调用onCreate()的方法。而当我们消除该service时,则调用了onDestroy()方法。
方法,以及下表一所示。
表一
公开的部分的方法
void | 如果之前通过onUnbind(Intent)方法已经通告解除所有联系后, 当新的clients再与service取得联系时,该方法被调用。 |
Boolean | 当所用Clients都已与一个service发出的特点界面断开联系时,调用该方法。 |
final void | stopSelf() 如果之前它开始过,则自己停止该service。 |
final void | startForeground(int id, Notification notification) 该方法使该service能够运行在前台(前景上), 并在这个状态下支持将ongoing的通知显示给用户。 |
final void | stopForeground(boolean removeNotification) 从前景中去除该service,并允许在内存低时杀死service。 |
想了解更多方法请参考: http://d.android.com/reference/android/app/Service.html
当然在我们这个例子中,不需要关心这些方法,因为他们都将由Service的子类WallpaperService 来处理。
我们的main类需要扩展成Wallpaper,就像下面这样。
1 package
classroom。studio;
2
import
android.service.wallpaper.WallpaperService;
3
public
class
Wallpaper
extends
WallpaperService {
4
private
class
MyEngine
extends
Engine {
5
//
从这里实现引擎。。。
6
}
7
@Override
8
public
Engine onCreateEngine() {
9
return
new
MyEngine();
10
}
11
}
12
我们现在要做的就是实现只有一行代码的onCreateEngine()方法。它的唯一目的就是要创建并返回另一个叫做MyEngine的类。
构建一个绘图引擎
因为这个MyEngine一定要是Wallpaper里的一个类,所以在类的封闭的大括号中声明了它。MyEngine 扩展了由Android提供的Engine类。以下的代码是带有所有要用到的方法的MyEngine 纲要。
wallpaper.java
1 private
class
MyEngine
extends
Engine {
2
@Override
3
public
void
onCreate(
final
SurfaceHolder holder) {
4
super
.onCreate(holder);
5
}
6
@Override
7
public
void
onDestroy() {
8
super
.onDestroy();
9
};
10
@Override
11
public
void
onSurfaceCreated(
final
SurfaceHolder holder) {
12
super
.onSurfaceCreated(holder);
13
}
14
@Override
15
public
void
onSurfaceDestroyed(
final
SurfaceHolder holder) {
16
super
.onSurfaceDestroyed(holder);
17
}@Override
18
public
void
onSurfaceChanged(
final
SurfaceHolder holder,
19
final
int
format,
final
int
width,
final
int
height) {
20
super
.onSurfaceChanged(holder, format, width, height);
21
}
22
@Override
23
public
void
onVisibilityChanged(
final
boolean
visible) {
24
super
.onVisibilityChanged(visible);
25
}
26
@Override
27
public
void
onOffsetsChanged(
final
float
xOffset,
28
final
float
yOffset,
final
float
xOffsetStep,
29
final
float
yOffsetStep,
final
int
xPixelOffset,
30
final
int
yPixelOffset) {
31
super
.onOffsetsChanged(xOffset, yOffset, xOffsetStep,
32
yOffsetStep, xPixelOffset, yPixelOffset);
33
}
34
}
需要注意的是每个方法应该总是调用其基类方法。
在Engine的整个生命周期中,Android会在特定的命令下调用这些方法。下面就是整个句子。
onCreate
onSurfaceCreated
onSurfaceChanged (1+ calls in any order)
onOffsetsChanged (0+ calls in any order)
onVisibilityChanged (0+ calls in any order)
onSurfaceDestroyed
onDestroy
接下来,我们将填写这些方法。不过我们还需要几个声明来防止编译出错,当我们编写代码时我们可以让Eclipse帮我们来创建这些语句(使用 Content Assist(Ctrl+空格)或是 Quick Fix(Ctrl+1) 亦或是通过Source > Organize Imports command ( Ctrl+Shift+O )实现批处理。但如果您愿意的话,也可以自己敲,下面是完整的列表。
1 import java.util.concurrent.ExecutorService;
2
import
java.util.concurrent.Executors;
3
import
javax.microedition.khronos.egl.EGL10;
4
import
javax.microedition.khronos.egl.EGL11;
5
import
javax.microedition.khronos.egl.EGLConfig;
6
import
javax.microedition.khronos.egl.EGLContext;
7
import
javax.microedition.khronos.egl.EGLDisplay;
8
import
javax.microedition.khronos.egl.EGLSurface;
9
import
javax.microedition.khronos.opengles.GL10;
10
import
android.service.wallpaper.WallpaperService;
11
import
android.view.SurfaceHolder;
下面让我们开始画我们的立方体吧。初始效果如图四所示。OpenGL ES
图四
先要了解一下何为OpenGL 以及OpenGL ES。
OpenGL的前世今生
1992年Silicon Graphics 开发出了OpenGL。它从此开始为程序员提供一个统一的平台让他们能够驾驭来自不同生产商的硬件。它的核心,OpenGL迎合了三维图形开发的一些经典概念如viewpoints和lighting并试图让开发者可以基本不去考虑错综复杂的硬件层就能实现3D的效果。您可以浏览http://www.opengl.org了解OpenGL的更多内容。
不过正是因为当初它是为工作站设计的,所以OpenGL对于一部手机来说实在是太大了。所以Google Android采用了OpenGL的一个子集——(OpenGL for Embedded Systems 针对嵌入式系统的OpenGL 简称OpenGL ES)。这个标准是由Intel、AMD、Nividia、Nokia、SONY、三星等行业巨头共同支持的 Khronos Group行业协会提出来的,包括Android、塞班和Iphone在内的主要手机平台都采用了这个库。虽然他们彼此之间还是有着细微的差别。您可以浏览http://www.khronos.org/opengles了解OpenGL ES的更多内容。
几乎每种计算机语言都有他自己与OpenGL ES相绑定的部分。当然JAVA也不例外。在JAVA Specification Request(JSR)239中定义了这部分绑定。
Android的OpenGL ES
OpenGL ES 1.0 基于完整的OpenGL 1.3,而 ES 1.1 基于 OpenGL 1.5。JSR 239 有两个版本:一个原有的1.0 和正式的1.0.1。而我们这次使用的便是OpenGL ES1.1
不过从Android2.2开始,OpenGL ES 2.0开始通过android.opengl 包得到支持。您也可以通过NDK来调用它。OpenGL ES 2.0 。不过还没有对应于OpenGL ES 2.0的JSR标准。如下图五所示
图五
现在让我们用OpenGL ES来建立我们自己的立方体模型吧。