You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态壁纸中如何实现多Canvas绘制(一静一动)?

嘿,其实你根本不需要多个Canvas就能搞定这个需求!动态壁纸的绘制逻辑里,单Canvas分层绘制就可以完美实现你的需求:先绘制背景Bitmap,再在手指位置绘制移动对象,每次onDraw时重新绘制整个画面,自然就能呈现出对象跟随手指移动的效果。

实现动态壁纸的正确思路(无需多Canvas)

1. 基础框架:创建WallpaperService和Engine子类

动态壁纸的核心是继承WallpaperService,并实现内部的Engine类,所有绘制、触摸逻辑都在Engine里处理。

2. 加载资源并初始化坐标

在Engine的初始化方法里加载背景和移动对象的Bitmap,同时初始化触摸坐标(比如默认在屏幕中心),注意要做内存优化,避免大图导致的OOM:

private Bitmap backgroundBitmap;
private Bitmap movingObjectBitmap;
private float touchX, touchY;

@Override
public void onCreate(SurfaceHolder surfaceHolder) {
    super.onCreate(surfaceHolder);
    // 允许壁纸接收触摸事件
    setTouchEventsEnabled(true);
    
    // 加载背景图(以资源文件为例)
    backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg_wallpaper);
    // 加载跟随手指的对象图
    movingObjectBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.follow_object);
    
    // 初始化默认坐标为屏幕中心
    DisplayMetrics metrics = getResources().getDisplayMetrics();
    touchX = metrics.widthPixels / 2f;
    touchY = metrics.heightPixels / 2f;
}

3. 处理触摸事件,实时更新坐标

重写onTouchEvent方法,记录手指的实时位置,然后触发重绘:

@Override
public void onTouchEvent(MotionEvent event) {
    super.onTouchEvent(event);
    // 更新触摸坐标
    touchX = event.getX();
    touchY = event.getY();
    // 触发画布重绘
    SurfaceHolder holder = getSurfaceHolder();
    Canvas canvas = holder.lockCanvas();
    if (canvas != null) {
        onDraw(canvas);
        holder.unlockCanvasAndPost(canvas);
    }
}

4. 核心绘制:单Canvas分层渲染

在onDraw方法里,按顺序绘制背景和移动对象——先画背景覆盖整个画布,再在触摸坐标处绘制对象,这样每次重绘都会覆盖之前的画面,视觉上就呈现出对象跟随移动的效果:

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (backgroundBitmap == null) return;
    
    // 1. 绘制背景,铺满整个屏幕
    canvas.drawBitmap(backgroundBitmap, 0, 0, null);
    
    // 2. 绘制跟随对象(调整坐标让对象中心对准手指)
    float objectOffsetX = movingObjectBitmap.getWidth() / 2f;
    float objectOffsetY = movingObjectBitmap.getHeight() / 2f;
    canvas.drawBitmap(movingObjectBitmap, touchX - objectOffsetX, touchY - objectOffsetY, null);
    
    // 如果需要更平滑的过渡,可以添加延迟重绘,维持60fps的刷新
    postDelayed(() -> {
        SurfaceHolder holder = getSurfaceHolder();
        Canvas refreshCanvas = holder.lockCanvas();
        if (refreshCanvas != null) {
            onDraw(refreshCanvas);
            holder.unlockCanvasAndPost(refreshCanvas);
        }
    }, 16);
}

收尾:回收资源避免内存泄漏

记得在Engine销毁时回收Bitmap资源:

@Override
public void onDestroy() {
    super.onDestroy();
    if (backgroundBitmap != null && !backgroundBitmap.isRecycled()) {
        backgroundBitmap.recycle();
    }
    if (movingObjectBitmap != null && !movingObjectBitmap.isRecycled()) {
        movingObjectBitmap.recycle();
    }
}

关键说明

为什么不用多Canvas?因为动态壁纸的SurfaceHolder每次提供的Canvas是用于整个画面绘制的,我们只需要通过绘制顺序实现分层:背景先画(覆盖旧画面),对象后画(叠加在背景上),这样就模拟出了“两层”的视觉效果,完全不需要多个Canvas。

内容的提问来源于stack exchange,提问作者kazmer1998

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:26:37