壁纸窗口和输入法窗口一样,都是一种特殊类型的窗口,一般情况下,系统中都会有一个壁纸窗口,通常位于最底层。
本文主要基于Android4.4说明,如何为应用程序设置壁纸以及设置壁纸的基本流程。
主要包含以下几个部分对壁纸进行介绍:
1. 壁纸显示的基本原理
2. 如何让一个Activity窗口显示壁纸
3.. 壁纸的设定流程(包含静态壁纸和动态壁纸)
4. 如何制作壁纸包。
如果一个Activity窗口能够显示壁纸,WindowManagerService会将壁纸窗口移动到这个Activity窗口的下面。
壁纸窗口对应一个WallpaperService,WallpaperService内部有一个Engine,Engine负责壁纸窗口的创建和显示内容的描画。
对于静态壁纸,系统中有一个ImageWallpaper(继承自WallpaperService) 和DrawableEngine(继承自Engine)。
动态壁纸的Service和Engine需要新规实现。它们之间的关系如下图所示。
WallpaperService是由WallpaperManagerService启动(通过绑定服务的方式)并管理的。WallpaperManagerService是一个系统Service,在系统启动时,由系统进程SystemServer创建。
在设定壁纸时,应用程序通过WallpaperManager通过AIDl与WallpaperManagerService进行通信。
静态壁纸的设定和动态壁纸的设定有一些不同。
1.静态壁纸实际上就是图片,设定完成后,会将壁纸图片保存为一个文件,WallpaperManagerService会监听这个文件,如果内容发生变化就会绑定Service。
2.动态壁纸实际上一个 APK,不会存在壁纸文件,设定完成后,直接绑定Service。
服务绑定后,到壁纸窗口的描画过程如下图所示。要让Activity窗口显示壁纸,必须要满足两个条件:
(1)窗口是透过的,比如透明或半透明
(2)窗口属性中的WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER位设置为1
可以 通过getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);就行设置
另外通过theme属性android:theme="@android:style/Theme.Holo.Wallpaper"就可以让Activity窗口显示壁纸,实际上这个主题包含了上面的两个条件。
1. 选择Live Wallpapers
LiveWallpaperActivity-〉 CubeWallpaper1(选择的动态壁纸,此时选择是example中的cube)-〉LiveWallpaperPreview
选择set wallpaper时,调用LiveWallpaperPreview的setLiveWallpaper()方法
public void setLiveWallpaper(View v) {
try {
mWallpaperManager.getIWallpaperManager().setWallpaperComponent(
mWallpaperIntent.getComponent());
mWallpaperManager.setWallpaperOffsetSteps(0.5f, 0.0f);
mWallpaperManager.setWallpaperOffsets(v.getRootView().getWindowToken(), 0.5f, 0.0f);
setResult(RESULT_OK);
} catch (RemoteException e) {
// do nothing
} catch (RuntimeException e) {
Log.w(LOG_TAG, "Failure setting wallpaper", e);
}
finish();
}
2. 从Pictures中选择壁纸
CropImage在剪切完图片并保存时调用WallpaperManager.getInstance(this).setBitmap(croppedImage);然后直接返回。
3. 选择系统的WallPapers(Launcher中自带的静态图片)
进入Launcher2.WallpaperChooser这个Activity,并调用它的selectWallpaper方法。
private void selectWallpaper(int position) {
try {
WallpaperManager wpm = (WallpaperManager) getActivity().getSystemService(
Context.WALLPAPER_SERVICE);
wpm.setResource(mImages.get(position));
Activity activity = getActivity();
activity.setResult(Activity.RESULT_OK);
activity.finish();
} catch (IOException e) {
Log.e(TAG, "Failed to set wallpaper: " + e);
}
}
这里设定的是resource id。
小结:从以上三种情况来看,最终调用的WallpaperManager的三个不同的方法,分别是
(1). setWallpaperComponent
(2). setBitmap
(3). setResource
另外还有一个接口是setStream(InputStream data), data的来源可以是一个uri或者一个文件。
当然也可以使用ContextImpl的setWallpaper方法。
静态wallpaper图片保存在data/system/users/userid/wallpaper文件中(通常userid=0,这种情况下,壁纸路径为data/system/users/0/wallpaper)。
wallpaperinfo的信息保存在data/system/users/0/wallpaper_info.xml
当通过资源的方式设置壁纸时,wallpaper_info.xml保存着资源名,如下所示:
而直接通过cropimage的方式时,xml文件中的name信息为空。
对于动态壁纸的情况,会在xml文件中追加component(动态壁纸service类的信息),如下:
动态壁纸不会保存wallpaper文件,如果设定了动态壁纸,wallpaper文件保存的是上次设定的静态壁纸的图片。如果是通过gallery中设置的壁纸(crop的情况),保存的壁纸数据是crop之后的图片。
注意:实现壁纸设定程序,要在应用程序中加入下列权限:
静态壁纸本身是图片,不需要壁纸包。
动态壁纸需要开发动态壁纸APK,开发语言可以选用Google的RenderScript语言。
需要继承WallpaperService和壁纸引擎Engine,另外壁纸程序本身有一个Service,需要在manifest文件中加入
<service
Android:label="@string/wallpaper_magicsmoke"
Android:name="LiveWallpaperService"
Android:permission="android.permission.BIND_WALLPAPER">
<intent-filter>
<action Android:name="android.service.wallpaper.WallpaperService" />
intent-filter>
<meta-data Android:name= "android.service.wallpaper" android:resource="@xml/livewallpaper" />
service>
xml/livewallpaper可以包含动态壁纸的thumbnail信息。