Glance でアプリ ウィジェットを作成する

マニフェスト、メタデータ

以降のセクションでは、Glance で基本的なアプリ ウィジェットを作成する方法について説明します。

マニフェストで AppWidget を宣言する

設定手順を完了したら、アプリで AppWidget とその メタデータを宣言します。

  1. GlanceAppWidgetReceiver から AppWidget レシーバを拡張します。

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
        override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
    }

  2. AndroidManifest.xml ファイルと関連するメタデータ ファイルで、アプリ ウィジェットのプロバイダを登録します。

        <receiver android:name=".glance.MyReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/my_app_widget_info" />
    </receiver>
    

AppWidgetProviderInfo メタデータを追加する

次に、ウィジェットを作成するガイドに沿って、@xml/my_app_widget_info ファイルでアプリ ウィジェット情報を定義します。

Glance の唯一の違いは、initialLayout XML がないことですが、定義する必要があります。ライブラリで提供されている事前定義済みの読み込みレイアウトを使用できます。

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

AppWidgetProviderInfo XML を宣言する

AppWidgetProviderInfo オブジェクトは、ウィジェットの重要な特性を定義します。XML メタデータ リソース ファイル (res/xml/my_app_widget_info.xml)の <appwidget-provider> 要素内で AppWidgetProviderInfo を定義します。

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:targetCellWidth="1"
    android:targetCellHeight="1"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="120dp"
    android:updatePeriodMillis="86400000"
    android:description="@string/example_appwidget_description"
    android:previewLayout="@layout/example_appwidget_preview"
    android:initialLayout="@layout/glance_default_loading_layout"
    android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen"
    android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

ウィジェットのサイズ設定属性

デフォルトのホーム画面では、定義された高さと幅のセルのグリッドに基づいて、ウィンドウにウィジェットが配置されます。ほとんどのホーム画面では、ウィジェットのサイズはグリッドセルの整数倍にしかできません(たとえば、横 2 セル、縦 3 セル)。

ウィジェットのサイズ設定属性を使用すると、ウィジェットのデフォルト サイズを指定し、ウィジェットのサイズの下限と上限を設定できます。このコンテキストでは、ウィジェットのデフォルト サイズは、ウィジェットがホーム画面に最初に追加されたときのサイズです。

次の表に、ウィジェットのサイズ設定に関連する <appwidget-provider> 属性を示します。

属性と説明
targetCellWidthtargetCellHeight(Android 12)、 minWidthminHeight
  • Android 12 以降では、 targetCellWidthtargetCellHeight 属性で、グリッド セル単位のウィジェットのデフォルト サイズを指定します。これらの属性は、Android 11 以前では無視されます。また、ホーム画面がグリッドベースのレイアウトをサポートしていない場合は無視できます。
  • minWidth 属性と minHeight 属性で、dp 単位のウィジェットのデフォルト サイズを指定します。ウィジェットの最小幅または高さの値がセルのサイズと一致しない場合は、最も近いセルサイズに切り上げられます。
