このページでは、Android 12(API レベル 31)で導入されたウィジェットのサイズ変更の改善と柔軟性の向上について説明します。また、ウィジェットのサイズを決定する方法についても詳しく説明します。
改良された API を使用してウィジェットのサイズとレイアウトを設定する
Android 12(API レベル 31)以降では、次のセクションで説明するように、次のようにして、サイズ属性とレイアウトをきめ細かく設定できます。
以前のバージョンの Android では、OPTION_APPWIDGET_MIN_WIDTH、OPTION_APPWIDGET_MIN_HEIGHT、OPTION_APPWIDGET_MAX_WIDTH、OPTION_APPWIDGET_MAX_HEIGHT の各エクストラを使用してウィジェットのサイズ範囲を取得し、ウィジェットのサイズを推定することができましたが、このロジックはすべての状況で使用できるわけではありません。Android 12 以上をターゲットとするウィジェットについては、レスポンシブまたは正確なレイアウトを提供することをおすすめします。
ウィジェットのサイズ調整に関する追加の制約を指定する
Android 12 には、画面サイズの異なるさまざまなデバイスで確実にウィジェットのサイズを調整できるようにする API が追加されています。
既存の minWidth、minHeight、minResizeWidth、minResizeHeight 属性に加えて、次の新しい appwidget-provider 属性を使用します。
- targetCellWidth、- targetCellHeight: ランチャーのグリッドセルの観点で、ウィジェットのターゲット サイズを定義します。定義されている場合は、- minWidthや- minHeightの代わりにこれらの属性が使用されます。
- maxResizeWidth、- maxResizeHeight: ランチャーでユーザーがウィジェットのサイズを変更する際に指定できる最大サイズを定義します。
次の 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(150f, 100f) to smallView, SizeF(150f, 200f) to tallView, SizeF(215f, 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(150f, 100f), smallView); viewMapping.put(new SizeF(150f, 200f), tallView); viewMapping.put(new SizeF(215f, 100f), wideView); RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews); }
ウィジェットに次の属性があるとします。
<appwidget-provider
    android:minResizeWidth="160dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="200dp">
</appwidget-provider>
上記のコード スニペットは、次のことを意味します。
- smallViewは 160 dp(- minResizeWidth)× 110 dp(- minResizeHeight)から 160 dp × 199 dp(次のカットオフ ポイント - 1 dp)までをサポートします。
- tallViewは 160 dp × 200 dp から 214 dp(次のカットオフ ポイント - 1)× 200 dp までをサポートします。
- wideViewは 215 dp × 110 dp(- minResizeHeight)から 250 dp(- maxResizeWidth)× 200 dp(- maxResizeHeight)までをサポートします。
ウィジェットは、minResizeWidth × minResizeHeight から maxResizeWidth × maxResizeHeight までのサイズ範囲をサポートする必要があります。この範囲内で、レイアウトを切り替えるカットオフ ポイントを決定できます。
 
  正確なレイアウトを提供する
