Widget — 是 iOS 或 Android 主屏幕上的紧凑型用户界面元素,无需打开应用程序即可显示最新信息。小部件于 iOS 14 (2020) 随 WidgetKit 框架在 SwiftUI 中推出,并在 Android 1.5 版本起通过 AppWidgetProvider 以 Kotlin 或 Java 存在。它们让用户可以直接在主屏幕上查看天气、汇率、日历、笔记或任务状态,节省时间并简化交互。根据 Apple 的数据,用户平均每天与小部件交互 4–6 次,在主屏幕上安装小部件可将应用程序参与度提高 20–30%。对于 Android,这些数字是可比的。让我们通过代码示例来了解小部件在两个平台上的工作方式。
要点
Widget — 是放置在移动设备主屏幕上的紧凑型用户界面元素,用于显示来自应用程序的最新信息。与完整应用程序不同,小部件无需启动 — 用户解锁屏幕后立即可看到数据。这一概念源自桌面操作系统(macOS Dashboard、Windows Sidebar),在移动环境中,Android 从第一个版本(Android 1.5 Cupcake,2009)就引入了对小部件的支持,而 Apple 直到 iOS 14 才添加。
技术实现因平台而异。iOS 小部件 — Xcode 中的独立 target,编译为 Widget Extension,在自身进程中运行。Android 小部件 — 扩展 AppWidgetProvider 的 BroadcastReceiver,通过 RemoteViews 渲染布局。两个平台都对性能和内存施加严格限制:系统可以在超出限制时删除小部件。iOS 将小部件限制在 30–50 MB,timeline 形成时间限制在 10 秒内。Android — 通过 updatePeriodMillis 更新间隔不超过 30 分钟,并在 onUpdate 卡死时强制终止。
Smart Stack 在 iOS 上和堆栈在 Android 上允许用户在主屏幕上的一个插槽中组合多个小部件。设备根据时间、位置或用户活动自动切换显示的小部件。例如,早上显示日历,白天显示天气,晚上显示笔记。对于开发者来说,这意味着小部件必须在堆栈内正确工作,不能依赖持续显示。
WidgetKit — Apple 用于在 iOS 14+、iPadOS 14+ 和 macOS 11+ 上创建小部件的框架。小部件使用 Swift 编写,利用 SwiftUI 和 TimelineProvider 机制来更新内容。WidgetKit 架构包括三个组件:TimelineProvider — 数据源,形成带有记录的时间线;Widget Entry View — 基于记录显示内容的 SwiftUI View;Widget Configuration — 描述小部件的类型、尺寸和系列。
WidgetKit 自行管理更新 — 开发者不能强制小部件比系统允许的更频繁更新。Apple 使用在设备上所有小部件之间分配的更新预算。最小间隔 — 15–30 分钟。对于关键数据(例如配送状态),使用 push-to-refresh — 发送带有未来相关日期的推送通知,之后 WidgetKit 请求新的时间线。从 iOS 17 开始,小部件通过 App Intents 支持交互性 — 用户可以在不打开应用程序的情况下按小部件内的按钮。
| 尺寸 | iOS (pt) | 内容 |
|---|---|---|
| Small | 170 × 170 | 1–2 个指标:温度、汇率 |
| Medium | 360 × 170 | 2–4 个指标:5 天预报、列表 |
| Large | 360 × 380 | 5–10 个指标:日历、新闻源 |
AppWidgetProvider — 在 Android 中创建小部件的基础类,自 API level 3 起成为 Android SDK 的一部分。Android 小部件 — 是一个 BroadcastReceiver,从系统接收更新、激活和停用事件。与 iOS 不同(小部件由 SwiftUI 在自己的引擎上绘制),Android 使用 RemoteViews — 在系统进程(启动器)中渲染的有限 View 组件集。不能使用自定义 View、复杂动画或 UI 库。
Android 小部件的工作方案包括四个阶段。配置 — 将小部件添加到屏幕时,系统可以启动配置 Activity,用户选择设置。更新 — 系统在指定间隔(至少 30 分钟)或通过 WorkManager 从应用程序发送信号时调用 onUpdate()。渲染 — 提供者创建带有当前数据的 RemoteViews 并通过 AppWidgetManager 传递。点击处理 — 小部件支持用于按钮的 PendingIntent:点击时打开 Activity 或执行操作。从 Android 12 开始,小部件获得了对圆角、Material You 自适应颜色和动态尺寸更改的支持。
让我们使用 WidgetKit 和 SwiftUI 用 Swift 创建一个天气小部件。项目应包含 Widget Extension target,在 Xcode 中通过 File → New → Target → Widget Extension 添加。
import WidgetKit
import SwiftUI
// 1. 小部件的数据模型
struct WeatherEntry: TimelineEntry {
let date: Date
let temperature: Int
let condition: String
}
// 2. TimelineProvider — 数据提供者
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> WeatherEntry {
WeatherEntry(date: Date(), temperature: 22, condition: "晴天")
}
func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> Void) {
let entry = WeatherEntry(date: Date(), temperature: 22, condition: "晴天")
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
let now = Date()
let entry = WeatherEntry(date: now, temperature: 20, condition: "多云")
let nextUpdate = Calendar.current.date(byAdding: .hour, value: 3, to: now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
// 3. 用于显示的 SwiftUI View
struct WeatherWidgetEntryView: View {
var entry: WeatherEntry
var body: some View {
VStack(alignment: .leading) {
Text("天气").font(.caption).foregroundColor(.secondary)
HStack {
Text("\(entry.temperature)°").font(.largeTitle).fontWeight(.bold)
Spacer()
Text(entry.condition).font(.body)
}
}.padding()
}
}
// 4. 小部件配置
@main
struct WeatherWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "WeatherWidget", provider: Provider()) { entry in
WeatherWidgetEntryView(entry: entry)
}
.configurationDisplayName("天气")
.description("显示当前温度")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
关键点:TimelineProvider 确定何时显示以及显示什么数据;getTimeline 创建带有 .after(nextUpdate) 更新策略的时间线;StaticConfiguration 将提供者与 View 连接。对于动态数据,使用带有用户参数的 IntentConfiguration。
我们用 Kotlin 使用 AppWidgetProvider 和 RemoteViews 实现类似的天气小部件。
// WeatherWidgetProvider.kt
class WeatherWidgetProvider : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
override fun onAppWidgetOptionsChanged(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int,
newOptions: Bundle
) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
companion object {
fun updateAppWidget(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val temperature = 20
val condition = "多云"
val views = RemoteViews(
context.packageName, R.layout.widget_weather
).apply {
setTextViewText(R.id.tv_temperature, "$temperature°")
setTextViewText(R.id.tv_condition, condition)
setOnClickPendingIntent(
R.id.widget_root, PendingIntent.getActivity(
context, 0,
Intent(context, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
// AndroidManifest — 提供者注册
<receiver android:name=".WeatherWidgetProvider" android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_weather_info" />
</receiver>
文件 widget_weather_info.xml 设置最小尺寸、间隔和布局:minWidth 160dp,minHeight 80dp,updatePeriodMillis 3600000(1 小时)。RemoteViews 仅使用支持的组件:LinearLayout、TextView、ImageView。自定义 View 和 ConstraintLayout 自 Android 5.0 起可用。GlanceLayout — RemoteViews 的实验性 Compose 方法 — 出现在 Android 15 中。
常见问题
iOS WidgetKit 提供三种固定尺寸(small、medium、large)和通过 TimelineProvider 的更新,带有系统预算。Android AppWidgetProvider 使用灵活的单元格尺寸、RemoteViews 进行渲染,并通过 updatePeriodMillis 或 WorkManager 进行更新。
在 iOS 上,更新由系统控制 — 最小间隔 15–30 分钟,对于关键数据使用 push-to-refresh。在 Android 上,间隔在 updatePeriodMillis(至少 30 分钟)中设置,或通过 WorkManager 用于后台任务。
iOS 支持 small(170x170pt)、medium(360x170pt)和 large(360x380pt)。Android 使用灵活的单元格网格:从 2x1 到 5x5,尺寸取决于启动器和操作系统版本。
在 iOS 上,小部件内部没有动画 — 它们是静态元素。Android RemoteViews 通过 setFloat() 和 ViewPropertyAnimator 支持基本动画,但复杂动画不可用。
如果正确实现 — 不会。系统严格限制小部件的资源:在 iOS 上 更新和内存预算(30–50 MB),在 Android 上 — 最小更新间隔。有问题的部件将被系统强制终止。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。