Android 改变壁纸

image.png

Android 允许使用 WallpaperManager 来改变壁纸,在该对象中改变壁纸的方法如下。

  • setBitmap(Bitmap bitmap) 将壁纸设置为 bitmap 所代表的位图。
  • setResource(int resid) 将壁纸设置为 resid 资源所代表的图片。
  • setStream(InputStream data) 将壁纸设置为 data 数据所代表的图片。

开发动态壁纸

Android 提供了 WallpaperService 基类,动态壁纸的实现类需要继承该基类。在 Android 应用中开发动态壁纸的步骤如下。

  1. 开发一个子类继承 WallpaperService 基类。
  2. 继承 WallpaperService 基类时必须重写 onCreateEngine() 方法,该方法返回 WallpaperService.Engine 子类对象。
  3. 开发者需要实现 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版)》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容