生成されたプレビューをウィジェット選択ツールに追加する

生成されたウィジェット プレビューを使用すると、ユーザーのホーム画面に表示されるウィジェットの様子を正確に反映した、動的でパーソナライズされたプレビューを作成できます。これらはプッシュ API を介して提供されます。つまり、アプリはウィジェット ホストから明示的なリクエストを受け取ることなく、ライフサイクルの任意の時点でプレビューを提供します。

アプリのウィジェット選択ツールのエクスペリエンスを改善するには、Android 15 以降のデバイスでは生成されたウィジェットのプレビューを、Android 12 ~ Android 14 のデバイスではスケーリングされたウィジェットのプレビュー(previewLayout を指定)を、それ以前のバージョンでは previewImage を提供します。

詳しくは、YouTube の Enrich your app with live updates and widgets をご覧ください。

生成されたウィジェットのプレビュー用にアプリをセットアップする

Android 15 以降のデバイスで生成されたウィジェットのプレビューを表示するには、まずモジュールの build.gradle ファイルで compileSdk の値を 35 以上に設定して、ウィジェット選択ツールに RemoteViews を提供できるようにします。

アプリは GlanceAppWidgetManager または AppWidgetManager のいずれかで setWidgetPreview を使用できます。不正使用を防ぎ、システムの健全性に関する懸念を軽減するため、setWidgetPreview はレート制限付きの API です。デフォルトの上限は 1 時間あたり約 2 回の呼び出しです。

Jetpack Glance で更新されたプレビューを生成する

Jetpack Glance で構築されたウィジェットの場合は、次の操作を行います。

  1. GlanceAppWidget.providePreview 関数をオーバーライドして、プレビュー用のコンポーズ可能なコンテンツを提供します。provideGlance の場合と同様に、アプリのデータを読み込んでウィジェットのコンテンツ コンポーザブルに渡し、プレビューに正確なデータが表示されるようにします。provideGlance とは異なり、これは再コンポーズやエフェクトのない単一のコンポジションです。

  2. GlanceAppWidgetManager.setWidgetPreviews を呼び出して、プレビューを生成して公開します。

システムからプレビューを提供するコールバックはないため、アプリで setWidgetPreviews を呼び出すタイミングを決定する必要があります。更新戦略は、ウィジェットのユースケースによって異なります。

  • ウィジェットに静的な情報が含まれている場合や、クイック アクションである場合は、アプリの初回起動時にプレビューを設定します。
  • プレビューは、アプリにデータが入力された後(ユーザーのログイン後や初期設定後など)に設定できます。
  • 選択したケイデンスでプレビューを更新する定期的なタスクを設定できます。

生成されたプレビューのトラブルシューティング

一般的な問題は、プレビューを生成した後、ウィジェットのドロップサイズに対して、プレビュー画像から画像、アイコン、その他のコンポーザブルが欠落していることです。このドロップ サイズは、指定されている場合は targetCellWidthtargetCellHeight で定義され、指定されていない場合はアプリ ウィジェット プロバイダ情報ファイルminWidthminHeight で定義されます。

これは、Android がデフォルトで、ウィジェットの最小サイズで表示されるコンポーザブルのみをレンダリングするためです。つまり、Android はデフォルトで previewSizeModeSizeMode.Single に設定します。アプリ ウィジェット プロバイダ情報 XMLandroid:minHeightandroid:minWidth を使用して、描画するコンポーザブルを決定します。

これを修正するには、GlanceAppWidgetpreviewSizeMode をオーバーライドして SizeMode.Responsive に設定し、DpSize 値のセットを指定します。これにより、プレビューのレンダリングに必要なすべてのレイアウト サイズが Android に通知され、すべての要素が正しく表示されます。

特定のフォーム ファクタ向けに最適化します。最小サイズからウィジェットのブレークポイントに沿って、1 つまたは 2 つのサイズを指定します。下位互換性のために、少なくとも 1 つのイメージを指定します。さまざまなグリッド サイズに適した最小 DP 値については、ウィジェット デザイン ガイダンスをご覧ください。

Jetpack Glance を使用せずに更新されたプレビューを生成

Glance なしで RemoteViews を使用できます。次の例では、XML ウィジェット レイアウト リソースを読み込み、プレビューとして設定します。このスニペットで setWidgetPreview をメソッドとして表示するには、compileSdk ビルド設定が 35 以降である必要があります。

AppWidgetManager.getInstance(appContext).setWidgetPreview(
    ComponentName(
        appContext,
        ExampleAppWidgetReceiver::class.java
    ),
    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
    RemoteViews("com.example", R.layout.widget_preview)
)

ウィジェット選択ツールにスケーラブルなウィジェット プレビューを追加する

Android 12 以降では、ウィジェット選択ツールに表示されるウィジェットのプレビューはスケーラブルです。これは、ウィジェットのデフォルトのサイズに設定された XML レイアウトとして提供されます。以前は、ウィジェット プレビューは静的なドローアブル リソースだったため、場合によっては、ウィジェットをホーム画面に追加したときの表示がプレビューに正確に反映されないことがありました。

スケーラブルなウィジェット プレビューを実装するには、appwidget-provider 要素の previewLayout 属性を使用して、代わりに XML レイアウトを提供します。

