Android動態桌布詳解
阿新 • • 發佈:2019-02-14
動態桌布
Livewallpaper(動態桌布): 首先動態桌布並不是GIF圖片,而是一個獨立的應用程式,本質是一個Service,甚至可以沒有桌面圖示。
直接看AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qwq.clocklivewallpaper">
<application
android:allowBackup="true"
android:icon="@drawable/clock"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!-- 主介面Activity.可以去掉。動態桌布應用是可以允許沒有Activity的 -->
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 動態桌布設定介面,可有可無,一般桌布應用會有,作用是讓使用者自定義動態桌布 -->
<activity
android:name=".SettingsActivity"
android:exported="true"
android:label="@string/app_name">
</activity>
<!-- 動態桌布本質就是Service,所以此類為動態桌布核心-->
<service
android:name=".ClockWallpaperService"
android:enabled="true"
android:label="@string/wallpaper_name" //下面第一張圖片中label
android:permission="android.permission.BIND_WALLPAPER">//動態桌布必須加此許可權
<intent-filter>
//系統就是通過APK的這個action把其當做一個動態牆紙。
<action android:name="android.service.wallpaper.WallpaperService"></action>
</intent-filter>
<!-- android:resource 指定的xml很重要!!!後面會單獨介紹 -->
<meta-data
android:name="android.service.wallpaper"
android:resource="@xml/clock_wallpaper">
</meta-data>
</service>
</application>
</manifest>
clock_wallpaper.xml
<?xml version="1.0" encoding="UTF-8"?>
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/wallpaper_description"
android:settingsActivity="com.qwq.clocklivewallpaper.SettingsActivity"
android:thumbnail="@drawable/ic_preview" />
android:thumbnail 動態桌布列表中的圖示,
android:description 動態桌布的簡單介紹文字,有的手機可能不顯示
android:settingsActivity 指定一個Activity。系統會檢測動態桌布應用有沒有此屬性。如果有則和下方的圖片顯示效果一樣(兩個Button),點選下圖中設定Button可跳轉至指定的Activity(設定介面)。如果沒有此屬性,就只有一個設定桌布Button。可以去掉此屬性看一下效果。
WallpaperService(核心)
實現動態桌布必須繼承WallpaperService,且過載onCreateEngine方法。onCreateEngine()方法只需返回一個Engine的子類物件就可以了,所以動態桌布應用的主要工作就是實現Engine的子類。其原理是使用surfaceview不斷更新,實現動態桌布效果。不多說了,直接貼程式碼,重要的方法都會有所說明。
package com.qwq.clocklivewallpaper;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import java.util.Date;
public class ClockWallpaperService extends WallpaperService {
static final String TAG = ClockWallpaperService.class.getSimpleName();
static final int POINTER_WIDTH = 9;
// onCreateEngine()方法需返回一個Engine的子類物件就可以了,
// 所以動態桌布應用的主要工作就是實現Engine的子類
@Override
public Engine onCreateEngine() {
Log.d(TAG, "onCreateEngine");
return new ClockWallpaperEngine();
}
//實現Engine的子類,本文的重中之重,動態桌布就是在此具體實現的
private class ClockWallpaperEngine extends Engine implements
OnSharedPreferenceChangeListener {
private final Handler handler = new Handler();
// surfacewive使用執行緒更新UI,所以我們可使用Runnable介面建立一個執行緒,把具體繪製方法放進去,
// 下文會使用handler呼叫
private final Runnable drawRunner = new Runnable() {
@Override
public void run() {
draw();
}
};
private Paint paint;
private int width;
private int height;
private boolean isVisible = true;
private boolean isShowSecond;
private ClockView clockView;
private SharedPreferences sp;
//建構函式,初始化動態桌布
public ClockWallpaperEngine() {
Log.d(TAG, "ClockWallpaperEngine");
initSp();
initPaint();
startDrawClock();
}
public void initSp() {
sp = PreferenceManager.getDefaultSharedPreferences(ClockWallpaperService.this);
sp.registerOnSharedPreferenceChangeListener(this);
isShowSecond = sp.getBoolean(SettingsActivity.IS_SHOW_SECOND, true);
}
public void initPaint() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(POINTER_WIDTH);
}
public void startDrawClock() {
clockView = new ClockView(getApplicationContext());
handler.post(drawRunner);
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
Log.d(TAG, "onCreate");
}
//監聽是否可見變化,可見時開始更新UI,不可見時停止重新整理
@Override
public void onVisibilityChanged(boolean visible) {
this.isVisible = visible;
if (visible) {
handler.post(drawRunner);
} else {
handler.removeCallbacks(drawRunner);
}
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
isVisible = false;
handler.removeCallbacks(drawRunner);
sp.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSurfaceChanged(SurfaceHolder holder,int format,int width,int height){
this.width = width;
this.height = height;
super.onSurfaceChanged(holder, format, width, height);
}
//繪製,每個200毫秒重新整理介面
private void draw() {
SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas != null) {
drawClock(canvas);
}
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
handler.removeCallbacks(drawRunner);
if (isVisible) {
handler.postDelayed(drawRunner, 200);
}
}
//具體繪製鐘錶
private void drawClock(Canvas canvas) {
canvas.drawColor(Color.WHITE); //繪製整個動態桌布的背景顏色
clockView.config(width / 2, height / 2, (int) (width * 0.8f), new Date(),
paint, isShowSecond);
clockView.draw(canvas);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String
key){
//監聽SharedPreference變化,改變動態桌布樣式
if (SettingsActivity.IS_SHOW_SECOND.equals(key)) {
isShowSecond = sharedPreferences.getBoolean(SettingsActivity.IS_SHOW_SECOND,
true);
}
}
@Override
public void onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
// 可以在這裡做一些與使用者互動的操作,例如動態星星桌布,使用者觸控式螢幕幕時增加星星數量。
Log.d(TAG, "onTouchEvent");
}
}
}
ClockView(鐘錶的具體繪製)
package com.qwq.clocklivewallpaper;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;
import java.util.Calendar;
import java.util.Date;
public class ClockView extends View {
static final String TAG = ClockView.class.getSimpleName();
private float x;
private float y;
private int radius;
private Calendar cal;
private Paint paint;
private Bitmap clockDial = BitmapFactory.decodeResource(getResources(),R.drawable.clock_bg);
private int sizeScaled = -1;
private Bitmap clockDialScaled;
private boolean isShowSecond;
public ClockView(Context context) {
super(context);
cal = Calendar.getInstance();
}
//設定鐘錶寬高等屬性
public void config(float x, float y, int size, Date date, Paint paint, boolean
isShowSecond) {
this.x = x;
this.y = y;
this.paint = paint;
this.isShowSecond = isShowSecond;
cal.setTime(date);
if (size != sizeScaled) {
clockDialScaled = Bitmap.createScaledBitmap(clockDial, size, size, false);
radius = size / 2;
}
}
//具體繪製鐘錶的指標
protected void onDraw(Canvas canvas) {
Log.d(TAG,"onDraw:"+canvas);
super.onDraw(canvas);
if (paint != null) {
canvas.drawBitmap(clockDialScaled, x - radius, y - radius, null);
float sec = cal.get(Calendar.SECOND);
float min = cal.get(Calendar.MINUTE);
float hour = cal.get(Calendar.HOUR_OF_DAY);
paint.setColor(Color.RED);
canvas.drawLine(x, y,
(float)(x + (radius * 0.5f)*Math.cos(Math.toRadians((hour / 12.0f * 360.0f) - 90f))),
(float)(y + (radius * 0.5f)*Math.sin(Math.toRadians((hour / 12.0f * 360.0f) -90f))),
paint);
canvas.save();
paint.setColor(Color.BLUE);
canvas.drawLine(x, y,
(float)(x + (radius * 0.6f)*Math.cos(Math.toRadians((min / 60.0f * 360.0f) - 90f))),
(float)(y + (radius * 0.6f)*Math.sin(Math.toRadians((min / 60.0f * 360.0f) - 90f))),
paint);
canvas.save();
if (isShowSecond) {
paint.setColor(Color.GREEN);
canvas.drawLine(x, y,
(float)(x + (radius * 0.7f)*Math.cos(Math.toRadians((sec/60.0f * 360.0f) - 90f))),
(float)(y + (radius * 0.7f)*Math.sin(Math.toRadians((sec/60.0f * 360.0f) - 90f))),
paint);
}
}
}
}