Android 12 におけるウィジェットの改善

Android 12 では既存の Widgets API が改善され、プラットフォームやランチャーにおけるユーザーとデベロッパーのエクスペリエンスが向上しています。このガイドでは、ウィジェットを Android 12 に対応させる方法について説明します。また、このガイドは既存のウィジェットを更新するための API のリファレンスとしてもご利用いただけます。

代替テキスト

ウィジェットが Android 12 に対応していることを確認する

Android 12 では、ウィジェットの角が丸くなっています。Android 12 以降を搭載するデバイスでアプリ ウィジェットを使用すると、ランチャーはウィジェットの背景を自動的に識別し、角が丸くなるよう切り抜きます。

このシナリオでは、次のいずれかの状況でウィジェットが正しく表示されないことがあります。

  • ウィジェットの角にコンテンツが含まれている: 角にあるコンテンツの一部が切り取られる可能性があります。

  • ウィジェットで切り抜きにくい背景を使用している。これには、透明な背景、空のビューやレイアウトなどの、切り抜きにくい特殊な背景が含まれます。この場合、使用する背景をシステムが正しく識別できない可能性があります。

ウィジェットがこの変更の影響を受ける場合は、角が丸くなるようにウィジェットを更新し(次のセクションで説明します)、適切に表示されるようにすることをおすすめします。

サンプルを使用する

新しい Widgets API の実際の動作については、リスト ウィジェットのサンプルをご覧ください。

角の丸みを実装する

Android 12 では、次のシステム パラメータが導入され、ウィジェットの角の丸みを設定できるようになりました。

  • system_app_widget_background_radius: ウィジェットの背景の角の丸み。28 dp を超えることはありません。

  • system_app_widget_inner_radius: ウィジェット内のビューの角の丸み。8 dp のパディングを使用する際に適切に調整されるように、背景の角の丸みよりちょうど 8 dp 小さくなっています。

次の例は、system_app_widget_background_radius をウィジェットの角に、system_app_widget_inner_radius をウィジェット内のビューに使用したウィジェットを示しています。

代替テキスト

1 ウィジェットの角。

2 ウィジェット内のビューの角。

角の丸みに関する下位互換性

ウィジェットを以前のバージョンの Android に対応させるには、カスタム属性を定義し、カスタムテーマを使用して Android 12 向けにオーバーライドすることをおすすめします。次に XML ファイルの例を示します。

/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>

/layout/my_widget_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  ...
  android:background="@drawable/my_widget_background" />

動的な色を適用する

Android 12 のウィジェットでは、ボタンや背景などのコンポーネントにデバイスのテーマカラーを使用できます。これにより、ウィジェット間の遷移がスムーズになり、一貫性が保持されます。

次の例では、デバイスのテーマカラーが「茶褐色」であるため、アクセント カラーとウィジェットの背景が調整されています。これを行うには、システムのデフォルト テーマ(@android:style/Theme.DeviceDefault.DayNight)とその色属性を使用します。よく使用される色属性は次のとおりです。

  • ?android:attr/colorAccent
  • ?android:attr/colorBackground
  • ?android:attr/textColorPrimary?android:attr/textColorSecondary
ライトモード テーマのウィジェット
ライトモードのウィジェット
ダークモード テーマのウィジェット
ダークモードのウィジェット

動的な色に関する下位互換性

カスタムテーマを作成し、Android 12 向けにオーバーライドすることをおすすめします。次の例は、各種の XML ファイルでこれを行う方法を示しています。

/values/styles.xml

<resources>
  <style name="MyWidget.TextView">
    <item name="android:textColor">@color/my_text_color</item>
  </style>
  <style name="MyWidgetTheme">
    <item name="textViewStyle">@style/MyWidget.TextView</item>
  </style>
</resources>

/values-31/styles.xml

<resources>
  <style name="MyWidgetTheme" parent="Theme.DeviceDefault.DayNight" />
</resources>

/layout/my_widget_layout.xml

<resources>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:theme="@style/MyWidgetTheme" />
</resources>

ウィジェットを簡単にカスタマイズできるようにする

appwidget-providerconfigure 属性を使用して設定アクティビティを指定した場合、ユーザーがアプリ ウィジェットをホーム画面に追加するとすぐに、アプリ ウィジェット ホストがそのアクティビティを起動します。