<appwidget-provider
    android:previewLayout="@layout/my_widget_preview">
</appwidget-provider>

現実的なデフォルト値またはテスト値を持つ実際のウィジェットと同じレイアウトを使用することをおすすめします。ほとんどのアプリは同じ previewLayoutinitialLayout を使用します。正確なプレビュー レイアウトを作成するためのガイダンスについては、このページの次のセクションをご覧ください。

ユーザーのデバイスが previewLayout をサポートしていない場合にアプリが previewImage を使用するようにフォールバックできるように、previewLayout 属性と previewImage 属性の両方を指定することをおすすめします。previewLayout 属性は previewImage 属性よりも優先されます。

ウィジェット プレビューに関する下位互換性

Android 11(API レベル 30)以下でウィジェット選択ツールを使用してウィジェットのプレビューを表示するには、または生成されたプレビューのフォールバックとして、previewImage 属性を指定します。

ウィジェットの外観を変更する場合は、プレビュー画像を更新します。

動的アイテムを含む正確なプレビューを作成する

図 1: リスト項目が表示されていないウィジェットのプレビュー。

このセクションでは、コレクション ビューListViewGridViewStackView を使用するウィジェット)を含むウィジェットのウィジェット プレビューに複数のアイテムを表示する場合の推奨アプローチについて説明します。これは、生成されたウィジェットのプレビューには適用されません。

ウィジェットがこれらのビューのいずれかを使用している場合、実際のウィジェット レイアウトを直接提供することでスケーラブルなプレビューを作成すると、ウィジェット プレビューにアイテムが表示されないときにエクスペリエンスが低下します。これは、コレクション ビューのデータが実行時に動的に設定されるためで、図 1 に示す画像と似たような表示になります。

コレクション ビューを含むウィジェットのプレビューをウィジェット ピッカーで正しく表示するには、プレビュー専用の個別のレイアウト ファイルを維持することをおすすめします。この別のレイアウト ファイルには、次のものを含める必要があります。

  • 実際のウィジェットのレイアウト。
  • 偽のアイテムを含むプレースホルダ コレクション ビュー。たとえば、複数の偽のリスト項目を含むプレースホルダ LinearLayout を指定することで、ListView を模倣できます。

ListView の例を示すため、別のレイアウト ファイルから始めます。

// res/layout/widget_preview.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:background="@drawable/widget_background"
   android:orientation="vertical">

    // Include the actual widget layout that contains ListView.
    <include
        layout="@layout/widget_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    // The number of fake items you include depends on the values you provide
    // for minHeight or targetCellHeight in the AppWidgetProviderInfo
    // definition.

    <TextView android:text="@string/fake_item1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="?attr/appWidgetInternalPadding" />

    <TextView android:text="@string/fake_item2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="?attr/appWidgetInternalPadding" />

</LinearLayout>

AppWidgetProviderInfo メタデータの previewLayout 属性を指定するときに、プレビュー レイアウト ファイルを指定します。initialLayout 属性の実際のウィジェット レイアウトは指定し、実行時に RemoteViews を構築するときに実際のウィジェット レイアウトを使用します。

<appwidget-provider
    previewLayout="@layout/widget_previe"
    initialLayout="@layout/widget_view" />

複雑なリストアイテム

前のセクションの例では、リストアイテムが TextView オブジェクトであるため、偽のリストアイテムが提供されます。アイテムが複雑なレイアウトの場合、偽のアイテムを提供するのは複雑になる可能性があります。

widget_list_item.xml で定義され、2 つの TextView オブジェクトで構成されるリストアイテムについて考えてみましょう。

<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    <TextView android:id="@id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/fake_title" />

    <TextView android:id="@id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/fake_content" />
</LinearLayout>

偽のリスト項目を提供するには、レイアウトを複数回含めることができますが、これにより各リスト項目が同一になります。一意のリスト項目を提供するには、次の手順を行います。

  1. テキスト値の属性のセットを作成します。

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. 次の属性を使用してテキストを設定します。

    <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        <TextView android:id="@id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="?widgetTitle" />
    
        <TextView android:id="@id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="?widgetContent" />
    </LinearLayout>
    
  3. プレビューに必要な数のスタイルを作成します。各スタイルの値を再定義します。

    <resources>
    
        <style name="Theme.Widget.ListItem">
            <item name="widgetTitle"></item>
            <item name="widgetContent"></item>
        </style>
        <style name="Theme.Widget.ListItem.Preview1">
            <item name="widgetTitle">Fake Title 1</item>
            <item name="widgetContent">Fake content 1</item>
        </style>
        <style name="Theme.Widget.ListItem.Preview2">
            <item name="widgetTitle">Fake title 2</item>
            <item name="widgetContent">Fake content 2</item>
        </style>
    
    </resources>
    
  4. プレビュー レイアウトのフェイク アイテムにスタイルを適用します。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" ...>
    
        <include layout="@layout/widget_view" ... />
    
        <include layout="@layout/widget_list_item"
            android:theme="@style/Theme.Widget.ListItem.Preview1" />
    
        <include layout="@layout/widget_list_item"
            android:theme="@style/Theme.Widget.ListItem.Preview2" />
    
    </LinearLayout>