少数のレスポンシブ レイアウトを提供できない場合は、ウィジェットを表示するサイズに合わせてさまざまなレイアウトを提供できます。これは通常、スマートフォン用の 2 つのサイズ(縦向きと横向き)と、折りたたみ式デバイス用の 4 つのサイズで構成されます。
このソリューションを実装するには、アプリで次の手順を行う必要があります。
- サイズセットが変更されたときに呼び出される - AppWidgetProvider.onAppWidgetOptionsChanged()をオーバーロードします。
- サイズを含む - Bundleを返す- AppWidgetManager.getAppWidgetOptions()を呼び出します。
- Bundleから- AppWidgetManager.OPTION_APPWIDGET_SIZESキーにアクセスします。
次のコードサンプルは、正確なレイアウトを提供する方法を示しています。
Kotlin
override fun onAppWidgetOptionsChanged( context: Context, appWidgetManager: AppWidgetManager, id: Int, newOptions: Bundle? ) { super.onAppWidgetOptionsChanged(context, appWidgetManager, 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 RemoteViews that you want. val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews)) appWidgetManager.updateAppWidget(id, remoteViews) } // Create the RemoteViews for the given size. private fun createRemoteViews(size: SizeF): RemoteViews { }
Java
@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 RemoteViews that you want. Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>(); for (SizeF size : sizes) { viewMapping.put(size, createRemoteViews(size)); } RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews); } // Create the RemoteViews for the given size. private RemoteViews createRemoteViews(SizeF size) { }
ウィジェットのサイズを決定する
各ウィジェットでは、デフォルトで使用する最小スペースを示す targetCellWidth と targetCellHeight(Android 12 以降を搭載したデバイスの場合)または minWidth と minHeight(すべてのバージョンの Android の場合)を定義する必要があります。ただし、ユーザーがウィジェットをホーム画面に追加すると、通常は指定した最小の幅と高さより大きいスペースが占有されます。
Android のホーム画面には、ウィジェットやアイコンを配置可能なスペースのグリッドがユーザーに表示されます。このグリッドはデバイスによって異なります。たとえば、多くのスマートフォンに表示されるグリッドは 5×4、タブレットに表示されるグリッドはそれより大きいものです。ウィジェットが追加されると、Android 12 以降を搭載したデバイスでは targetCellWidth と targetCellHeight の制約、Android 11(API レベル 30)以前を搭載したデバイスでは minWidth と minHeight の制約を満たすために必要な最小限の数のセルを占有するように、グリッドが縦方向と横方向に引き伸ばされます。
セルの幅と高さ、およびウィジェットに適用される自動マージンのサイズは、デバイスによって異なります。次の表を使用して、一般的な 5x4 グリッドのハンドセットで、必要なグリッドセルの数からウィジェットのおおよその最小サイズを見積もることができます。
| セルの数(幅 x 高さ) | 縦向きモードで使用可能なサイズ(dp) | 横向きモードで使用可能なサイズ(dp) | 
|---|---|---|
| 1x1 | 57x102dp | 127x51dp | 
| 2x1 | 130x102dp | 269x51dp | 
| 3x1 | 203x102dp | 412x51dp | 
| 4x1 | 276x102dp | 554x51dp | 
| 5x1 | 349x102dp | 697x51dp | 
| 5x2 | 349x220dp | 697x117dp | 
| 5x3 | 349x337dp | 697x184dp | 
| 5x4 | 349x455dp | 697x250dp | 
| ... | ... | ... | 
| n x m | (73n - 16) x (118m - 16) | (142n - 15)×(66m - 15) | 
ポートレート モードのセルサイズを使用して、minWidth、minResizeWidth、maxResizeWidth の各属性に指定する値を決定します。同様に、横向きモードのセルサイズを使用して、minHeight、minResizeHeight、maxResizeHeight 属性に指定する値を通知します。
これは、通常、縦向きモードではセルの幅が横向きモードよりも小さく、同様に、横向きモードではセルの高さが縦向きモードよりも小さいためです。
たとえば、Google Pixel 4 でウィジェットの幅を 1 セルまでサイズ変更できるようにするには、minResizeWidth 属性の値が 57 dp より小さくなるように、minResizeWidth を最大 56 dp に設定する必要があります。これは、縦向きのセルの幅が少なくとも 57 dp あるためです。同様に、同じデバイスの 1 つのセルでウィジェットの高さをサイズ変更可能にするには、minResizeHeight 属性の値が 51 dp より小さくなるように、minResizeHeight を最大 50 dp に設定する必要があります。これは、横向きモードでは 1 つのセルの高さが少なくとも 51 dp になるためです。
各ウィジェットは、minResizeWidth/minResizeHeight 属性と maxResizeWidth/maxResizeHeight 属性の間のサイズ範囲内でサイズ変更できます。つまり、これらの間の任意のサイズ範囲に適応する必要があります。
たとえば、プレースメントに配置するウィジェットのデフォルト サイズを設定するには、次の属性を設定します。
<appwidget-provider
    android:targetCellWidth="3"
    android:targetCellHeight="2"
    android:minWidth="180dp"
    android:minHeight="110dp">
</appwidget-provider>
つまり、ウィジェットのデフォルト サイズは、targetCellWidth 属性と targetCellHeight 属性で指定されている 3x2 セル、または Android 11 以前を搭載しているデバイスの場合は minWidth 属性と minHeight 属性で指定されている 180×110dp になります。後者の場合、セルのサイズはデバイスによって異なります。
また、ウィジェットのサポート対象のサイズ範囲を設定するには、次の属性を設定します。
<appwidget-provider
    android:minResizeWidth="180dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="530dp"
    android:maxResizeHeight="450dp">
</appwidget-provider>
上記の属性で指定されているように、ウィジェットの幅は 180dp から 530dp まで、高さは 110dp から 450dp までサイズ変更できます。ウィジェットは、次の条件が満たされている限り、3x2 セルから 5x2 セルまでサイズ変更できます。
- デバイスに 5x4 のグリッドがある。
- セルの数と利用可能なサイズ(dps)のマッピングは、このページの最小寸法を推定する表に準拠します。
- ウィジェットはそのサイズ範囲に適応します。
Kotlin
val smallView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_small) val mediumView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_medium) val largeView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_large) val viewMapping: Map<SizeF, RemoteViews> = mapOf( SizeF(180f, 110f) to smallView, SizeF(270f, 110f) to mediumView, SizeF(270f, 280f) to largeView ) appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))
Java
RemoteViews smallView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_small); RemoteViews mediumView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_medium); RemoteViews largeView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_large); Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>(); viewMapping.put(new SizeF(180f, 110f), smallView); viewMapping.put(new SizeF(270f, 110f), mediumView); viewMapping.put(new SizeF(270f, 280f), largeView); RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews);
ウィジェットが、上記のコード スニペットで定義されているレスポンシブ レイアウトを使用しているとします。つまり、180dp(minResizeWidth)× 110dp(minResizeHeight)から 269×279dp(次のカットオフ ポイント - 1)まで、R.layout.widget_weather_forecast_small として指定されたレイアウトが使用されます。同様に、R.layout.widget_weather_forecast_medium は 270x110dp から 270x279dp まで、R.layout.widget_weather_forecast_large は 270x280dp から 530dp(maxResizeWidth)x 450dp(maxResizeHeight)まで使用されます。
ユーザーがウィジェットのサイズを変更すると、次の例に示すように、セルの各サイズに合わせて外観が変化します。
 
  R.layout.widget_weather_forecast_small. 
  R.layout.widget_weather_forecast_medium. 
  R.layout.widget_weather_forecast_medium。 
  R.layout.widget_weather_forecast_large。 
  R.layout.widget_weather_forecast_large。 
  