Android 12 には、ユーザーが簡単に設定できるようにするための新しいオプションが追加されています。

配置したウィジェットをユーザーが再設定できるようにする

ユーザーはウィジェットを長押しすることで、「再設定可能」のラベルが付いたウィジェットを設定できます。表示される再設定ボタンをタップすると、設定を変更できます。

代替テキスト

1 再設定ボタン。

appwidget-providerwidgetFeatures 属性で reconfigurable フラグを指定します。

<appwidget-provider
  ...
  android:configure="com.myapp.WidgetConfigActivity"
  android:widgetFeatures="reconfigurable">
</appwidget-provider>

ウィジェットのデフォルト設定を使用する

ユーザーがウィジェットを追加するときにデフォルト設定が使用されるようにしたい場合は、widgetFeatures フィールドに configuration_optional フラグと reconfigurable フラグの両方を指定することで、設定ステップをスキップできます。これにより、ユーザーがウィジェットを追加した後の設定アクティビティの起動が省略されます(前述のとおり、ユーザーは後でウィジェットを再設定できます)。

たとえば、時計ウィジェットでは初期設定を省略して、デフォルトでデバイスのタイムゾーンを表示できます。

<appwidget-provider
  ...
  android:configure="com.myapp.WidgetConfigActivity"
  android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

ウィジェットの設定オプションに関する下位互換性

以前のバージョンの Android では、アプリで configuration_optional フラグと reconfigurable フラグを使用できます。ただし、これらのフラグによる影響はなく、引き続き設定アクティビティが起動されます。

新しい複合ボタンを追加する

Android 12 では、以下の既存のコンポーネントを使用したステートフル動作のサポートが追加されています。

このウィジェットはまだステートレスであるため、アプリが状態を保存し、状態変化イベントを登録できるようにする必要があります。

代替テキスト

次のコードサンプルは、これらのコンポーネントを実装する方法を示しています。

Kotlin

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)

// Listen for check changes. The intent will have an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
  R.id.my_checkbox,
  RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)

Java

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true);

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2);

// Listen for check changes. The intent will have an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
  R.id.my_checkbox,
  RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
);

ウィジェットの複合ボタンに関する下位互換性

2 種類のレイアウトを用意します。1 つは Android 12 以降を搭載しているデバイスをターゲットにしたレイアウト(res/layout-v31)で、もう 1 つは以前のバージョンの Android をターゲットにしたレイアウト(デフォルトの res/layout フォルダ内)です。

改良された API を使用してウィジェットのサイズとレイアウトを設定する

Android 12 以降では、次のようにして、サイズ属性とレイアウトをきめ細かく設定できます。

  1. ウィジェットのサイズ調整に関する追加の制約を指定する

  2. レスポンシブ レイアウトまたは正確なレイアウトを提供する

ウィジェットのサイズ調整に関する追加の制約を指定する

Android 12 には、画面サイズの異なるさまざまなデバイスで確実にウィジェットのサイズを調整できるように、新しい API が追加されています。

既存の minWidthminHeightminResizeWidthminResizeHeight 属性に加えて、次の新しい appwidget-provider 属性を使用します。

  • targetCellWidthtargetCellHeight: ランチャーのグリッドセルの観点で、ウィジェットのターゲット サイズを定義します。定義されている場合は、minWidthminHeight の代わりにこれらの属性が使用されます。

  • maxResizeWidthmaxResizeHeight: ランチャーでユーザーがウィジェットのサイズを変更する際に指定できる最大サイズを定義します。

次の XML は、サイズ調整属性の使用方法を示しています。

<appwidget-provider
  ...
  android:targetCellWidth="3"
  android:targetCellHeight="2"
  android:maxResizeWidth="250dp"
  android:maxResizeHeight="110dp">
</appwidget-provider>

レスポンシブ レイアウトを提供する

ウィジェットのサイズに応じてレイアウトを変更する必要がある場合は、さまざまなサイズに対して有効な、少数のレイアウトを作成することをおすすめします(それができない場合は、ランタイムでの正確なウィジェット サイズに基づいてレイアウトを提供する方法もあります)。