両方の属性セット(targetCellWidthtargetCellHeightminWidthminHeight)を指定することをおすすめします。これにより、ユーザーのデバイスが targetCellWidthtargetCellHeight をサポートしていない場合、アプリは minWidthminHeight を使用できます。サポートされている場合、 targetCellWidth 属性と targetCellHeight 属性は、minWidth 属性と minHeight 属性よりも優先されます。
minResizeWidthminResizeHeight ウィジェットの絶対最小サイズを指定します。これらの値は、 ウィジェットが読みにくい、または使用できないサイズを指定します。これらの属性を使用すると、ユーザーはウィジェットのサイズをデフォルトのウィジェット サイズよりも小さく変更できます。minResizeWidth 属性は、minWidth より大きい場合、または横方向のサイズ変更が有効になっていない場合は無視されます。 をご覧ください。resizeMode同様に、 minResizeHeight 属性は、 minHeight より大きい場合、または縦方向のサイズ変更が有効になっていない場合は無視されます。
maxResizeWidthmaxResizeHeight ウィジェットの推奨最大サイズを指定します。値がグリッドセルのサイズの倍数でない場合は、最も近いセルサイズに切り上げられます。maxResizeWidth 属性は、minWidth より小さい場合、または横方向のサイズ変更が有効になっていない場合は無視されます。resizeMode をご覧ください。同様に、maxResizeHeight 属性は、minHeight より小さい場合、または縦方向のサイズ変更が有効になっていない場合は無視されます。Android 12 で導入されました。
resizeMode ウィジェットのサイズを変更できるルールを指定します。この属性を使用すると、ホーム画面ウィジェットのサイズを横方向、縦方向、 またはその両方で変更できます。ユーザーがウィジェットを長押しするとサイズ変更ハンドルが表示され、横または縦のハンドルをドラッグしてレイアウト グリッド上でサイズを変更できるようになります。`resizeMode` 属性の値には、`horizontal`、`vertical`、`none` があります。縦と横の両方のサイズを変更できるウィジェットを宣言するには、horizontal|vertical を使用します。

上記の表の属性がウィジェットのサイズ設定にどのように影響するかを示すために、次の仕様を想定します。

  • グリッドセルの幅は 30 dp、高さは 50 dp です。
  • 次の属性仕様が提供されています。
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="80dp"
    android:minHeight="80dp"
    android:targetCellWidth="2"
    android:targetCellHeight="2"
    android:minResizeWidth="40dp"
    android:minResizeHeight="40dp"
    android:maxResizeWidth="120dp"
    android:maxResizeHeight="120dp"
    android:resizeMode="horizontal|vertical" />

Android 12 以降:

targetCellWidth 属性と targetCellHeight 属性をウィジェットのデフォルト サイズとして使用します。

ウィジェットのサイズはデフォルトで 2x2 です。ウィジェットのサイズは 2x1 から 4x3 まで変更できます。

Android 11 以前:

minWidth 属性と minHeight 属性を使用して、ウィジェットのデフォルト サイズを計算します。

デフォルトの幅 = Math.ceil(80 / 30) = 3

デフォルトの高さ = Math.ceil(80 / 50) = 2

ウィジェットのサイズはデフォルトで 3x2 です。ウィジェットのサイズは 2x1 から全画面表示まで変更できます。

その他のウィジェット属性

次の表に、ウィジェットのサイズ設定以外の特性に関連する <appwidget-provider> 属性を示します。

属性と説明
updatePeriodMillis ウィジェット フレームワークが GlanceAppWidgetReceiver から更新をリクエストする頻度を、onUpdate() コールバック メソッドを呼び出して定義します。バッテリーを節約するため、更新はできるだけ頻繁に行わないことをおすすめします(1 時間に 1 回以下)。 詳細については、Glance の状態管理のウィジェットを更新するタイミングをご覧ください。
initialLayout Glance UI コンポジションがレンダリングされる前に、ウィジェットの読み込みレイアウトを定義するレイアウト リソースを指定します。ライブラリで提供されている事前定義済みの読み込みレイアウト(@layout/glance_default_loading_layout)を使用できます。
configure ユーザーがウィジェットを追加したときに起動する構成アクティビティを定義します。このページのウィジェット構成アクティビティを実装するをご覧ください。
description ウィジェット選択ツールに表示するウィジェットの説明を指定します。Android 12 で導入されました。
previewLayout(Android 12)と previewImage(Android 11 以前)
  • Android 12 以降では、 previewLayout 属性でスケーラブルなプレビューを指定します。これは、 ウィジェットのデフォルト サイズに設定された XML レイアウトとして提供されます。理想的には、 デザイン レイアウトに一致する静的な XML マッピングを指定します。
  • Android 11 以前では、previewImage 属性で、ウィジェット選択ツールに表示されるウィジェットの静止画像ドローアブル スクリーンショットを指定します。
