Android静态壁纸和动态壁纸的使用和理解

这两天在公众号上偶然看到一篇关于设置动态壁纸的文章,觉得蛮有意思的,学习了一下,以此文章记录一下怎样给手机设置静态壁纸和动态壁纸,设置壁纸的使用方法。

静态壁纸

Android中WallpaperManager系统服务用于管理壁纸的运行与切换,并通过WallpaperManager类向外界提供操作壁纸的接口。
设置静态壁纸是通过调用系统WallpaperManager的方法来实现的,
主要分为下面三种,同一种因不同的方法参数会对应多个方法:
(1)通过bitmap设置壁纸:setBitmap
(2)通过资源文件设置壁纸:setResource
(3)通过流设置壁纸:setStream
具体例子如下:

WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
    try {
      Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
      wallpaperManager.setBitmap(bitmap);
    } catch (IOException e) {
      e.printStackTrace();
    }

注意
(1)设置壁纸需要声明权限


(2)壁纸分系统壁纸和锁屏壁纸,但是API 24才能调用系统api设置。
比如:

WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
    try {
      if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
        wallpaperManager.setResource(R.raw.wallpaper, WallpaperManager.FLAG_SYSTEM);
      }
    } catch (IOException e) {
      e.printStackTrace();
    }

其中WallpaperManager.FLAG_SYSTEM表示系统壁纸,WallpaperManager.FLAG_LOCK表示锁屏壁纸。
既然可以设置壁纸,相对应的就可以清除壁纸,具体如下:

WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
    try {
      wallpaperManager.clear();
    } catch (IOException e) {
      e.printStackTrace();
    }

动态壁纸

动态壁纸是通过Android中的WallpaperService服务启动并开始进行壁纸的绘制工作,其中内部类Engine,实现了壁纸窗口的创建以及Surface的维护工作。
那怎样实现一个简单的动态壁纸呢?主要分为以下几步:
1.定义MyWallpaperService继承WallpaperService并实现onCreateEngine方法,返回自己的Engine实现类:

public class MyWallpaperService extends WallpaperService {
  //  实现动态壁纸必须要实现的抽象方法
  @Override
  public Engine onCreateEngine() {
    bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.star);
    return new MyEngine();
  }
}

其中在MyEngine中通过对生命周期方法的调用实现壁纸进行动画和绘制工作:

class MyEngine extends Engine{
    @Override
    public void onCreate(SurfaceHolder surfaceHolder) {
      super.onCreate(surfaceHolder);
      当引擎初始化时被调用,此时绘制surface还未被创建
      可以进行paint等的初始化工作
    }

    @Override
    public void onVisibilityChanged(boolean visible) {
      super.onVisibilityChanged(visible);
     当壁纸可见或隐藏时被调用,应该暂停你的动画,不再绘制任何东西,以节省CPU(比如说移除Runnable等)。
    }
    @Override
    public void onSurfaceCreated(SurfaceHolder holder) {
      super.onSurfaceCreated(holder);
      当surface被创建时被调用
    }

    @Override
    public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
      super.onSurfaceChanged(holder, format, width, height);
      当surface的图形结构发生变化(大小、格式)
    }

    @Override
    public void onSurfaceDestroyed(SurfaceHolder holder) {
      super.onSurfaceDestroyed(holder);
      当surface被销毁时被调用
    }

    @Override
    public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep,
        int xPixelOffset, int yPixelOffset) {
      super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixelOffset,
          yPixelOffset);
          当壁纸发生偏移变化,例如用户左右滑动主屏幕,这个可以用来创建一个平行滑动的效果,就是用户滑动时,壁纸同时发生偏移。
    }

    @Override
    public void onTouchEvent(MotionEvent event) {
      super.onTouchEvent(event);
      当显示壁纸时,用户跟主屏幕有触摸交互时被调用
    }

    @Override
    public void onDestroy() {
      super.onDestroy();
      引擎销毁时被调用。此方法调用之后,引擎变得不可用。
    }
  }

2.配置AndroidManifest.xml,声明上述中的自定义的service和一个SettingActivity:



  
  

  
  
    

      
        
      

      
    
  


注意:
(1)设置动态壁纸需要在MyWallpaperService中声明android:permission="android.permission.BIND_WALLPAPER权限
(2)必须声明,表示动态壁纸


(3)声明MyWallpaperService的action为android:name="android.service.wallpaper.WallpaperService,其中在meta-data中有一个resource在下一步中说明。
可能比较疑惑为什么要创建SettingActivity,关于为什么创建SettingActivity,在下述中说明。
3.在res/xml下创建xml文件,我这里命名为wallpaper.xml,为上述中service中的resource:



其中description(应用描述)settingsActivity(应用指定的壁纸设置页面),thumbnail(应用图标)三个属性自定义设置。
4.配置工作完成,启动壁纸服务:
通过下面这种方式打开壁纸设置页面设置动态壁纸:

