Как создать простой виджет

Попробуйте Compose
Jetpack Compose – это рекомендуемый набор инструментов для создания пользовательского интерфейса в Android. Узнайте, как создавать виджеты с помощью API в стиле Compose.

Виджеты приложений – это миниатюрные представления для приложений, которые можно встраивать в другие приложения, например в главный экран, и периодически обновлять. В пользовательском интерфейсе эти представления называются виджетами. Вы можете опубликовать виджет с помощью поставщика виджетов приложений (или поставщика виджетов). Компонент приложения, который содержит другие виджеты, называется хостом виджетов приложения (или хостом виджетов). На рисунке 1 показан пример музыкального виджета.

Пример музыкального виджета
Рисунок 1. Пример виджета с музыкой.

В этом документе описывается, как опубликовать виджет с помощью поставщика виджетов. Подробнее о том, как создать собственный класс AppWidgetHost для размещения виджетов приложений, рассказывается в статье Как создать хост виджетов.

Подробнее о том, как разработать виджет…

Компоненты виджета

Чтобы создать виджет, вам понадобятся следующие основные компоненты:

Объект AppWidgetProviderInfo
Описывает метаданные виджета, такие как его макет, частота обновлений и класс AppWidgetProvider. AppWidgetProviderInfo определяется в XML, как описано в этом документе.
Класс AppWidgetProvider
Определяет основные методы, которые позволяют программно взаимодействовать с виджетом. Через него вы получаете трансляции, когда виджет обновляется, включается, отключается или удаляется. Вы объявляете AppWidgetProvider в манифесте, а затем реализуете его, как описано в этом документе.
Посмотреть шаблон экрана
Определяет начальный макет виджета. Макет определяется в XML-файле, как описано в этом документе.

На рисунке 2 показано, как эти компоненты вписываются в общий процесс обработки виджетов приложений.

Процесс обработки виджета приложения
Рисунок 2. Схема обработки виджета приложения.

Если виджету требуется пользовательская настройка, реализуйте действие по настройке виджета приложения. Это действие позволяет пользователям изменять настройки виджета, например часовой пояс для виджета часов.

Мы также рекомендуем следующие улучшения: гибкие макеты виджетов, различные улучшения, расширенные виджеты, виджеты коллекций и создание хоста виджетов.

Объявление XML-файла AppWidgetProviderInfo

Определение настроек метаданных (таких как размеры ячеек по умолчанию, ограничения на изменение размера и частота обновления) абсолютно одинаково как для традиционных представлений, так и для виджетов на основе Glance.

Чтобы узнать, как определить и настроить XML-файл метаданных, ознакомьтесь с разделом Объявление XML-раздела AppWidgetProviderInfo в документации по Glance.

Используйте класс AppWidgetProvider для обработки широковещательных рассылок виджетов.

Механизмы широковещательного приемника, фильтры объявления манифеста и циклы событий жизненного цикла объединены в платформе. При разработке с приоритетом Compose эти трансляции координируются с помощью оболочки GlanceAppWidgetReceiver.

Чтобы узнать, как зарегистрировать приемник в манифесте и реализовать переопределения жизненного цикла, совместимые с Hilt, ознакомьтесь с разделом Использование класса AppWidgetProvider для обработки широковещательных сообщений в документации по Glance.

Создайте макет виджета

Вы должны определить начальный макет виджета в XML и сохранить его в каталоге res/layout/ проекта. Подробную информацию можно найти в рекомендациях по дизайну.

Если вы знакомы с макетами, то создать макет виджета будет несложно. Однако макеты виджетов основаны на RemoteViews, который поддерживает не все виды макетов и виджетов. Вы не можете использовать специальные представления или подклассы представлений, поддерживаемых RemoteViews.

RemoteViews также поддерживает ViewStub – невидимый View нулевого размера, который можно использовать для отложенного заполнения ресурсов макета во время выполнения.

Поддержка поведения с сохранением состояния

В Android 12 добавлена поддержка сохранения состояния с помощью следующих существующих компонентов:

Виджет по-прежнему не имеет состояния. Приложение должно сохранять состояние и регистрироваться для событий изменения состояния.

Пример виджета списка покупок, показывающий поведение с сохранением состояния
Рисунок 3. Пример поведения с сохранением состояния.

В примере кода ниже показано, как реализовать эти компоненты.

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
    R.id.my_checkbox,
    RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)

Предоставьте два макета: один для устройств с Android 12 или более поздней версией в папке res/layout-v31, а другой – для устройств с Android 11 или более ранней версией в папке res/layout по умолчанию.

Как реализовать скругленные углы

Расчет внешнего фона и внутренних пропорциональных радиусов является стандартным и общим. При разработке на основе Compose это можно задать динамически в Kotlin вместе с ресурсами пользовательской темы.

Чтобы настроить радиус скругления углов или динамические стили для более старых устройств Android, ознакомьтесь с разделом Реализация скругленных углов в документации по Glance.

Следите за тем, чтобы изображения RemoteViews не превышали лимиты системной памяти

Виджеты запускаются в процессе главного экрана, и system_server устанавливает ограничение на объем памяти, используемой битовыми картами каждого виджета (6 * width * height байт; см. Изменение в Android 17, связанное с ограничением памяти для виджетов). При создании классических виджетов AppWidgetProvider с помощью RemoteViews выполните следующие действия:

  • Уменьшите разрешение растровых изображений с помощью WindowMetrics. На уровне API 30 и выше рассчитывайте максимальный бюджет растровых изображений на основе WindowManager.currentWindowMetrics.bounds, а не resources.displayMetrics, и декодируйте непрозрачные изображения с помощью Bitmap.Config.RGB_565 и inSampleSize, кратного двум.
  • При замене изображений используйте полные вызовы updateAppWidget. Не вызывайте partiallyUpdateAppWidget для обновления растровых изображений ImageView или URI Icon, поскольку частичные обновления добавляют действия в иерархию кешированных RemoteViews в system_server, не освобождая предыдущие выделения изображений.
  • Добавьте фоновую задачу в очередь с помощью WorkManager. Не вызывайте Context.startForegroundService из AppWidgetProvider.onReceive или onUpdate, так как при запуске в фоновом режиме это приводит к ошибке ForegroundServiceStartNotAllowedException. Вместо этого поставьте в очередь WorkManager CoroutineWorker и передайте context.applicationContext при заполнении или обновлении RemoteViews из фоновых рабочих процессов.