自作ウォッチフェイスにコンプリケーションを実装する
さくっとコンプリケーションを乗せてみたよって記事があっても良いと思ったので書きます。
前提
これが出来ているとする。
Android Studio で新プロジェクトを作成する際に生成できるやつです。
目標
↑をこういう風にすること。
今回は設定画面の作成をせず、とにかく上画像のようにコンプリケーションを置ければ OK とします。
MyWatchFace
の変更点
コンプリケーションのためのフィールドを定義する
private static final int LEFT_COMPLICATION_ID = 100; private static final int RIGHT_COMPLICATION_ID = 101; private static final int[] COMPLICATION_IDS = { LEFT_COMPLICATION_ID, RIGHT_COMPLICATION_ID };
コンプリケーションを識別するための ID。これからたくさん使う。
Engine
の変更点
追加のフィールドを定義する
private SparseArray<ComplicationData> mComplicationDataArray; private SparseArray<ComplicationDrawable> mComplicationDrawables;
SparseArray<Object>
は Map<Integer, Object>
のように使えるクラスだが、キーには int
を使用する。キーのオートボクシングを行わない、データ構造がマッピングごとに余分なエントリ オブジェクトに依存しない、という理由で HashMap
よりメモリの効率が良いらしい。*1 エントリ オブジェクトって Map.Entry<K, V>
のことかな。
ComplicationData
はコンプリケーションのデータの入れ物で、ComplicationDrawable
はコンプリケーションを描画する Drawable
。
onCreate(SurfaceHolder)
に追記する
mComplicationDataArray = new SparseArray<>(COMPLICATION_IDS.length); mComplicationDrawables = new SparseArray<>(COMPLICATION_IDS.length); // コンプリケーション ID をキーとして、 // ComplicationDrawable インスタンスを SparseArray<> に追加する mComplicationDrawables.put( LEFT_COMPLICATION_ID, new ComplicationDrawable(MyWatchFace.this)); mComplicationDrawables.put( RIGHT_COMPLICATION_ID, new ComplicationDrawable(MyWatchFace.this)); // デフォルトのコンプリケーションを設定する // 今回は設定画面を作らないのでここでの設定が必要 setDefaultSystemComplicationProvider( LEFT_COMPLICATION_ID, SystemProviders.NEXT_EVENT, ComplicationData.TYPE_SHORT_TEXT); setDefaultSystemComplicationProvider( RIGHT_COMPLICATION_ID, SystemProviders.WATCH_BATTERY, ComplicationData.TYPE_RANGED_VALUE); // コンプリケーションに通常モード用とアンビエントモード用の色を設定する for (int complicationId : COMPLICATION_IDS) { ComplicationDrawable complicationDrawable = mComplicationDrawables.get(complicationId); complicationDrawable.setBorderColorActive(Color.RED); complicationDrawable.setBorderColorAmbient(Color.WHITE); complicationDrawable.setRangedValuePrimaryColorActive(Color.RED); complicationDrawable.setRangedValuePrimaryColorAmbient(Color.WHITE); } // ここで渡したコンプリケーションに対してデータが送られるようになる setActiveComplications(COMPLICATION_IDS);
ComplicationDrawable
インスタンスの作成を XML から行うこともできるが、今回は Java コードのみで行っている。
onPropertiesChanged(Bundle)
に追記する
for (int complicationId : COMPLICATION_IDS) { ComplicationDrawable complicationDrawable = mComplicationDrawables.get(complicationId); complicationDrawable.setLowBitAmbient(mLowBitAmbient); complicationDrawable.setBurnInProtection(mBurnInProtection); }
このメソッドは時計のプロパティ(焼き付き保護、低ビットアンビエントモード)が決定されたときに呼ばれる。
onAmbientModeChanged(boolean)
に追記する
for (int complicationId : COMPLICATION_IDS) { mComplicationDrawables.get(complicationId).setInAmbientMode(mAmbient); }
このメソッドはアンビエントモードが開始または終了したときに呼ばれる。
onTapCommand(int, int, int, long)
に挿入する
case TAP_TYPE_TAP: // The user has completed the tap gesture. // TODO: Add code to handle the tap gesture. Toast.makeText(getApplicationContext(), R.string.message, Toast.LENGTH_SHORT) .show(); // 追加ここから for (int complicationId : COMPLICATION_IDS) { // タップ位置がコンプリケーションの境界の内側なら、 // タップアクションをコンプリケーションに送信する boolean successfulTap = mComplicationDrawables.get(complicationId).onTap(x, y); if (successfulTap) { return; } } // 追加ここまで break;
このメソッドはタップまたはタッチに関連するイベントが発生したときに呼ばれる。
onDraw(Canvas, Rect)
に追記する
for (int complicationId : COMPLICATION_IDS) { mComplicationDrawables.get(complicationId).draw(canvas, now); }
このメソッドはウォッチフェイスを描画する。invalidate()
でこれの実行をスケジュールできる。
onComplicationDataUpdate(int, ComplicationData)
を作成する
@Override public void onComplicationDataUpdate(int watchFaceComplicationId, ComplicationData data) { mComplicationDataArray.put(watchFaceComplicationId, data); mComplicationDrawables.get(watchFaceComplicationId).setComplicationData(data); }
このメソッドは新しいコンプリケーションデータを受信したときに呼ばれる。
onSurfaceChanged(SurfaceHolder, int, int, int)
を作成する
@Override public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { super.onSurfaceChanged(holder, format, width, height); int sizeOfComplication = width / 4; int midpointOfScreen = width / 2; int horizontalOffset = (midpointOfScreen - sizeOfComplication) / 2; int verticalOffset = midpointOfScreen - (sizeOfComplication / 2); mComplicationDrawables.get(LEFT_COMPLICATION_ID).setBounds( horizontalOffset, verticalOffset, (horizontalOffset + sizeOfComplication), (verticalOffset + sizeOfComplication) ); mComplicationDrawables.get(RIGHT_COMPLICATION_ID).setBounds( (midpointOfScreen + horizontalOffset), verticalOffset, (midpointOfScreen + horizontalOffset + sizeOfComplication), (verticalOffset + sizeOfComplication) ); }
ここではコンプリケーションを配置する位置と範囲の計算を行っている。
サンプルアプリ(後述)のコメントによると、円形または正方形のコンプリケーションは画面幅の少なくとも四分の一、横長の長方形のコンプリケーションは画面幅の少なくとも三分の二を使用することが勧められている。
マニフェストに ComplicationHelperActivity
を追加する
<activity android:name="android.support.wearable.complications.ComplicationHelperActivity"/>
コンプリケーションで「次の予定」を表示するには、ユーザがウォッチフェイスに権限を与える必要がある。ComplicationHelperActivity
をマニフェストに追加しておくだけで、権限を要求するダイアログが実装される。(とても楽で良い)
実行してみる
まだ権限を与えていないので「次の予定」は表示されていない。
アンビエントモードではこんな感じ。
「次の予定」コンプリケーションをタップすると、
ダイアログが出るので了承する。
予定ないんか~い!
最後に
ここではコンプリケーションを 2 個置くための最低限の実装しかしていません。実用的なことを学ぶにはサンプルアプリとドキュメントを見るのが良いと思います。というか僕も初心者なのでこれから学んでいくつもりです。
参考
- Add complications to a watch face | Android Developers … 基礎を学べる
- GitHub - googlesamples/android-WatchFace: A simple sample that demonstrates watch faces and complications for Wear 2.0. … 公式のサンプルアプリ
以下、重要っぽいクラス達。