この機能を実装すると、ウィジェットのサイズが変わるたびにアプリを復帰させる必要がなくなるため、スムーズなスケーリングが可能になり、全体的なシステムの健全性が改善されます。

次のコードサンプルは、レイアウトのリストを提供する方法を示しています。

Kotlin

override fun onUpdate(...) {
  val smallView = ...
  val tallView = ...
  val wideView = ...

  val viewMapping: Map<SizeF, RemoteViews> = mapOf(
    SizeF(100f, 100f) to smallView,
    SizeF(100f, 200f) to tallView,
    SizeF(200f, 100f) to wideView
  )
  val remoteViews = RemoteViews(viewMapping)

  appWidgetManager.updateAppWidget(id, remoteViews)
}

Java

@Override
public void onUpdate(...) {
  RemoteViews smallView = ...;
  RemoteViews tallView = ...;
  RemoteViews wideView = ...;

  Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
  viewMapping.put(new SizeF(100f, 100f), smallView);
  viewMapping.put(new SizeF(100f, 200f), tallView);
  viewMapping.put(new SizeF(200f, 100f), wideView);
  RemoteViews remoteViews = new RemoteViews(viewMapping);

  appWidgetManager.updateAppWidget(id, remoteViews);
}

正確なレイアウトを提供する

少数のレスポンシブ レイアウトを提供できない場合は、ウィジェットを表示するサイズに合わせてさまざまなレイアウトを提供できます。これは通常、スマートフォン用の 2 つのサイズ(縦向きと横向き)と、折りたたみ式デバイス用の 4 つのサイズで構成されます。

このソリューションを実装するには、アプリで次の手順を行う必要があります。

  1. サイズセットが変更されたときに呼び出される AppWidgetProvider#onAppWidgetOptionsChanged(...) をオーバーロードします。

  2. サイズを含む Bundle を返す getAppWidgetManager#getAppWidgetOptions(...) を呼び出します。

  3. Bundle から AppWidgetManager.OPTION_APPWIDGET_SIZES キーにアクセスします。

次のコードサンプルは、正確なレイアウトを提供する方法を示しています。

Kotlin

// Create the RemoteViews for the given size.
private fun createRemoteViews(size: SizeF): RemoteViews { }

override fun onAppWidgetOptionsChanged(
  context: Context,
  manager: AppWidgetManager,
  id: Int,
  newOptions: Bundle?
) {
  super.onAppWidgetOptionsChanged(context, manager, id, newOptions)
  // Get the new sizes.
  val sizes = newOptions?.getParcelableArrayList<SizeF>(
    AppWidgetManager.OPTION_APPWIDGET_SIZES
  )
  // Check that the list of sizes is provided by the launcher.
  if (sizes.isNullOrEmpty()) {
    return
  }
  // Map the sizes to the desired RemoteViews
  val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews))
  appWidgetManager.updateAppWidget(id, remoteViews)
}

Java

// Create the RemoteViews for the given size.
private RemoteViews createRemoteViews(SizeF size) { }

@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager
  appWidgetManager, int appWidgetId, Bundle newOptions) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    // Get the new sizes.
    ArrayList<SizeF> sizes =
      newOptions.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES);
    // Check that the list of sizes is provided by the launcher.
    if (sizes == null || sizes.isEmpty()) {
      return;
    }
    // Map the sizes to the desired RemoteViews.
    Map<SizesF, RemoteViews> viewMapping = new ArrayMap<>();
    for (SizeF size : sizes) {
      viewMapping.put(size, createRemoteViews(size));
    }
    RemoteViews remoteViews = new RemoteViews(viewMapping);
    appWidgetManager.updateAppWidget(id, remoteViews);
}

ウィジェットのレイアウト サイズに関する下位互換性

以前は、OPTION_APPWIDGET_MIN_WIDTHOPTION_APPWIDGET_MIN_HEIGHTOPTION_APPWIDGET_MAX_WIDTHOPTION_APPWIDGET_MAX_HEIGHT の各エクストラを使用してウィジェットのサイズ範囲を取得し、ウィジェットのサイズを推定することができましたが、このロジックはすべての状況で使用できるわけではありません。Android 12 をターゲットとするウィジェットについては、前述のとおり、代わりにレスポンシブまたは正確なレイアウトを提供することをおすすめします。

