下面以一個範例來實作一個簡單的動態桌布,桌布背景為黑色, 當使用者點擊或劃過桌布時,會產生藍色的圓,並且隨時間變小消失:
- 建立WallpaperService。動態桌布其實也是一種Service,使用的class為WallpaperService,所以我們要先建立WallpaperService類別,程式碼如下,需要注意的是,在WallpaperService裡的Engine內部類別不行在WallpaperService之外建立然後使用,必須是也內部類別的方式存在在WallpaperService中:
- 建立給WallpaperService的資訊xml檔。其中可以指定縮圖(thumbnail)、簡介(description)等,配置內容如下:
- 註冊WallpaperService。因為WallpaperService是一種Service,所以要在AndroidManifest.xml中的<Application>節點中添加Service的注冊,如下程式碼,有幾點要注意,在Service中要加入"android.permission.BIND_WALLPAPER"並設定<intent-filter>的<action>為"android.service.wallpaper.WallpaperService"、還要加入相關的<meta-data>並在其中指定動態桌步的資訊xml檔,即上步驟的wallpaperinfo.xml:
- 建立Engine要用的Thread類別。 WallpaperThread.java:
- 最後的成品如下面影片展示:
MyWallpaperService.java:
package com.example.administrator.wallpaperservice_example;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
public class MyWallpaperService extends WallpaperService{
@Override
public Engine onCreateEngine() {
return new MyEngine();
}
//設定自訂的Engine
class MyEngine extends Engine{
WallpaperThread wallpaperThread;
public MyEngine() {
SurfaceHolder surfaceHolder = getSurfaceHolder();
wallpaperThread = new WallpaperThread(surfaceHolder);
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
//設定動態桌布可以互動觸控事件
setTouchEventsEnabled(true);
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
//開始執行動態桌布
wallpaperThread.start();
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
//結束動態桌布
wallpaperThread.stopRunning();
}
@Override
public void onDestroy() {
super.onDestroy();
//讓GC回收wallpaperThread
wallpaperThread = null;
}
@Override
public void onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
//執行wallpaperThread裡的觸控事件
wallpaperThread.doTouchEvent(event);
}
}
}
wallpaperinfo.xml:
<?xml version="1.0" encoding="utf-8"?> <wallpaper xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android" android:description="動態桌布" </wallpaper>
AndroidManifest.xml的片段:
<service android:name=".MyWallpaperService" 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/wallpaperinfo" /> </service>
package com.example.administrator.wallpaperservice_example;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import java.util.ArrayList;
public class WallpaperThread extends Thread {
//run為ture表示不斷檢查並執行消圓動作
boolean run = true;
//wait為true表示無圓可消,所以暫停消圓動作
boolean wait = true;
//SurfaceHolder用來取得Canvas
SurfaceHolder surfaceHolder;
Canvas canvas;
Paint paint;
//用來紀錄已產生的圓資料,待處理
ArrayList<Circle> pastCircles = new ArrayList<>();
//用來紀錄要從pastCircles中刪去的圓資料,因為不能在ArrayList遍歷元素時刪除元素,否則會有ConcurrentModificationException
ArrayList<Circle> circlesToBeRemoved = new ArrayList<>();
public WallpaperThread(SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
//設定畫圓用的畫筆顏色
paint = new Paint();
paint.setColor(Color.BLUE);
}
@Override
public void run() {
canvas = this.surfaceHolder.lockCanvas(null);
//繪製初始背景
canvas.drawColor(Color.BLACK);
surfaceHolder.unlockCanvasAndPost(canvas);
while (run) {
if (wait) {
try {
synchronized (this) {
//沒有圓可消時,讓Thread等待
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
synchronized (pastCircles) {
//將圓刪掉(背景重新塗色)
canvas = surfaceHolder.lockCanvas(null);
canvas.drawColor(Color.BLACK);
//檢查pastCircles中有無待處理的圓
for (Circle circle : pastCircles) {
//如果圓的半徑已被減0以下,就將其從pastCircles中刪除
if (circle.getRadius() <= 0) {
circlesToBeRemoved.add(circle);
} else {
//畫圓
canvas.drawCircle(circle.getCenterX(), circle.getCenterY(), circle.getRadius(), paint);
//將圓半徑減1
circle.setRadius(circle.getRadius() - 1);
}
}
surfaceHolder.unlockCanvasAndPost(canvas);
//從pastCircles中刪去已消完的圓資料
pastCircles.removeAll(circlesToBeRemoved);
//如果pastCircles中沒有圓了,就進行等待
wait = (pastCircles.size() == 0);
}
}
}
}
public void stopRunning() {
run = false;
}
public void doTouchEvent(MotionEvent motionEvent) {
//判斷Touch動作
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
//用來紀錄觸控動作發生時相應的圓圈資訊
Circle currentCircle = new Circle(motionEvent.getX(), motionEvent.getY(), 100);
//保護pastCircles,避免跟run()中讀取pastCircles的動作起衡突
synchronized (pastCircles) {
//將要處理的的圓紀錄到pastCircles中
pastCircles.add(currentCircle);
wait = false;
synchronized (this) {
//主動喚醒Thread
notify();
}
}
break;
}
}
//用來紀錄圓的資訊
class Circle {
float centerX;
float centerY;
int radius = 100;
public Circle(float centerX, float centerY, int radius) {
this.centerX = centerX;
this.centerY = centerY;
this.radius = radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public float getCenterX() {
return centerX;
}
public float getCenterY() {
return centerY;
}
public int getRadius() {
return radius;
}
}
}
附上源始碼下載: Android動態桌布.7z
感謝 晚點立刻測試!!!
回覆刪除您好,想請問大大如何把動態桌布的黑底改成自定圖片?
回覆刪除您可以使用canvas.drawBitmap()代替canvas.drawColor()來繪製您要的圖片。
刪除