このページでは、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、タブレットに表示されるグリッドはそれより大きい 8×7 です。ウィジェットが追加されると、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)x(66m - 15) |
縦向きのセルサイズを使用して、minWidth
、minResizeWidth
、maxResizeWidth
の各属性に指定する値を指定します。同様に、横向きモードのセルサイズを使用して、minHeight
、minResizeHeight
、maxResizeHeight
の各属性に指定する値を指定します。
これは、通常、セルの幅は縦向きよりも横向きの方が狭く、同様に、セルの高さは縦向きよりも横向きの方が狭いためです。
たとえば、Google Pixel 4 でウィジェットの幅を 1 つのセルまでサイズ変更できるようにするには、minResizeWidth
を最大 56 dp に設定して、minResizeWidth
属性の値が 57 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
属性で指定された 3×2 セル、または Android 11 以前を搭載したデバイスの場合は minWidth
属性と minHeight
属性で指定された 180×110 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
として指定されたレイアウトは、180dp(minResizeWidth
)x 110dp(minResizeHeight
)から 269x279dp(次のカットオフ ポイント - 1)まで使用されます。同様に、R.layout.widget_weather_forecast_medium
は 270x110dp ~ 270x279dp で使用され、R.layout.widget_weather_forecast_large
は 270x280dp ~ 530dp(maxResizeWidth
)x 450dp(maxResizeHeight
)で使用されます。
ユーザーがウィジェットのサイズを変更すると、次の例に示すように、セルのサイズに合わせて外観が変化します。