このページでは、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 x 200 dp から 214 dp(次のカットオフ ポイント - 1)x 200 dp をサポートします。wideView
は、215 dp × 110 dp(minResizeHeight
)~ 250 dp(maxResizeWidth
)× 200 dp(maxResizeHeight
)の範囲をサポートします。
ウィジェットは、minResizeWidth
× minResizeHeight
~maxResizeWidth
× maxResizeHeight
のサイズ範囲をサポートする必要があります。この範囲内で、レイアウトを切り替えるカットオフ ポイントを決定できます。
![レスポンシブ レイアウトの例](https://developer.android.google.cn/static/images/appwidgets/size-range.gif?authuser=9&hl=ja)
正確なレイアウトを提供する
少数のレスポンシブ レイアウトを提供できない場合は、ウィジェットを表示するサイズに合わせてさまざまなレイアウトを提供できます。これは通常、スマートフォン用の 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) { }
ウィジェットのサイズを決定する
各ウィジェットでは、Android 12 以降を搭載したデバイスでは targetCellWidth
と targetCellHeight
を定義する必要があります。また、Android のすべてのバージョンで、minWidth
と minHeight
を定義して、デフォルトで消費する最小容量を示す必要があります。ただし、ユーザーがホーム画面にウィジェットを追加すると、通常は、指定した最小の幅と高さよりも大きくなります。
Android のホーム画面には、利用可能なスペースのグリッドが表示され、ユーザーはそこにウィジェットやアイコンを配置できます。このグリッドはデバイスによって異なります。たとえば、多くのハンドセットでは 5x4 のグリッドが、タブレットではより大きなグリッドが提供されています。ウィジェットが追加されると、Android 12 以降を搭載したデバイスでは targetCellWidth
と targetCellHeight
の制約、Android 11(API レベル 30)以下を搭載したデバイスでは minWidth
と minHeight
の制約を満たすために必要な、最小数のセルを横方向と縦方向に占有するように引き伸ばされます。
セルの幅と高さ、およびウィジェットに適用される自動マージンのサイズは、デバイスによって異なります。次の表を使用して、一般的な 5x4 グリッド ハンドセットにおけるウィジェットの最小サイズを大まかに見積もっています。必要な占有グリッドセルの数を考慮します。
セル数(幅×高さ) | 縦表示で使用可能なサイズ(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) x (66m - 15) |
ポートレート モードのセルサイズを使用して、minWidth
属性、minResizeWidth
属性、maxResizeWidth
属性に指定する値を指定します。同様に、横表示のセルサイズを使用して、minHeight
属性、minResizeHeight
属性、maxResizeHeight
属性に指定する値を指定します。
その理由は、通常、縦表示ではセルの幅が横表示よりも小さくなり、同様に、縦表示よりもセルの高さが通常より小さくなるためです。
たとえば、Google Pixel 4 でウィジェットの幅を 1 セルにサイズ変更可能にする場合、minResizeWidth
属性の値を 57 dp より小さくするには、minResizeWidth
を最大 56 dp に設定する必要があります。これは、セルの幅が縦向きで 57 dp 以上であるためです。同様に、同じデバイス上の 1 つのセルでウィジェットの高さをサイズ変更可能にする場合は、minResizeHeight
属性の値を最大 50 dp に設定して、minResizeHeight
属性の値を 51 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
で指定される 180x110 dp です。後者の場合、セルのサイズはデバイスによって異なります。
また、ウィジェットでサポートされるサイズ範囲を設定するには、次の属性を設定します。
<appwidget-provider
android:minResizeWidth="180dp"
android:minResizeHeight="110dp"
android:maxResizeWidth="530dp"
android:maxResizeHeight="450dp">
</appwidget-provider>
上記の属性で指定されているように、ウィジェットの幅は 180 dp から 530 dp にサイズ変更でき、高さは 110 dp から 450 dp にサイズ変更できます。ウィジェットは、次の条件が満たされていれば、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);
ウィジェットでは、上記のコード スニペットで定義されたレスポンシブ レイアウトが使用されているとします。つまり、R.layout.widget_weather_forecast_small
として指定されたレイアウトは、180 dp(minResizeWidth
)x 110 dp(minResizeHeight
)から 269x279 dp(次のカットオフ ポイント - 1)の範囲で使用されます。同様に、R.layout.widget_weather_forecast_medium
は 270x110 dp から 270x279 dp の範囲で使用され、R.layout.widget_weather_forecast_large
は 270x280 dp から 530 dp(maxResizeWidth
)x 450 dp(maxResizeHeight
)の範囲で使用されます。
ユーザーがウィジェットのサイズを変更すると、次の例に示すように、セルの各サイズに合わせてウィジェットの外観が変化します。
![最小の 3x2 グリッドサイズの天気ウィジェットの例。UI には、場所の名前(東京)、気温(14°)、部分曇りを示すシンボルが表示されます。](https://developer.android.google.cn/static/images/appwidgets/weather-size-3x2.png?authuser=9&hl=ja)
R.layout.widget_weather_forecast_small
。![4x2 の「中」サイズの天気ウィジェットの例。この方法でウィジェットのサイズを変更すると、以前のウィジェット サイズの UI がすべて作成され、「ほぼ曇り」というラベルが追加され、午後 4 時から午後 7 時までの気温の予報が追加されます。](https://developer.android.google.cn/static/images/appwidgets/weather-size-4x2.png?authuser=9&hl=ja)
R.layout.widget_weather_forecast_medium
。![5x2 の「中」サイズの天気ウィジェットの例。この方法でウィジェットのサイズを変更すると、前のサイズと同じ UI になりますが、水平方向のスペースをより多く占めるために、1 つのセルの長さ分引き伸ばされます。](https://developer.android.google.cn/static/images/appwidgets/weather-size-5x2.png?authuser=9&hl=ja)
R.layout.widget_weather_forecast_medium
。![5x3 の「L」サイズの天気ウィジェットの例。この方法でウィジェットのサイズを変更すると、以前のウィジェット サイズの UI がすべて作成され、火曜日と水曜日の天気予報を含むビューがウィジェット内に追加されます。晴れまたは雨の日と、毎日の最高気温と最低気温を示す記号。](https://developer.android.google.cn/static/images/appwidgets/weather-size-5x3.png?authuser=9&hl=ja)
R.layout.widget_weather_forecast_large
。![5x4 の「L」サイズの天気ウィジェットの例。この方法でウィジェットのサイズを変更すると、以前のウィジェット サイズのすべての UI を基に、木曜日と金曜日(および天気の種類や各日の最高気温と最低気温を示す対応する記号)が追加されます。](https://developer.android.google.cn/static/images/appwidgets/weather-size-5x4.png?authuser=9&hl=ja)
R.layout.widget_weather_forecast_large
。