LiveWallpaperをつくる
LiveWallpaperを作るメモ。
Handlerを利用してメッセージを処理する(描画を行う)サンプルです。
参考サイト
Android Developers CubeLiveWallpaper
http://developer.android.com/intl/ja/resources/samples/CubeLiveWallpaper/index.html
概要
実装
WallpaperService継承クラス作成
WallpaperServiceを継承するクラスを作成します。
このクラスがLiveWallpaperサービスの主な実装となります。
- ExampleWallpaer.java
package com.ttshrk.livewallpaper_example; import android.os.Handler; import android.service.wallpaper.WallpaperService; import android.service.wallpaper.WallpaperService.Engine; public class ExampleWallpaer extends WallpaperService { private final Handler mHandler = new Handler(); @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } @Override public Engine onCreateEngine() { // TODO return null; } }
このクラスの中のWallpaperService::onCreateEngineが描画を行うインスタンス(後述)を返します。
このEngineクラスのインスタンスは複数作成されることがあるため、静的プロパティの扱いに注意が必要です。(壁紙に設定してあるLiveWallpaperを設定画面のプレビューで表示したときなど。)
WallpaperService.Engineを実装する
壁紙の描画を行うクラスExampleWallpaer.ExampleEngineを作成します。
このクラスは、WallpaperService.Engineを継承します。
- WallpaperService.java
public class ExampleWallpaer extends WallpaperService { private final Handler mHandler = new Handler(); @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } @Override public Engine onCreateEngine() { return new ExampleEngine(); } class ExampleEngine extends Engine { Context context; int width; int height; boolean mVisible; PointF timePoint = new PointF(); Paint timePaint; float mMillisPerFrame; SimpleDateFormat simpleDateFormat; String timeString; private final Runnable mDrawer = new Runnable() { public void run() { drawFrame(); } }; ExampleEngine() { context = ExampleWallpaer.this.getApplicationContext(); width = getWallpaperDesiredMinimumWidth() / 2; height = getWallpaperDesiredMinimumHeight(); mMillisPerFrame = 1000f / 30f; simpleDateFormat = new SimpleDateFormat("HH:mm:ss"); timePaint = new Paint(); timePaint.setAntiAlias(true); timePaint.setColor(Color.WHITE); timePaint.setTextSize(80); timePaint.setTextAlign(Paint.Align.RIGHT); timePaint.setAlpha(0xcf); timePoint.set(width, height * 3 / 5); timeString = ""; } @Override public void onCreate(SurfaceHolder surfaceHolder) { super.onCreate(surfaceHolder); } @Override public void onDestroy() { super.onDestroy(); mHandler.removeCallbacks(mDrawer); } @Override public void onVisibilityChanged(boolean visible) { mVisible = visible; if (visible) { drawFrame(); } else { mHandler.removeCallbacks(mDrawer); } } @Override public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { super.onSurfaceChanged(holder, format, width, height); drawFrame(); } @Override public void onSurfaceCreated(SurfaceHolder holder) { super.onSurfaceCreated(holder); } @Override public void onSurfaceDestroyed(SurfaceHolder holder) { super.onSurfaceDestroyed(holder); mVisible = false; mHandler.removeCallbacks(mDrawer); } @Override public void onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels) { drawFrame(); } /* * Store the position of the touch event so we can use it for drawing * later */ @Override public void onTouchEvent(MotionEvent event) { timePoint.set(event.getX(), event.getY()); super.onTouchEvent(event); } void drawFrame() { final SurfaceHolder holder = getSurfaceHolder(); Canvas c = null; try { c = holder.lockCanvas(); if (c != null) { update(); c.drawColor(Color.BLACK); draw(c); } } finally { if (c != null) holder.unlockCanvasAndPost(c); } // Reschedule the next redraw mHandler.removeCallbacks(mDrawer); if (mVisible) { mHandler.postDelayed(mDrawer, (int) mMillisPerFrame); } } void update() { timeString = simpleDateFormat.format(new Date()); } void draw(Canvas canvas) { canvas.drawText(timeString, timePoint.x, timePoint.y, timePaint); } } }
ポイントは以下の通りです。
- Handler
ExampleWallpaer#mHandlerにメッセージ(mDrawer)をPostして描画を行います。Handlerクラスは受け取ったメッセージを独立したスレッドで順次処理していくため、サービスの実行を阻害せずに描画を行ってくれます。
- ExampleEngine#mDrawer
描画を行うRnnbable(メッセージ)です。
- ExampleEngine#onVisibilityChanged
壁紙が表示、非表示されたタイミングで呼ばれます。
このイベントで、描画の開始や停止を設定します。
- ExampleEngine#drawFrame
1frameあたりの処理を行います。また、次に行う描画処理の登録も行います。
AndroidManifest.xml
AndroidManifest.xmlにLiveWallpaperの設定を作成します。
- AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ttshrk.livewallpaper_example" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="7" /> <uses-feature android:name="android.software.live_wallpaper" /> <application android:label="@string/app_name" android:icon="@drawable/icon" android:debuggable="true"> <service android:label="@string/app_name" android:name=".ExampleWallpaer" 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/wallpaper" /> </service> </application> </manifest>
ポイントは以下の通りです。
- live_wallpaperの設定
uses-featureにandroid.software.live_wallpaperを設定します。
<uses-feature android:name="android.software.live_wallpaper" />
- serviceの登録
ExampleWallpaerをサービスとして登録します。
- wallpaperのxmlファイルを設定
壁紙の設定を記述したXMLファイルを、meta-data指定します。
<meta-data android:name="android.service.wallpaper" android:resource="@xml/wallpaper" />
リソースファイル作成
壁紙の設定を記述したXMLファイルを作成します。
これはライブ壁紙の設定画面の一覧に表示されます。
<?xml version="1.0" encoding="utf-8"?> <wallpaper xmlns:android="http://schemas.android.com/apk/res/android" android:thumbnail="@drawable/icon" android:description="@string/service_description" />
stringリソース設定。
- res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Livewallpaper Example</string> <string name="service_description">壁紙の説明</string> </resources>
以上です。
実行すると壁紙に時刻が表示されます。また、タッチしたポイントに表示が移動します。
サンプルコード
livewallpaper_example.zip