両方を指定することをおすすめします。これにより、古いプラットフォームでもアプリが正常にフォールバックされます。新しいプラットフォーム(Android 15 以降)では、`GlanceAppWidget.providePreview` を使用して Kotlin でライブ生成プレビューを定義できます。生成されたプレビュー ガイドをご覧ください。
autoAdvanceViewId ウィジェットのホストによって自動的に進められるウィジェット サブビューのビュー ID を指定します。
widgetCategory ウィジェットをホーム画面 (home_screen)、ロック画面(keyguard)、またはその両方に表示できるかどうかを宣言します。Android 5.0 以降では、home_screen のみが有効です。
widgetFeatures ウィジェットでサポートされている機能を宣言します。たとえば、ウィジェットの構成が省略可能な場合は、configuration_optionalreconfigurable の両方を指定します。

GlanceAppWidget を定義する

  1. GlanceAppWidget から拡張し、 provideGlance メソッドをオーバーライドする新しいクラスを作成します。これは、ウィジェットのレンダリングに必要なデータを読み込むメソッドです。

    class MyAppWidget : GlanceAppWidget() {
    
        override suspend fun provideGlance(context: Context, id: GlanceId) {
    
            // In this method, load data needed to render the AppWidget.
            // Use `withContext` to switch to another thread for long running
            // operations.
    
            provideContent {
                // create your AppWidget here
                Text("Hello World")
            }
        }
    }

  2. GlanceAppWidgetReceiverglanceAppWidget でインスタンス化します。

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
    
        // Let MyAppWidgetReceiver know which GlanceAppWidget to use
        override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
    }

これで、Glance を使用して AppWidget を構成しました。

AppWidgetProvider クラスを使用してウィジェットのブロードキャストを処理する

GlanceAppWidgetReceiver は、基盤となる AppWidgetProvider を拡張して、ウィジェットのブロードキャストとプラットフォームの状態 の更新を調整します。ウィジェットが更新、削除、有効化、無効化されると、プラットフォーム イベントを受信し、Compose ライフサイクル リクエストに変換します。

マニフェストでウィジェットを宣言する

AndroidManifest.xml ファイルで、GlanceAppWidgetReceiver クラス サブクラスをブロードキャスト レシーバとして宣言します。

<receiver android:name="ExampleAppWidgetReceiver"
          android:exported="false">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/my_app_widget_info" />
</receiver>

<receiver> 要素には、レシーバ クラスを指定する android:name 属性が必要です。レシーバは、<intent-filter> 内で ACTION_APPWIDGET_UPDATE ブロードキャスト アクションを受け入れる必要があります。

<meta-data> 要素では、名前を android.appwidget.provider として識別する必要があります。また、android:resource 属性は AppWidgetProviderInfo XML メタデータ リソース(@xml/my_app_widget_info)を指定する必要があります。

AppWidgetProvider クラスを実装する

Glance では、AppWidgetProvider を直接拡張するのではなく、GlanceAppWidgetReceiver を拡張します。レシーバを GlanceAppWidget インスタンスにリンクして実装します。GlanceAppWidgetReceiver で使用できる主なコールバックは次のように動作します。

  • onUpdate(): コンポジションの更新を実行するために Glance によって自動的にオーバーライドされます。`onUpdate` を手動でオーバーライドする場合は、Glance がコンポジション スレッドを正常に起動できるように、**`super.onUpdate` を呼び出す必要があります**super.onUpdate
  • onAppWidgetOptionsChanged(): ウィジェットが最初に配置されたとき、またはサイズ変更されたときに呼び出されます。Glance は、オプション バンドル アイテムを内部で読み取るため、レイアウトは実行時のサイズに基づいてシームレスに調整されます。
  • onDeleted(Context, IntArray): ユーザーが特定のウィジェット インスタンスを削除するたびに呼び出されます。
  • onEnabled(Context): ウィジェットの最初のインスタンスが正常に作成されたときにトリガーされます。グローバル移行の実行に最適です。
  • onDisabled(Context): プロバイダの最後のアクティブなインスタンスが削除されたときに呼び出されます。
  • onReceive(Context, Intent): 特定のコールバック メソッドの前に、すべてのプラットフォーム ブロードキャストをインターセプトします。作成するカスタム レシーバ ロジック で super.onReceive(context, intent) が呼び出されるようにする必要があります。Glance は自動的に非同期で処理をルーティングするため、goAsync を自分で呼び出すことはできません

