image.png
Android 允许使用 WallpaperManager
来改变壁纸,在该对象中改变壁纸的方法如下。
-
setBitmap(Bitmap bitmap)
将壁纸设置为 bitmap 所代表的位图。 -
setResource(int resid)
将壁纸设置为 resid 资源所代表的图片。 -
setStream(InputStream data)
将壁纸设置为 data 数据所代表的图片。
开发动态壁纸
Android 提供了 WallpaperService
基类,动态壁纸的实现类需要继承该基类。在 Android 应用中开发动态壁纸的步骤如下。
- 开发一个子类继承
WallpaperService
基类。 - 继承
WallpaperService
基类时必须重写onCreateEngine()
方法,该方法返回WallpaperService.Engine
子类对象。 - 开发者需要实现
WallpaperService.Engine
予类,并重写其中的onVisibilityChanged(boolean visible)
、onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels)
方法。不仅如此,由于WallpaperService.Engine
子类采用了与SurfaceView
相同的绘图机制,因此还可有选择性地重写SurfaceHolder.Callback
中的三个方法。重写这些方法时可通过SurfaceHolder
动态地绘制图形。
实例:蜿蜒壁纸
LiveWallpaper
public class LiveWallpaper extends WallpaperService {
// 记录用户触碰点的位图
private Bitmap heart;
// 实现WallpaperService必须实现的抽象方法
@Override
public WallpaperService.Engine onCreateEngine() {
// 加载心形图片
heart = BitmapFactory.decodeResource(getResources(), R.drawable.heart);
// 返回自定义的Engine
return new MyEngine();
}
class MyEngine extends WallpaperService.Engine {
// 记录程序界面是否可见
private boolean mVisible;
// 记录当前当前用户动作事件的发生位置
private float mTouchX = -1f;
private float mTouchY = -1f;
// 记录当前需要绘制的矩形的数量
private int count = 1;
// 记录绘制第一个矩形所需坐标变换的X、Y坐标的偏移
private float originX = 0f;
private float originY = 100f;
private float cubeHeight = 0f;
private float cubeWidth = 0f;
// 定义画笔
private Paint mPaint = new Paint();
// 定义一个Handler
private Handler mHandler = new Handler();
// 用于记录屏幕大小
private DisplayMetrics dis = new DisplayMetrics();
// 定义一个周期性执行的任务
private Runnable drawTarget = this::drawFrame;
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
// 初始化画笔
mPaint.setARGB(76, 0, 0, 255);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
// 根据屏幕宽度设置动态壁纸的起点位置的X坐标
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(dis);
originX = dis.widthPixels / 3;
originY = dis.heightPixels / 10;
cubeHeight = dis.widthPixels / 4;
cubeWidth = dis.widthPixels / 8;
// 设置处理触摸事件
setTouchEventsEnabled(true);
}
@Override
public void onDestroy() {
super.onDestroy();
// 删除回调
mHandler.removeCallbacks(drawTarget);
}
@Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
// 当界面可见时,执行drawFrame()方法
if (visible) {
// 动态地绘制图形
drawFrame();
} else {
// 如果界面不可见,删除回调
mHandler.removeCallbacks(drawTarget);
}
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xStep, float yStep, int xPixels, int yPixels) {
drawFrame();
}
@Override
public void onTouchEvent(MotionEvent event) {
// 如果检测到滑动操作
if (event.getAction() == MotionEvent.ACTION_MOVE) {
mTouchX = event.getX();
mTouchY = event.getY();
} else {
mTouchX = -1f;
mTouchY = -1f;
}
super.onTouchEvent(event);
}
// 定义绘制图形的工具方法
private void drawFrame() {
// 获取该壁纸的SurfaceHolder
SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
// 对画布加锁
c = holder.lockCanvas();
if (c != null) {
// 绘制背景色
c.drawColor(-0x1);
// 在触碰点绘制心形
drawTouchPoint(c);
// 设置画笔的透明度
mPaint.setAlpha(76);
c.translate(originX, originY);
// 采用循环绘制count个矩形
for (int i = 0; i < count; i++) // ①
{
c.translate((cubeHeight * 2 / 3), 0f);
c.scale(0.95f, 0.95f);
c.rotate(20f);
c.drawRect(0f, 0f, cubeHeight, cubeWidth, mPaint);
}
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
mHandler.removeCallbacks(drawTarget);
// 调度下一次重绘
if (mVisible) {
count++;
if (count > 50) {
originX = new Random().nextInt(dis.heightPixels * 2 / 3);
originY = new Random().nextInt(dis.heightPixels / 5);
count = 1;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 指定0.1秒后重新执行drawTarget一次
mHandler.postDelayed(drawTarget, 100); // ②
}
}
// 在屏幕触碰点绘制位图
private void drawTouchPoint(Canvas c) {
if (mTouchX >= 0 && mTouchY >= 0) {
// 设置画笔的透明度
mPaint.setAlpha(255);
c.drawBitmap(heart, mTouchX, mTouchY, mPaint);
}
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyWallpaper"
tools:targetApi="31">
<service
android:name=".LiveWallpaper"
android:exported="true"
android:label="@string/app_name"
android:permission="android.permission.BIND_WALLPAPER">
<!-- 为动态壁纸配置intent-filter -->
<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService" />
</intent-filter>
<!-- 为动态壁纸配置meta-data -->
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/livewallpaper" />
</service>
</application>
</manifest>
xml/livewallpaper.xml
<?xml version="1.0" encoding="utf-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" />
把上面的程序部署到模拟器上,该程序不能直接运行,需要按步骤来设置动态壁纸。
image.png
摘抄至《疯狂Android讲义(第4版)》