アプリのウィジェット選択ツールのエクスペリエンスを改善する

Android 12 では、動的ウィジェットのプレビューとウィジェットの説明を追加することで、アプリのウィジェット選択ツールのエクスペリエンスを改善できます。

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

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

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

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

可能であれば、現実的なデフォルト値またはテスト値を持つ実際のウィジェットと同じレイアウトにします。

スケーラブルなウィジェット プレビューに関する下位互換性

Android 11 以前でウィジェット選択ツールを使用してウィジェットのプレビューを表示するには、引き続き previewImage 属性を指定します。

ウィジェットの外観を変更する場合は、必ずプレビュー画像も更新してください。

ウィジェットの説明を追加する

Android 12 では、必要に応じて、ウィジェットに表示するウィジェット選択ツールの説明を指定できます。

代替テキスト

appwidget-provider の description 属性を使用して、ウィジェットの説明を指定します。

<appwidget-provider
  ...
  android:description="@string/my_widget_description">
</appwidget-provider>

ウィジェットの説明に関する下位互換性

以前のバージョンの Android では、アプリで widgetDescription 属性を使用できますが、ウィジェット選択ツールには表示されません。

スムーズな遷移を有効にする

Android 12 では、ユーザーがウィジェットからアプリを起動すると、ランチャーにより遷移がスムーズに行われます。

この改善された遷移を有効にするには、@android:id/background または android.R.id.background を使用して背景要素を指定します。

// Top level layout of the widget.
<LinearLayout
  ...
  android:id="@android:id/background">
</LinearLayout>

スムーズな遷移に関する下位互換性

アプリは Android の以前のバージョンで @android:id/background を使用できますが、有効にはなりません。

簡素化された RemoteViews コレクションを使用する

Android 12 では、setRemoteAdapter(int viewId, RemoteViews.RemoteCollectionItems items) メソッドが追加されました。このメソッドにより、アプリは ListView の入力時にコレクションを直接渡せるようになります。以前は、ListView を使用する場合、RemoteViewsService を実装して宣言し、RemoteViewsFactory を返す必要がありました。

コレクションで一定数のレイアウトが使用されていない場合(つまり、一部のアイテムがときどきしか存在しない場合)、setViewTypeCount を使用してコレクションに含まれる固有のレイアウトの最大数を指定します。

簡素化された RemoteViews コレクションを実装する方法の例を次に示します。

Kotlin

remoteView.setRemoteAdapter(
  R.id.list_view,
  RemoteViews.RemoteCollectionItems.Builder()
    .addItem(/* id= */ ID_1, RemoteViews(...))
    .addItem(/* id= */ ID_2, RemoteViews(...))
    ...
    .setViewTypeCount(MAX_NUM_DIFFERENT_REMOTE_VIEWS_LAYOUTS)
    .build()
)

Java

remoteView.setRemoteAdapter(
  R.id.list_view,
  new RemoteViews.RemoteCollectionItems.Builder()
    .addItem(/* id= */ ID_1, new RemoteViews(...))
    .addItem(/* id= */ ID_2, new RemoteViews(...))
    ...
    .setViewTypeCount(MAX_NUM_DIFFERENT_REMOTE_VIEWS_LAYOUTS)
    .build()
);

RemoteViews のランタイム変更を使用する

Android 12 では、RemoteViews 属性のランタイム変更を可能にするいくつかの RemoteViews メソッドが追加されています。追加されたメソッドの完全なリストについては、RemoteViews API リファレンスをご覧ください。

次のコードサンプルは、新しいメソッドの使用方法をいくつか示しています。

Kotlin

// Set the colors of a progress bar at runtime.
remoteView.setColorStateList(R.id.progress, "setProgressTintList", createProgressColorStateList())

// Specify exact sizes for margins.
remoteView.setViewLayoutMargin(R.id.text, RemoteViews.MARGIN_END, 8f, TypedValue.COMPLEX_UNIT_DP)

Java

// Set the colors of a progress bar at runtime.
remoteView.setColorStateList(R.id.progress, "setProgressTintList", createProgressColorStateList());

// Specify exact sizes for margins.
remoteView.setViewLayoutMargin(R.id.text, RemoteViews.MARGIN_END, 8f, TypedValue.COMPLEX_UNIT_DP);