Widget:是什么,iOS WidgetKit 和 Android 上的小部件

作者: IT Sectr 发布日期: 2026-02-15 阅读时间: 10 分钟

Widget — 是 iOS 或 Android 主屏幕上的紧凑型用户界面元素,无需打开应用程序即可显示最新信息。小部件于 iOS 14 (2020) 随 WidgetKit 框架在 SwiftUI 中推出,并在 Android 1.5 版本起通过 AppWidgetProvider 以 Kotlin 或 Java 存在。它们让用户可以直接在主屏幕上查看天气、汇率、日历、笔记或任务状态,节省时间并简化交互。根据 Apple 的数据,用户平均每天与小部件交互 4–6 次,在主屏幕上安装小部件可将应用程序参与度提高 20–30%。对于 Android,这些数字是可比的。让我们通过代码示例来了解小部件在两个平台上的工作方式。

要点

  • Widget — 主屏幕上的迷你应用程序,无需启动主应用即可快速查看信息
  • iOS WidgetKit (iOS 14+) 基于 SwiftUI 构建,具有三种尺寸和通过 TimelineProvider 更新
  • Android AppWidgetProvider 自 API level 3 起存在,使用 RemoteViews — 在单独进程中受限的 View 集
  • 数据更新 在 iOS 上 — 通过带有日期指定的 timeline,在 Android 上 — 通过 updateIntervalMillis 或 WorkManager
  • Smart Stack 在 iOS 上和小部件堆栈在 Android 上允许将多个小部件组合到一个插槽中

什么是 Widget

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 上允许用户在主屏幕上的一个插槽中组合多个小部件。设备根据时间、位置或用户活动自动切换显示的小部件。例如,早上显示日历,白天显示天气,晚上显示笔记。对于开发者来说,这意味着小部件必须在堆栈内正确工作,不能依赖持续显示。

iOS WidgetKit:SwiftUI 和 TimelineProvider

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)内容
Small170 × 1701–2 个指标:温度、汇率
Medium360 × 1702–4 个指标:5 天预报、列表
Large360 × 3805–10 个指标:日历、新闻源

Android AppWidgetProvider:RemoteViews

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 自适应颜色和动态尺寸更改的支持。

在 iOS 上使用 WidgetKit 创建小部件

让我们使用 WidgetKit 和 SwiftUI 用 Swift 创建一个天气小部件。项目应包含 Widget Extension target,在 Xcode 中通过 File → New → Target → Widget Extension 添加。

swift
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。

在 Android 上使用 AppWidgetProvider 创建小部件

我们用 Kotlin 使用 AppWidgetProvider 和 RemoteViews 实现类似的天气小部件。

kotlin
// 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 和 Android 上的 Widget 有什么区别?

iOS WidgetKit 提供三种固定尺寸(small、medium、large)和通过 TimelineProvider 的更新,带有系统预算。Android AppWidgetProvider 使用灵活的单元格尺寸、RemoteViews 进行渲染,并通过 updatePeriodMillis 或 WorkManager 进行更新。

Widget 多久更新一次?

在 iOS 上,更新由系统控制 — 最小间隔 15–30 分钟,对于关键数据使用 push-to-refresh。在 Android 上,间隔在 updatePeriodMillis(至少 30 分钟)中设置,或通过 WorkManager 用于后台任务。

Widget 支持哪些尺寸?

iOS 支持 small(170x170pt)、medium(360x170pt)和 large(360x380pt)。Android 使用灵活的单元格网格:从 2x1 到 5x5,尺寸取决于启动器和操作系统版本。

可以为 Widget 添加动画吗?

在 iOS 上,小部件内部没有动画 — 它们是静态元素。Android RemoteViews 通过 setFloat() 和 ViewPropertyAnimator 支持基本动画,但复杂动画不可用。

Widget 会降低设备性能吗?

如果正确实现 — 不会。系统严格限制小部件的资源:在 iOS 上 更新和内存预算(30–50 MB),在 Android 上 — 最小更新间隔。有问题的部件将被系统强制终止。

总结

  • Widget — 主屏幕上的紧凑元素,用于快速访问应用程序数据而无需打开它
  • iOS WidgetKit 支持三种固定尺寸和通过 TimelineProvider 的更新,使用 SwiftUI
  • Android AppWidgetProvider 通过 BroadcastReceiver 和 RemoteViews 工作,具有灵活的单元格尺寸
  • 数据更新 在两个平台上都受到系统限制:iOS — 更新预算,Android — 至少 30 分钟
  • 交互性 出现在 iOS 17(App Intents)和 Android 12(动态颜色、按钮)中
  • Smart Stack 在 iOS 上和堆栈在 Android 上允许组合多个小部件并自动切换
  • 性能至关重要 — 系统在超出限制时会强制终止有问题的部件

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读