wear-widgets について
Glance Wear と Remote Compose を使った Wear OS ウィジェットの作成や移行を支援する、Android 開発者向けスキルです。サービス登録、アダプティブレイアウト、リモート状態と操作、プレビュー、ピッカー用素材を扱います。
主な機能
- 01Glance Wear ウィジェットの設定
- 02Remote Compose のアダプティブレイアウト
- 03リモート状態とクリック操作
- 04ウィジェットのプレビューとピッカー画像
- 05旧タイルからの移行ガイド
活用シーン
- 01Wear OS ウィジェットの作成
- 02旧 Wear OS タイルの移行
- 03ウィジェットのプレビューとピッカー素材の追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ android/ skills --skill wear-widgetsインストール後、Claude Code で/wear-widgetsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/previews-and-testing.mdremote-compose-layouts.mdstate-and-actions.mdwidget-service-and-manifest.md詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
Wear OS ウィジェットでは、androidx.compose.remote(Remote Compose)とともに androidx.glance.wear(GlanceWearWidget および
GlanceWearWidgetService)を使用します。
宣言型 UI と状態のドキュメントをシリアライズして、ウォッチのホストプロセスに送信します。
前提条件とセットアップ
- ライブラリのバージョンを解決する:
{GLANCE_WEAR_VERSION}(androidx.glance.wear)、{COMPOSE_REMOTE_VERSION}(androidx.compose.remote)、{WEAR_COMPOSE_REMOTE_VERSION}(androidx.wear.compose.remote)の最新バージョンを解決します。安定版がまだない場合は、-alpha、-beta、または-rcリリースも含めます。バージョン解決のために./gradlew dependenciesを実行しないでください。- 利用可能な依存関係検索ツールがある場合は、それを使用します。
- それ以外の場合は、Google Maven のメタデータから最新バージョンを取得します(Glance Wear のメタデータ、Compose Remote のメタデータ、Wear Compose Remote のメタデータ)。
- SDK と Compose コンパイラの要件:ユーザーがウィジェットの作成、タイルの移行、または依存関係のセットアップを依頼していない限り、リポジトリ全体に適用される Gradle ファイルを変更する前に許可を求めます。
compileSdk = 37以上を設定し、buildFeatures { compose = true }を有効にします。プロジェクト既存の Android および Kotlin Gradle プラグインの宣言はそのまま維持します。
dependencies {
implementation("androidx.glance.wear:wear:{GLANCE_WEAR_VERSION}")
implementation("androidx.glance.wear:wear-core:{GLANCE_WEAR_VERSION}")
implementation("androidx.glance.wear:wear-tooling-preview:{GLANCE_WEAR_VERSION}")
implementation("androidx.compose.remote:remote-creation-compose:{COMPOSE_REMOTE_VERSION}")
implementation("androidx.compose.remote:remote-creation:{COMPOSE_REMOTE_VERSION}")
implementation("androidx.compose.remote:remote-core:{COMPOSE_REMOTE_VERSION}")
implementation("androidx.compose.remote:remote-tooling-preview:{COMPOSE_REMOTE_VERSION}")
implementation("androidx.wear.compose.remote:remote-material3:{WEAR_COMPOSE_REMOTE_VERSION}")
}
コードベースの調査
- モジュール内で、既存の
TileService、Material3TileService、またはGlanceWearWidgetの実装、app/src/main/AndroidManifest.xml内の<service>エントリ、およびapp/src/main/res/配下の@drawable/プレビューアセットを検索します。 - 既存のタイルを移行する場合、または構成を把握していないマルチモジュールプロジェクトを変更する場合は、
./gradlew :app:assembleDebugを実行して、変更前のビルド状態を確認します。
ワークフロー
ステップ 1:AndroidManifest.xml とプロバイダー XML を設定する
BIND_TILE_PROVIDER 権限と BIND_WIDGET_PROVIDER インテントアクションを指定して、
GlanceWearWidgetService のサブクラスを
app/src/main/AndroidManifest.xml に登録します。SMALL と LARGE の <container>
エントリを含む @xml/ の <wearwidget-provider> リソースを指す <meta-data> を追加します。サービス、プロバイダー XML、または従来の TileService の移行を設定する際は、必ず ウィジェットサービスとマニフェストのリファレンス に従ってください。
<service
android:name=".MyWidgetService"
android:exported="true"
android:permission="com.google.android.wearable.permission.BIND_TILE_PROVIDER"
>
<intent-filter>
<action android:name="androidx.glance.wear.action.BIND_WIDGET_PROVIDER" />
</intent-filter>
<meta-data
android:name="androidx.glance.wear.widget.provider"
android:resource="@xml/my_widget_info" />
</service>
ステップ 2:GlanceWearWidget と GlanceWearWidgetService を実装する
GlanceWearWidget を拡張し、WearWidgetDocument を返す
provideWidgetData をオーバーライドします。静的なプロバイダー解決のため、
GlanceWearWidgetService のサブクラスに @AssociateWithGlanceWearWidget を付与します。
// 誤り:モバイル向け GlanceAppWidget API(Wear OS では使用しないでください)
class BrokenWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {}
}
// 正しい例:Wear OS の GlanceWearWidget + GlanceWearWidgetService
@AssociateWithGlanceWearWidget(MyWidget::class)
class MyWidgetService : GlanceWearWidgetService() {
override val widget: GlanceWearWidget = MyWidget()
}
class MyWidget : GlanceWearWidget() {
override suspend fun provideWidgetData(
context: Context,
params: WearWidgetParams,
): WearWidgetData = WearWidgetDocument(
background = WearWidgetBrush.color(Color(0xFF1C1B1F).rc)
) {
MyWidgetContent(params = params)
}
}
ステップ 3:Remote Compose のレイアウト、状態、アクションを構築する
Remote Compose は、アプリのプロセスではなく Wear OS ホストのプロセス内で UI と状態を評価します。アダプティブレイアウトや画面全体に広がる背景を構築する場合は、必ず Remote Compose レイアウトのリファレンスに従ってください。状態やクリックアクションを実装する場合は、必ず 状態とアクションのリファレンスに従ってください。
- Remote の値ラッパー:
.rs(RemoteString)、.rc(RemoteColor)、.rdp(RemoteDp)、.ri(RemoteInt)を使ってプリミティブ値を変換します。整数の書式設定には、RemoteInt.toRemoteStringメンバーメソッドを使います。 - アダプティブなコンテナーサイズ:
params.containerType(ContainerInfo.CONTAINER_TYPE_SMALLとContainerInfo.CONTAINER_TYPE_LARGE)に応じて、レイアウト密度、タイポグラフィ、項目数を切り替えます。 - マスクされない画面全体に広がる背景:ルートの
RemoteBox(RemoteModifier.fillMaxSize)内でRemoteImageとRemoteImageBitmap(bitmap.asImageBitmap)を使って背景ビットマップを描画します。cornerRadius = 0dpを指定し、CircleShapeやRoundedCornerShapeでクリッピングせず、背景アートワークをマスクしないでください。ホストが端をウォッチの形状に合わせてクリッピングします。 - 宣言的なホスト状態と PendingIntent アクション:メモリ内の Compose 状態(
mutableStateOfやmutableIntStateOf)ではなく、rememberMutableRemoteIntとvalueChange(state, newValue)を使ってウィジェットのローカル状態を変更します。pendingIntentAction { ctx -> ... }(常に(Context) -> PendingIntentラムダを渡します)を使って、アクティビティの起動やバックグラウンドブロードキャストを実行します。widget.triggerUpdateAll(context)を使って、アクティブなウィジェットを更新します。
ステップ 4:WearWidgetPreview 関数とピッカープレビュー PNG を追加する
- コンポーザブルの境界線プレビュー(
androidx.glance.wear.tooling.preview):androidx.glance.wear.tooling.previewからWearWidgetPreview、SquircleAllWidgetPreviewParams、RoundAllWidgetPreviewParams、RectangularAllWidgetPreviewParamsをインポートします(androidx.glance.wear.testingは絶対に使用しないでください)。WearWidgetPreview(widget, params)を呼び出す@Preview関数を定義します。 - 長方形のピッカー PNG(
res/drawable-nodpi/):320 dpiでRectangularAllWidgetPreviewParamsを使うか、プロバイダー XML 内のすべての<container>に対して、空白ではなくマスクされていない長方形の PNG を作成します。SMALL(CONTAINER_TYPE_SMALL):448x168px(320 dpi で224x84dp、アスペクト比2.67)。LARGE(CONTAINER_TYPE_LARGE):464x288px(320 dpi で232x144dp、アスペクト比1.61)。- 各プレビュー PNG には、背景、テキストラベル、アイコン、アバターのイニシャル、ボタンなど、ウィジェットの実際の UI 要素を必ず描画してください。空白のプレースホルダー、ラベルのない空の図形、1:1 のタイルプレビューは絶対に使用しないでください。コンポーザブルプレビュー、ピッカー PNG、ユニットテストを追加する場合は、必ずプレビューとテストのリファレンスに従ってください。
コンポーネントの対応関係
次のサンプル表を使って、実装パターンとインポートの全体を確認してください。
| 機能または API サーフェス | リファレンスガイド |
|---|---|
GlanceWearWidget、GlanceWearWidgetService、AndroidManifest.xml、<wearwidget-provider>、レガシーな TileService からの移行 | ウィジェットサービスとマニフェストのリファレンス |
RemoteBox、RemoteColumn、RemoteRow、RemoteText、RemoteImage、ContainerInfo(SMALL と LARGE のアダプティブレイアウト) | Remote Compose レイアウトのリファレンス |
RemoteButton、rememberMutableRemoteInt、valueChange、pendingIntentAction、BroadcastReceiver、triggerUpdateAll | 状態とアクションのリファレンス |
WearWidgetPreview、SquircleAllWidgetPreviewParams、RoundAllWidgetPreviewParams、RectangularAllWidgetPreviewParams、ピッカー用 PNG、captureRawContent テスト | プレビューとテストのリファレンス |
レガシーな ProtoLayout とモバイル版 Glance の対応 API
| レガシー API またはモバイル API | Wear OS ウィジェット API | 対応内容 |
|---|---|---|
androidx.wear.tiles.TileService | GlanceWearWidgetService + GlanceWearWidget | サービスを置き換え、@AssociateWithGlanceWearWidget を付与します。 |
GlanceAppWidget / provideGlance | GlanceWearWidget / provideWidgetData | provideWidgetData から WearWidgetDocument を返します。 |
LayoutElementBuilders / primaryLayout | RemoteColumn、RemoteRow、RemoteBox | @RemoteComposable 関数でレイアウトを構築します。 |
mutableStateOf / mutableIntStateOf | rememberMutableRemoteInt + valueChange | valueChange を使って、リモート状態を宣言的に変更します。 |
ModifiersBuilders.Clickable | RemoteModifier.clickable(pendingIntentAction { ... }) | (Context) -> PendingIntent ラムダを pendingIntentAction に渡します。 |
トラブルシューティング
| 症状 | 原因 | 修正方法 |
|---|---|---|
GlanceWearWidget の参照を解決できない | androidx.glance.wear:wear:{GLANCE_WEAR_VERSION} がない、または compileSdk が古い | compileSdk = 37 を設定し、androidx.glance.wear:wear:{GLANCE_WEAR_VERSION} を追加します。 |
toRemoteString の参照を解決できない | toRemoteString をトップレベルの拡張関数としてインポートした | インポートを削除し、メンバーメソッドとして remoteInt.toRemoteString を呼び出します。 |
pendingIntentAction で型が一致しない | ラムダではなく、PendingIntent をそのまま渡した | ラムダでラップします:pendingIntentAction { ctx -> pendingIntent }。 |
移行後に wear.tiles または protolayout の参照を解決できない | モジュール内の他のクラスまたはソースセットが、まだレガシーなタイル API を使っている | build.gradle.kts からレガシーな wear.tiles と protolayout の依存関係を削除する前に、残りの参照を移行または更新します。 |
検証
./gradlew :app:assembleDebugを実行し、ウィジェット、サービス、AndroidManifest.xmlの宣言、および@xml/の<wearwidget-provider>リソースがエラーなくコンパイルされ、リンクされることを確認します。- プロジェクトにユニットテストまたは新しいウィジェットテストが含まれている場合は、
./gradlew :app:testDebugUnitTestを実行し、すべてのテストが成功することを確認します。コンポーザブルのプレビュー、ピッカー用 PNG、またはユニットテストを追加する場合は、必ず プレビューとテストのリファレンス に従ってください。
アンチパターン
- 絶対に、モバイル版の
GlanceAppWidgetまたはAppWidgetProviderを継承したり、Wear OS ウィジェットに非推奨のGlanceTileServiceまたはTileServiceを使ったりしないでください。 - 絶対に、Remote Compose UI でメモリ内のクライアント Compose 状態(
mutableStateOf、mutableIntStateOf)やプロセス内のクリックラムダを使わないでください。 - 絶対に、
PendingIntentインスタンスをそのままpendingIntentActionに渡さないでください。必ず(Context) -> PendingIntentラムダを渡してください。 - ウィジェットの全面背景アートに
CircleShapeまたはRoundedCornerShapeのクリッピングを絶対に適用しないでください。ウィジェットピッカー用 PNG に 1:1(400x400)のタイルプレビューや、単色の空のプレースホルダーを絶対に再利用しないでください。
ベストプラクティス
<service>にcom.google.android.wearable.permission.BIND_TILE_PROVIDERとandroidx.glance.wear.action.BIND_WIDGET_PROVIDERを宣言する必要があります。- ドメインに適した
448x168および464x288の PNG をres/drawable-nodpi/に配置し、@xml/にSMALLとLARGEの<container>エントリーを宣言する必要があります。 - レイアウトのコンテンツを
params.containerTypeに合わせて調整し、SquircleAllWidgetPreviewParamsとRoundAllWidgetPreviewParamsの両方を対象とした@Preview関数を含める必要があります。