ウィジェットのブロードキャスト インテントを受信する

内部的には、GlanceAppWidgetReceiver は次の基本的なプラットフォーム ウィジェット ブロードキャスト インテントをフィルタして処理します。

UI を作成する

次のスニペットは、UI を作成する方法を示しています。

/* Import Glance Composables
 In the event there is a name clash with the Compose classes of the same name,
 you may rename the imports per https://kotlinlang.org/docs/packages.html#imports
 using the `as` keyword.

import androidx.glance.Button
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.text.Text
*/
class MyAppWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // Load data needed to render the AppWidget.
        // Use `withContext` to switch to another thread for long running
        // operations.

        provideContent {
            // create your AppWidget here
            MyContent()
        }
    }

    @Composable
    private fun MyContent() {
        Column(
            modifier = GlanceModifier.fillMaxSize(),
            verticalAlignment = Alignment.Top,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button(
                    text = "Home",
                    onClick = actionStartActivity<MyActivity>()
                )
                Button(
                    text = "Work",
                    onClick = actionStartActivity<MyActivity>()
                )
            }
        }
    }
}

上記のコードサンプルは次の処理を行います。

  • 最上位の Column では、アイテムが縦に配置されます。
  • Column は、使用可能なスペースに合わせてサイズを拡大し( GlanceModifier を使用)、コンテンツを上部 (verticalAlignment)に揃え、水平方向の中央(horizontalAlignment)に配置します。
  • Column のコンテンツは、ラムダを使用して定義されます。順序が重要です。
    • Column の最初のアイテムは、パディングが 12.dpText コンポーネントです。
    • 2 番目のアイテムは Row で、アイテムは横に配置され 、互いに並んで、2 つの Buttons が水平方向の中央 (horizontalAlignment)に配置されます。最終的な表示は、使用可能な スペースによって異なります。次の画像は、表示例です。
destination_widget
図 1.UI の例。

配置の値の変更や、さまざまな修飾子値(パディングなど)の適用により、コンポーネントの配置とサイズを変更できます。各クラスのコンポーネント、パラメータ、使用可能な 修飾子の完全なリストについては、リファレンス ドキュメントをご覧ください。

角の丸みを実装する

Android 12 では、システム パラメータが導入され、アプリ ウィジェットの角の丸みを動的にカスタマイズできるようになりました。

  • system_app_widget_background_radius: ウィジェットの背景コンテナの角の丸みを指定します(28 dp を超えることはありません)。
  • 内側の半径: コンテンツのクリッピングを防ぐため、システム背景のアウトラインに基づいて内側のコンテンツの比例半径を計算します。 systemRadiusValue - widgetPadding

Glance では、GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius) を使用して、コンポジションで角の丸みのサイズ設定プロパティを動的に適用できます。

Android 11(API レベル 30)以前を搭載しているデバイスとの下位互換性を確保するため、カスタム属性とカスタム テーマ リソースのフォールバックを実装します。

  • /values/attrs.xml

    <resources>
    <attr name="backgroundRadius" format="dimension" />
    </resources>
    
  • /values/styles.xml

    <resources>
    <style name="MyWidgetTheme">
      <item name="backgroundRadius">@dimen/my_background_radius_dimen</item>
    </style>
    </resources>
    
  • /values-31/styles.xml

    <resources>
    <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
      <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item>
    </style>
    </resources>
    
  • /drawable/my_widget_background.xml

    <shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="?attr/backgroundRadius" />
    </shape>