本页面介绍了 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
extra 获取小部件的大小范围,然后估算小部件的尺寸,但这在所有情况下都不适用。对于以 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
支持从 160dp(minResizeWidth
)× 110dp(minResizeHeight
)到 160dp × 199dp(下一个截止点 - 1dp)的尺寸。tallView
支持从 160dp × 200dp 到 214dp(下一个截止点 - 1)× 200dp 的尺寸。wideView
支持从 215dp × 110dp(minResizeHeight
)到 250dp(maxResizeWidth
)× 200dp(maxResizeHeight
)的尺寸。
您的小部件必须支持从 minResizeWidth
× minResizeHeight
到 maxResizeWidth
× maxResizeHeight
的尺寸范围。在该范围内,您可以决定切换布局的截止点。

提供精确布局
如果无法使用一组少量的响应式布局,您可以改为提供根据小部件显示尺寸量身定制的不同布局。对于手机,这通常是两种尺寸(纵向和横向模式),对于可折叠设备,则是四种尺寸。
要实现此解决方案,您的应用需要执行以下步骤
重载
AppWidgetProvider.onAppWidgetOptionsChanged()
,该方法在尺寸集合发生变化时调用。调用
AppWidgetManager.getAppWidgetOptions()
,它会返回一个包含尺寸的Bundle
。从
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 网格手机中,给定您希望占用的网格单元格数量时,您的小部件的最小尺寸
单元格数量(宽 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 上将小部件宽度调整到最小为一个单元格,则需要将 minResizeWidth
设置为最多 56dp,以确保 minResizeWidth
属性的值小于 57dp(因为在纵向模式下,一个单元格的宽度至少为 57dp)。同样,如果您希望在同一设备上将小部件高度调整到最小为一个单元格,则需要将 minResizeHeight
设置为最多 50dp,以确保 minResizeHeight
属性的值小于 51dp(因为在横向模式下,一个单元格的高度至少为 51dp)。
每个小部件可以在 minResizeWidth
/minResizeHeight
和 maxResizeWidth
/maxResizeHeight
属性之间的尺寸范围内调整大小,这意味着它需要适应它们之间的任何尺寸范围。
例如,要在放置时设置小部件的默认尺寸,您可以设置以下属性
<appwidget-provider
android:targetCellWidth="3"
android:targetCellHeight="2"
android:minWidth="180dp"
android:minHeight="110dp">
</appwidget-provider>
这意味着小部件的默认尺寸为 3x2 单元格,如 targetCellWidth
和 targetCellHeight
属性所指定,或者对于运行 Android 11 或更低版本的设备,尺寸为 180×110dp,如 minWidth
和 minHeight
所指定。在后一种情况下,以单元格为单位的尺寸可能会因设备而异。
此外,要设置您的小部件支持的尺寸范围,您可以设置以下属性
<appwidget-provider
android:minResizeWidth="180dp"
android:minResizeHeight="110dp"
android:maxResizeWidth="530dp"
android:maxResizeHeight="450dp">
</appwidget-provider>
如上面的属性所指定,小部件的宽度可从 180dp 调整到 530dp,高度可从 110dp 调整到 450dp。只要满足以下条件,小部件即可从 3x2 单元格调整到 5x2 单元格
- 设备具有 5x4 网格。
- 单元格数量与以 dp 为单位的可用尺寸之间的映射关系遵循本页面中显示最小尺寸估算的表格。
- 小部件适应该尺寸范围。
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
)。
当用户调整小部件大小时,其外观会发生变化以适应每个单元格尺寸,如下图所示。

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
。