Виджеты приложений — это миниатюрные представления приложений, которые можно встраивать в другие приложения, например на главный экран, и получать периодические обновления. Эти представления называются виджетами в пользовательском интерфейсе, и вы можете опубликовать их с помощью поставщика виджетов приложения (или поставщика виджетов ). Компонент приложения, содержащий другие виджеты, называется хостом виджета приложения (или хостом виджета ). На рис. 1 показан пример музыкального виджета:
![Пример музыкального виджета](https://developer.android.com/static/images/appwidgets/music.png?authuser=7&hl=ru)
В этом документе описывается, как опубликовать виджет с помощью поставщика виджетов. Подробные сведения о создании собственного AppWidgetHost
для размещения виджетов приложений см. в разделе Создание хоста виджетов .
Информацию о том, как создать виджет, см. в разделе Обзор виджетов приложений .
Компоненты виджета
Для создания виджета вам потребуются следующие основные компоненты:
- Объект
AppWidgetProviderInfo
- Описывает метаданные виджета, такие как макет виджета, частота обновления и класс
AppWidgetProvider
.AppWidgetProviderInfo
определяется в XML , как описано в этом документе. - Класс
AppWidgetProvider
- Определяет основные методы, которые позволяют программно взаимодействовать с виджетом. Через него вы получаете трансляции, когда виджет обновляется, включается, отключается или удаляется. Вы объявляете
AppWidgetProvider
в манифесте , а затем реализуете его, как описано в этом документе. - Посмотреть макет
- Определяет исходный макет виджета. Макет определяется в XML , как описано в этом документе.
На рис. 2 показано, как эти компоненты вписываются в общий процесс обработки виджетов приложения.
![Процесс обработки виджета приложения](https://developer.android.com/static/images/appwidgets/flow-diagram.png?authuser=7&hl=ru)
Если ваш виджет требует пользовательской настройки, реализуйте действие по настройке виджета приложения. Это действие позволяет пользователям изменять настройки виджета, например часовой пояс для виджета часов.
- Начиная с Android 12 (уровень API 31), вы можете предоставить конфигурацию по умолчанию и позволить пользователям перенастроить виджет позже. Дополнительные сведения см. в разделах «Использовать конфигурацию виджета по умолчанию» и «Разрешить пользователям перенастраивать размещенные виджеты» .
- В Android 11 (уровень API 30) или ниже это действие запускается каждый раз, когда пользователь добавляет виджет на свой главный экран.
Мы также рекомендуем следующие улучшения: гибкие макеты виджетов , различные улучшения , расширенные виджеты , виджеты-коллекции и создание хоста виджетов .
Объявите XML AppWidgetProviderInfo.
Объект AppWidgetProviderInfo
определяет основные качества виджета. Определите объект AppWidgetProviderInfo
в файле ресурсов XML, используя один элемент <appwidget-provider>
, и сохраните его в папке res/xml/
проекта.
Это показано в следующем примере:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:maxResizeWidth="250dp"
android:maxResizeHeight="120dp"
android:updatePeriodMillis="86400000"
android:description="@string/example_appwidget_description"
android:previewLayout="@layout/example_appwidget_preview"
android:initialLayout="@layout/example_loading_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>
Атрибуты размера виджета
Главный экран по умолчанию размещает виджеты в своем окне на основе сетки ячеек, имеющих определенную высоту и ширину. Большинство домашних экранов позволяют виджетам принимать размеры только целочисленные кратные размерам ячеек сетки — например, две ячейки по горизонтали и три ячейки по вертикали.
Атрибуты размера виджета позволяют указать размер виджета по умолчанию и указать нижнюю и верхнюю границы размера виджета. В этом контексте размер виджета по умолчанию — это размер, который виджет принимает при первом добавлении на главный экран.
В следующей таблице описаны атрибуты <appwidget-provider>
, относящиеся к размеру виджета:
Атрибуты и описание | |
---|---|
targetCellWidth и targetCellHeight (Android 12), minWidth и minHeight |
targetCellWidth и targetCellHeight , а также minWidth и minHeight — чтобы ваше приложение могло вернуться к использованию minWidth и minHeight , если устройство пользователя не поддерживает targetCellWidth и targetCellHeight . Если они поддерживаются, атрибуты targetCellWidth и targetCellHeight имеют приоритет над атрибутами minWidth и minHeight . |
minResizeWidth и minResizeHeight | Укажите абсолютный минимальный размер виджета. Эти значения определяют размер, при котором виджет становится неразборчивым или непригоден для использования по другим причинам. Использование этих атрибутов позволяет пользователю изменить размер виджета до размера, меньшего, чем размер виджета по умолчанию. Атрибут minResizeWidth игнорируется, если он больше minWidth или если горизонтальное изменение размера не включено. См. resizeMode . Аналогично, атрибут minResizeHeight игнорируется, если он больше minHeight или если вертикальное изменение размера не включено. |
maxResizeWidth и maxResizeHeight | Укажите рекомендуемый максимальный размер виджета. Если значения не кратны размерам ячейки сетки, они округляются до ближайшего размера ячейки. Атрибут maxResizeWidth игнорируется, если он меньше minWidth или если горизонтальное изменение размера не включено. См. resizeMode . Аналогично, атрибут maxResizeHeight игнорируется, если он больше minHeight или если вертикальное изменение размера не включено. Представлено в Android 12. |
resizeMode | Определяет правила, по которым можно изменить размер виджета. Вы можете использовать этот атрибут, чтобы изменить размер виджетов главного экрана по горизонтали, вертикали или по обеим осям. Пользователи касаются и удерживают виджет, чтобы отобразить его маркеры изменения размера, затем перетаскивают горизонтальные или вертикальные маркеры, чтобы изменить его размер в сетке макета. Значения атрибута resizeMode включают horizontal , vertical и none . Чтобы объявить виджет как изменяемый по горизонтали и вертикали, используйте horizontal|vertical . |
Пример
Чтобы проиллюстрировать, как атрибуты в предыдущей таблице влияют на размер виджета, предположим следующие характеристики:
- Ячейка сетки имеет ширину 30 dp и высоту 50 dp.
- Предоставляется следующая спецификация атрибута:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp"
android:minHeight="80dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:minResizeWidth="40dp"
android:minResizeHeight="40dp"
android:maxResizeWidth="120dp"
android:maxResizeHeight="120dp"
android:resizeMode="horizontal|vertical" />
Начиная с Android 12:
Используйте атрибуты targetCellWidth
и targetCellHeight
в качестве размера виджета по умолчанию.
По умолчанию размер виджета составляет 2x2. Размер виджета можно изменить до 2x1 или до 4x3.
Android 11 и более ранние версии:
Используйте атрибуты minWidth
и minHeight
для вычисления размера виджета по умолчанию.
Ширина по умолчанию = Math.ceil(80 / 30)
= 3.
Высота по умолчанию = Math.ceil(80 / 50)
= 2.
По умолчанию размер виджета составляет 3x2. Размер виджета можно изменить до 2х1 или до полноэкранного.
Дополнительные атрибуты виджета
В следующей таблице описаны атрибуты <appwidget-provider>
, относящиеся к качествам, отличным от размера виджета.
Атрибуты и описание | |
---|---|
updatePeriodMillis | Определяет, как часто платформа виджетов запрашивает обновление у AppWidgetProvider вызывая метод обратного вызова onUpdate() . При этом значении фактическое обновление не гарантируется точно в срок, и мы рекомендуем обновлять как можно реже — не чаще одного раза в час — для экономии заряда батареи. Полный список рекомендаций по выбору подходящего периода обновления см. в разделе «Оптимизация обновления содержимого виджета» . |
initialLayout | Указывает на ресурс макета, определяющий макет виджета. |
configure | Определяет действие, которое запускается, когда пользователь добавляет виджет, позволяя ему настраивать свойства виджета. См. раздел Разрешение пользователям настраивать виджеты . Начиная с Android 12, ваше приложение может пропускать начальную настройку. Подробности см. в разделе Использование конфигурации виджета по умолчанию . |
description | Указывает описание средства выбора виджета, которое будет отображаться для вашего виджета. Представлено в Android 12. |
previewLayout (Android 12) и previewImage (Android 11 и более ранние версии) |
previewImage previewLayout , чтобы ваше приложение могло вернуться к previewImage если устройство пользователя не previewLayout . Дополнительные сведения см. в разделе Обратная совместимость с масштабируемыми предпросмотрами виджетов . |
autoAdvanceViewId | Указывает идентификатор представления подпредставления виджета, которое автоматически расширяется хостом виджета. |
widgetCategory | Объявляет, может ли ваш виджет отображаться на главном экране ( home_screen ), экране блокировки ( keyguard ) или на обоих. Для Android 5.0 и выше допустим только home_screen . |
widgetFeatures | Объявляет функции, поддерживаемые виджетом. Например, если вы хотите, чтобы ваш виджет использовал конфигурацию по умолчанию, когда пользователь добавляет его, укажите флаги configuration_optional и reconfigurable . Это обходит запуск действия по настройке после того, как пользователь добавляет виджет. Пользователь по-прежнему может впоследствии перенастроить виджет . |
Используйте класс AppWidgetProvider для обработки трансляций виджетов.
Класс AppWidgetProvider
обрабатывает широковещательные рассылки виджетов и обновляет виджет в ответ на события жизненного цикла виджета. В следующих разделах описано, как объявить AppWidgetProvider
в манифесте и затем реализовать его.
Объявить виджет в манифесте
Сначала объявите класс AppWidgetProvider
в файле AndroidManifest.xml
вашего приложения, как показано в следующем примере:
<receiver android:name="ExampleAppWidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
</receiver>
Элементу <receiver>
требуется атрибут android:name
, который указывает AppWidgetProvider
используемый виджетом. Компонент нельзя экспортировать, если только отдельный процесс не требует трансляции в ваш AppWidgetProvider
, что обычно не происходит.
Элемент <intent-filter>
должен включать элемент <action>
с атрибутом android:name
. Этот атрибут указывает, что AppWidgetProvider
принимает широковещательную рассылку ACTION_APPWIDGET_UPDATE
. Это единственная трансляция, которую вы должны явно заявить. AppWidgetManager
автоматически отправляет все остальные трансляции виджетов в AppWidgetProvider
по мере необходимости.
Элемент <meta-data>
определяет ресурс AppWidgetProviderInfo
и требует следующих атрибутов:
-
android:name
: указывает имя метаданных. Используйтеandroid.appwidget.provider
чтобы идентифицировать данные как дескрипторAppWidgetProviderInfo
. -
android:resource
: указывает расположение ресурсаAppWidgetProviderInfo
.
Реализуйте класс AppWidgetProvider.
Класс AppWidgetProvider
расширяет BroadcastReceiver
как удобный класс для обработки широковещательных сообщений виджетов. Он получает только широковещательные сообщения о событиях, которые имеют отношение к виджету, например, когда виджет обновляется, удаляется, включается и отключается. Когда происходят эти широковещательные события, вызываются следующие методы AppWidgetProvider
:
-
onUpdate()
- Это вызывается для обновления виджета с интервалами, определяемыми атрибутом
updatePeriodMillis
вAppWidgetProviderInfo
. Дополнительную информацию см. в таблице, описывающей дополнительные атрибуты виджета на этой странице. - Этот метод также вызывается, когда пользователь добавляет виджет, поэтому он выполняет необходимые настройки, такие как определение обработчиков событий для объектов
View
или запуск заданий по загрузке данных для отображения в виджете. Однако если вы объявляете действие настройки без флагаconfiguration_optional
, этот метод не вызывается, когда пользователь добавляет виджет, но он вызывается для последующих обновлений. Ответственность за выполнение первого обновления после завершения настройки лежит на деятельности по настройке. Дополнительные сведения см. в разделе Разрешить пользователям настраивать виджеты приложений . - Самый важный обратный вызов —
onUpdate()
. Дополнительную информацию см. в разделе «Обработка событий с помощью классаonUpdate()
на этой странице. -
onAppWidgetOptionsChanged()
Это вызывается при первом размещении виджета и при каждом изменении размера виджета. Используйте этот обратный вызов, чтобы показать или скрыть контент в зависимости от диапазонов размеров виджета. Получите диапазоны размеров (а, начиная с Android 12, список возможных размеров, которые может принимать экземпляр виджета), вызвав
getAppWidgetOptions()
, который возвращаетBundle
, который включает в себя следующее:-
OPTION_APPWIDGET_MIN_WIDTH
: содержит нижнюю границу ширины экземпляра виджета в единицах dp. -
OPTION_APPWIDGET_MIN_HEIGHT
: содержит нижнюю границу высоты экземпляра виджета в единицах dp. -
OPTION_APPWIDGET_MAX_WIDTH
: содержит верхнюю границу ширины экземпляра виджета в единицах dp. -
OPTION_APPWIDGET_MAX_HEIGHT
: содержит верхнюю границу высоты экземпляра виджета в единицах dp. -
OPTION_APPWIDGET_SIZES
: содержит список возможных размеров (List<SizeF>
) в единицах dp, которые может принимать экземпляр виджета. Представлено в Android 12.
-
-
onDeleted(Context, int[])
Это вызывается каждый раз, когда виджет удаляется с хоста виджетов.
-
onEnabled(Context)
Это вызывается, когда экземпляр виджета создается впервые. Например, если пользователь добавляет два экземпляра вашего виджета, он вызывается только в первый раз. Если вам нужно открыть новую базу данных или выполнить другую настройку, которую нужно выполнить только один раз для всех экземпляров виджета, то это хорошее место для этого.
-
onDisabled(Context)
Это вызывается, когда последний экземпляр вашего виджета удаляется с хоста виджетов. Здесь вы очищаете любую работу, выполненную в
onEnabled(Context)
, например удаление временной базы данных.-
onReceive(Context, Intent)
Это вызывается для каждой трансляции и перед каждым из предыдущих методов обратного вызова. Обычно вам не нужно реализовывать этот метод, поскольку реализация
AppWidgetProvider
по умолчанию фильтрует все широковещательные сообщения виджетов и вызывает соответствующие предыдущие методы.
Вы должны объявить реализацию класса AppWidgetProvider
как получатель широковещательной передачи, используя элемент <receiver>
в AndroidManifest
. Дополнительные сведения см. в разделе «Объявление виджета в манифесте» на этой странице.
Обработка событий с помощью класса onUpdate().
Самый важный обратный вызов AppWidgetProvider
— это onUpdate()
, поскольку он вызывается при добавлении каждого виджета на хост, если только вы не используете действие настройки без флага configuration_optional
. Если ваш виджет принимает какие-либо события взаимодействия с пользователем, зарегистрируйте обработчики событий в этом обратном вызове. Если ваш виджет не создает временные файлы или базы данных и не выполняет другую работу, требующую очистки, то onUpdate()
может быть единственным методом обратного вызова, который вам нужно определить.
Например, если вам нужен виджет с кнопкой, которая запускает действие при нажатии, вы можете использовать следующую реализацию AppWidgetProvider
:
Котлин
class ExampleAppWidgetProvider : AppWidgetProvider() { override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // Perform this loop procedure for each widget that belongs to this // provider. appWidgetIds.forEach { appWidgetId -> // Create an Intent to launch ExampleActivity. val pendingIntent: PendingIntent = PendingIntent.getActivity( /* context = */ context, /* requestCode = */ 0, /* intent = */ Intent(context, ExampleActivity::class.java), /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // Get the layout for the widget and attach an onClick listener to // the button. val views: RemoteViews = RemoteViews( context.packageName, R.layout.appwidget_provider_layout ).apply { setOnClickPendingIntent(R.id.button, pendingIntent) } // Tell the AppWidgetManager to perform an update on the current // widget. appWidgetManager.updateAppWidget(appWidgetId, views) } } }
Ява
public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // Perform this loop procedure for each widget that belongs to this // provider. for (int i=0; i < appWidgetIds.length; i++) { int appWidgetId = appWidgetIds[i]; // Create an Intent to launch ExampleActivity Intent intent = new Intent(context, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity( /* context = */ context, /* requestCode = */ 0, /* intent = */ intent, /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // Get the layout for the widget and attach an onClick listener to // the button. RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout); views.setOnClickPendingIntent(R.id.button, pendingIntent); // Tell the AppWidgetManager to perform an update on the current app // widget. appWidgetManager.updateAppWidget(appWidgetId, views); } } }
Этот AppWidgetProvider
определяет только метод onUpdate()
, используя его для создания PendingIntent
, который запускает Activity
и прикрепляет его к кнопке виджета с помощью setOnClickPendingIntent(int, PendingIntent)
. Он включает в себя цикл, который перебирает каждую запись в appWidgetIds
, который представляет собой массив идентификаторов, идентифицирующих каждый виджет, созданный этим поставщиком. Если пользователь создает более одного экземпляра виджета, все они обновляются одновременно. Однако для всех экземпляров виджета управляется только одно расписание updatePeriodMillis
. Например, если расписание обновления определено каждые два часа, а второй экземпляр виджета добавляется через час после первого, то они оба обновляются в период, определенный первым и вторым периодом обновления. игнорируется. Они оба обновляются каждые два часа, а не каждый час.
Дополнительные сведения см. в примере класса ExampleAppWidgetProvider.java
.
Получение намерений трансляции виджета
AppWidgetProvider
— это удобный класс. Если вы хотите получать трансляции виджета напрямую, вы можете реализовать свой собственный BroadcastReceiver
или переопределить обратный вызов onReceive(Context,Intent)
. Намерения, о которых вам нужно заботиться, следующие:
-
ACTION_APPWIDGET_UPDATE
-
ACTION_APPWIDGET_DELETED
-
ACTION_APPWIDGET_ENABLED
-
ACTION_APPWIDGET_DISABLED
-
ACTION_APPWIDGET_OPTIONS_CHANGED
Создайте макет виджета
Вы должны определить первоначальный макет вашего виджета в XML и сохранить его в каталоге res/layout/
проекта. Подробности см. в Руководстве по проектированию .
Создать макет виджета несложно, если вы знакомы с макетами . Однако имейте в виду, что макеты виджетов основаны на RemoteViews
, который поддерживает не все виды виджетов макетов и представлений. Вы не можете использовать собственные представления или подклассы представлений, поддерживаемых RemoteViews
.
RemoteViews
также поддерживает ViewStub
— невидимое View
нулевого размера, которое можно использовать для ленивого раздувания ресурсов макета во время выполнения.
Поддержка поведения с сохранением состояния
В Android 12 добавлена поддержка поведения с отслеживанием состояния с использованием следующих существующих компонентов:
Виджет по-прежнему не имеет состояния. Ваше приложение должно хранить состояние и регистрироваться для событий изменения состояния.
![Пример виджета списка покупок, демонстрирующего поведение с сохранением состояния](https://developer.android.com/static/images/appwidgets/home.png?authuser=7&hl=ru)
В следующем примере кода показано, как реализовать эти компоненты.
Котлин
// 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) )
Ява
// 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
по умолчанию.
Реализуйте закругленные углы
В Android 12 представлены следующие системные параметры для установки радиусов закругленных углов вашего виджета:
system_app_widget_background_radius
: угловой радиус фона виджета, который никогда не превышает 28 dp.system_app_widget_inner_radius
: угловой радиус любого вида внутри виджета. Это ровно на 8 dp меньше радиуса фона, чтобы обеспечить хорошее выравнивание при использовании заполнения 8 dp.
В следующем примере показан виджет, который использует system_app_widget_background_radius
для угла виджета и system_app_widget_inner_radius
для просмотра внутри виджета.
![Виджет, показывающий радиусы фона виджета и виды внутри виджета.](https://developer.android.com/static/images/appwidgets/widget-weather.png?authuser=7&hl=ru)
1 Угол виджета.
2 Угол обзора внутри виджета.
Важные соображения относительно закругленных углов
- Сторонние программы запуска и производители устройств могут переопределить параметр
system_app_widget_background_radius
, чтобы он был меньше 28 dp. Параметрsystem_app_widget_inner_radius
всегда на 8 dp меньше значенияsystem_app_widget_background_radius
. - Если ваш виджет не использует
@android:id/background
и не определяет фон, который обрезает его содержимое на основе контура (если дляandroid:clipToOutline
установленоtrue
), средство запуска автоматически идентифицирует фон и обрезает виджет, используя прямоугольник с закругленными углами. до 16 дп. См. раздел Убедитесь, что ваш виджет совместим с Android 12 .
Для совместимости виджета с предыдущими версиями Android мы рекомендуем определить пользовательские атрибуты и использовать пользовательскую тему для их переопределения для Android 12, как показано в следующих примерах XML-файлов:
/values/attrs.xml
<resources>
<attr name="backgroundRadius" format="dimension" />
</resources>
/values/styles.xml
<resources>
<style name="MyWidgetTheme">
<item name="backgroundRadius">@dimen/my_background_radius_dimen</item>
</style>
</resources>
/values-31/styles.xml
<resources>
<style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item>
</style>
</resources>
/drawable/my_widget_background.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="?attr/backgroundRadius" />
...
</shape>
/layout/my_widget_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:background="@drawable/my_widget_background" />