Intent intent = new Intent(
        WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
    intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
        new ComponentName(this, MyWallpaperService.class));
    startActivity(intent);

这时候心里想MyWallpaperService是service,那能不能通过startService()启动壁纸,通过stopService()清除壁纸呢?
经测试,不能。这也是上述声明SettingActivity的原因。
**所以启动壁纸服务时,不能通过context的startService()方法来启动壁纸服务,需要通过启动系统的预览界面来间接启动服务。**但明明是一个service为什么这种方式不能成功设置壁纸呢,在下述原理中说明。
通过以上四个步骤我们就可以设置动态壁纸了,在运行app时点击设置动态壁纸进入到下面页面:
Android静态壁纸和动态壁纸的使用和理解_第1张图片
点击应用就可以应用此动态壁纸,但是点击设置按钮,因为这个例子中的的SettingActivity继承的是PreferenceActivity,进入到下面页面:
Android静态壁纸和动态壁纸的使用和理解_第2张图片
此页面内容为SettingActivity中设置的PreferenceScreen的xml文件,可以通过设置xml文件在此页面中对动态壁纸进行设置:

public class SettingActivity extends PreferenceActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    addPreferencesFromResource(R.xml.prefs);
    Preference circlePreferennce = getPreferenceScreen().findPreference("numberOfCircles");
    circlePreferennce.setOnPreferenceChangeListener(numberCheckListener);
  }

  Preference.OnPreferenceChangeListener numberCheckListener = new Preference.OnPreferenceChangeListener() {
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
      if(newValue != null && newValue.toString().length() > 0 && newValue.toString().matches("\\d*")){
        return true;
      }
      return false;
    }
  };
}

res/xml下的prefs.xml文件:




  

  

使用PreferenceActivity还可以做一些其他事情,关于PreferenceActivity的用法请参考这里
最后关于清除动态壁纸
可以使用上述中的清除静态壁纸的方法进行清除,或是如下利用WallpaperService的onDestroy方法:

if(myWallpaperService != null){
      myWallpaperService.onDestroy();
    }

除此之外,还可以使用视频,gif等设置动态壁纸,流程相同,只是在Engine中的不同的实现对壁纸进行不同的绘制工作,后续有时间学习一下,再补充。
学习demo点击这里。

原理

知道了怎样设置静态壁纸和动态壁纸之后引发一些思考:动态壁纸需要自定义继承WallpaperService,在onCreateEngine中返回Engine实现,为什么静态壁纸只需要调用WallpaperManager的api方法就可以呢?而动态壁纸继承WallpaperService也就说明了是一个service,为什么通过startService启动方式不能成功设置动态壁纸呢?系统是怎样通过WallpaperService进行通信的?此时需要了解动态壁纸和静态壁纸的原理了。
对于上述几个疑问:
1.通信:
动态壁纸和静态壁纸都是以一个Service的形式运行在系统后台,并在一个类型为TYPE_WALLPAPER的窗口上绘制内容。
WallpaperService是由WallpaperManagerService(系统Service,在系统启动时,由系统进程SystemServer创建)启动(通过绑定服务的方式)并管理的。
在设置壁纸时,应用程序通过WallpaperManager通过AIDl与WallpaperManagerService进行通信。
Android的动态墙纸虽然似乎是显示在Launcher的背景里,但其实这只是假象,动态墙纸和Launcher是完全不同的两个进程,只不过Launcher和动态墙纸的进程可以通过框架里的WallpaperManager进行进程间通信罢了,用户在Launcher桌面滑动、点击屏幕时有的动态墙纸能产生交互效果,实际上就是这个进程通信完成的。如果你通过代码将Launcher的背景设置为非透明的,比如以不透明的图片或者颜色作为背景,那么,你将看不到任何动态墙纸效果,当然,这样的话,静态墙纸你也不会看到了。
2.启动动态壁纸方法:
查看源码可知,启动动态壁纸是通过WallpaperManager.java的setWallpaperComponent这个方法实现的,如下:

@SystemApi
    @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT)
    public boolean setWallpaperComponent(ComponentName name) {
        return setWallpaperComponent(name, mContext.getUserId());
    }

setWallpaperComponent这个方法是系统api,不能直接被开发者调用,因此第三方应用程序是无法进行动态壁纸的设置的,只能通过上述中的方法启动动态壁纸。
3.静态壁纸:
ImageWallpaper表示静态壁纸,对应静态壁纸,系统中有一个ImageWallpaper(继承自WallpaperService) 和DrawableEngine(继承自Engine)。
查看源码深入理解壁纸原理请点击这里

参考

https://mp.weixin.qq.com/s/4_ER7XsObMjRfHGOkOWmhg
https://blog.csdn.net/wangjinyu501/article/details/83317542
https://blog.csdn.net/qxs965266509/article/details/61925652
https://blog.csdn.net/u012968084/article/details/52160399
https://blog.csdn.net/leopard21/article/details/32201501

你可能感兴趣的:(android学习笔记)