壁纸窗口和输入法窗口一样,都是一种特殊类型的窗口,一般情况下,系统中都会有一个壁纸窗口,通常位于最底层。

本文主要基于Android4.4说明,如何为应用程序设置壁纸以及设置壁纸的基本流程。

主要包含以下几个部分对壁纸进行介绍:

1.  壁纸显示的基本原理

2.  如何让一个Activity窗口显示壁纸

3.. 壁纸的设定流程(包含静态壁纸和动态壁纸)

4.  如何制作壁纸包。

1.  壁纸显示的基本原理

如果一个Activity窗口能够显示壁纸,WindowManagerService会将壁纸窗口移动到这个Activity窗口的下面。

壁纸窗口对应一个WallpaperService,WallpaperService内部有一个Engine,Engine负责壁纸窗口的创建和显示内容的描画。

对于静态壁纸,系统中有一个ImageWallpaper(继承自WallpaperService) 和DrawableEngine(继承自Engine)。

动态壁纸的Service和Engine需要新规实现。它们之间的关系如下图所示。


Android 13添加默认壁纸 安卓怎么设置壁纸图片_xml

WallpaperService是由WallpaperManagerService启动(通过绑定服务的方式)并管理的。WallpaperManagerService是一个系统Service,在系统启动时,由系统进程SystemServer创建。

在设定壁纸时,应用程序通过WallpaperManager通过AIDl与WallpaperManagerService进行通信。

静态壁纸的设定和动态壁纸的设定有一些不同。

1.静态壁纸实际上就是图片,设定完成后,会将壁纸图片保存为一个文件,WallpaperManagerService会监听这个文件,如果内容发生变化就会绑定Service。

2.动态壁纸实际上一个 APK,不会存在壁纸文件,设定完成后,直接绑定Service。


服务绑定后,到壁纸窗口的描画过程如下图所示。

Android 13添加默认壁纸 安卓怎么设置壁纸图片_android_02

2. 如何让一个Activity窗口显示壁纸

要让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窗口显示壁纸,实际上这个主题包含了上面的两个条件。

<style name="Theme.Holo.Wallpaper">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:colorBackgroundCacheHint">@null</item>
        <item name="android:windowShowWallpaper">true</item>
    </style>

3. 壁纸的设定流程

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保存着资源名,如下所示:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
 <wp width="884" height="800" name="res:com.android.launcher:drawable/wallpaper_01" />而直接通过cropimage的方式时,xml文件中的name信息为空。
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
 <wp width="884" height="800" name="" />对于动态壁纸的情况,会在xml文件中追加component(动态壁纸service类的信息),如下:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
 <wp width="884" height="800" name="res:com.android.launcher:drawable/wallpaper_01" component="com.example.android.livecubes/.cube1.CubeWallpaper1" />

动态壁纸不会保存wallpaper文件,如果设定了动态壁纸,wallpaper文件保存的是上次设定的静态壁纸的图片。如果是通过gallery中设置的壁纸(crop的情况),保存的壁纸数据是crop之后的图片。


注意:实现壁纸设定程序,要在应用程序中加入下列权限: <uses-permission android:name="android.permission.SET_WALLPAPER"/>

4. 如何制作壁纸包

静态壁纸本身是图片,不需要壁纸包。

动态壁纸需要开发动态壁纸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信息。