Создайте виджет приложения с помощью Glance

манифест, метаданные

В следующих разделах описано, как создать простой виджет приложения с помощью Glance.

Объявите AppWidget в файле Manifest.

После завершения шагов настройки объявите AppWidget и его метаданные в вашем приложении.

  1. Расширьте функциональность обработчика AppWidget , используя GlanceAppWidgetReceiver :

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
        override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
    }

  2. Зарегистрируйте поставщика виджета приложения в файле AndroidManifest.xml и в соответствующем файле метаданных:

        <receiver android:name=".glance.MyReceiver"
        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/my_app_widget_info" />
    </receiver>
    

Добавьте метаданные AppWidgetProviderInfo

Далее, следуя инструкциям по созданию виджета , создайте и определите информацию о виджете приложения в файле @xml/my_app_widget_info .

Единственное отличие Glance заключается в отсутствии XML-файла initialLayout , который необходимо определить самостоятельно. Вы можете использовать предопределенный макет загрузки, предоставленный в библиотеке:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

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

Объект AppWidgetProviderInfo определяет основные характеристики вашего виджета. Определите AppWidgetProviderInfo в файле метаданных XML ( res/xml/my_app_widget_info.xml ) внутри элемента <appwidget-provider> :

<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/glance_default_loading_layout"
    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
  • Начиная с Android 12, атрибуты targetCellWidth и targetCellHeight определяют размер виджета по умолчанию в ячейках сетки. В Android 11 и более ранних версиях эти атрибуты игнорируются и могут быть проигнорированы, если главный экран не поддерживает сеточную компоновку.
  • Атрибуты minWidth и minHeight задают размер виджета по умолчанию в dp. Если значения минимальной ширины или высоты виджета не соответствуют размерам ячеек, то значения округляются до ближайшего размера ячейки.
Мы рекомендуем указывать оба набора атрибутов 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 знаков после запятой в ширину и 50 знаков после запятой в высоту.
  • Предоставляется следующая спецификация атрибутов:
<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 в качестве размера виджета по умолчанию.

По умолчанию размер виджета составляет 2х2. Размер виджета можно уменьшить до 2х1 или увеличить до 4х3.

Android 11 и ниже:

Используйте атрибуты minWidth и minHeight для вычисления размера виджета по умолчанию.

Ширина по умолчанию = Math.ceil(80 / 30) = 3

Высота по умолчанию = Math.ceil(80 / 50) = 2

По умолчанию размер виджета составляет 3х2. Размер виджета можно уменьшить до 2х1 или увеличить до полноэкранного режима.

Дополнительные атрибуты виджета

В следующей таблице описаны атрибуты <appwidget-provider> , относящиеся к параметрам, отличным от размера виджета.

Атрибуты и описание
updatePeriodMillis Определяет, как часто платформа виджетов запрашивает обновление у GlanceAppWidgetReceiver , вызывая метод обратного вызова onUpdate() . Мы рекомендуем обновлять виджеты как можно реже — не чаще одного раза в час — чтобы экономить заряд батареи. Подробнее см. раздел « Когда обновлять виджеты» в разделе управления состоянием Glance.
initialLayout Указывает на ресурс макета, определяющий макет загрузки виджета перед рендерингом композиций пользовательского интерфейса Glance. Вы можете использовать предопределенный макет загрузки, предоставленный в библиотеке: @layout/glance_default_loading_layout .
configure Определяет действие по настройке, которое запускается при добавлении виджета пользователем. См. раздел «Реализация действия по настройке виджета» на этой странице.
description Задает описание для средства выбора виджета, которое будет отображаться для вашего виджета. Введено в Android 12.
previewLayout (Android 12) и previewImage (Android 11 и ниже)
  • Начиная с Android 12, атрибут previewLayout задает масштабируемый предварительный просмотр, который вы предоставляете в виде XML-макета, заданного размером виджета по умолчанию. В идеале, он указывает на статическое XML-сопоставление, соответствующее вашему макету дизайна.
  • В Android 11 и более ранних версиях атрибут previewImage задает статическое изображение (скриншот) того, как выглядит виджет, отображаемый в окне выбора виджетов.
Мы рекомендуем указать оба параметра, чтобы ваше приложение корректно работало на более старых платформах. Для более новых платформ (Android 15+) вы можете определить предварительный просмотр в реальном времени в Kotlin, используя `GlanceAppWidget.providePreview`. См. руководство по предварительному просмотру .
autoAdvanceViewId Указывает идентификатор представления дочернего элемента виджета, который автоматически перемещается в зависимости от хоста виджета.
widgetCategory Определяет, может ли ваш виджет отображаться на главном экране ( home_screen ), экране блокировки ( keyguard ) или на обоих. Для Android 5.0 и выше допустимо только значение home_screen .
widgetFeatures Объявляет поддерживаемые виджетом функции. Например, если конфигурация вашего виджета является необязательной, укажите одновременно configuration_optional и reconfigurable .

Определить GlanceAppWidget

  1. Создайте новый класс, наследующий от GlanceAppWidget и переопределяющий метод provideGlance . В этом методе вы можете загрузить данные, необходимые для отображения вашего виджета:

    class MyAppWidget : GlanceAppWidget() {
    
        override suspend fun provideGlance(context: Context, id: GlanceId) {
    
            // In this method, load data needed to render the AppWidget.
            // Use `withContext` to switch to another thread for long running
            // operations.
    
            provideContent {
                // create your AppWidget here
                Text("Hello World")
            }
        }
    }

  2. Создайте его экземпляр в виджете glanceAppWidget в вашем GlanceAppWidgetReceiver :

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
    
        // Let MyAppWidgetReceiver know which GlanceAppWidget to use
        override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
    }

Теперь вы успешно настроили AppWidget с помощью Glance.

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

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

Объявите виджет в манифесте.

Объявите подкласс класса GlanceAppWidgetReceiver в качестве широковещательного приемника в файле AndroidManifest.xml :

<receiver android:name="ExampleAppWidgetReceiver"
          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/my_app_widget_info" />
</receiver>

Элемент <receiver> требует атрибута android:name , который указывает класс получателя. Получатель должен принимать широковещательное действие ACTION_APPWIDGET_UPDATE внутри <intent-filter> .

Элемент <meta-data> должен указывать свое имя как android.appwidget.provider , а атрибут android:resource должен указывать на ваш XML-ресурс метаданных AppWidgetProviderInfo ( @xml/my_app_widget_info ).

Реализуйте класс AppWidgetProvider.

В Glance вы наследуете GlanceAppWidgetReceiver вместо AppWidgetProvider напрямую. Реализуйте его, связав ваш приемник с экземпляром GlanceAppWidget . Основные коллбэки, доступные в GlanceAppWidgetReceiver работают следующим образом:

  • onUpdate() : Автоматически переопределяется Glance для выполнения обновлений композиции. Если вы переопределяете onUpdate вручную, необходимо вызвать super.onUpdate , чтобы Glance мог успешно запустить потоки композиции.
  • onAppWidgetOptionsChanged() : Вызывается при первом размещении или изменении размера виджета. Glance считывает элементы пакета параметров, чтобы ваш макет плавно подстраивался под размеры во время выполнения.
  • onDeleted(Context, IntArray) : Вызывается всякий раз, когда пользователь удаляет конкретный экземпляр виджета.
  • onEnabled(Context) : Срабатывает при успешном создании первого экземпляра вашего виджета. Отлично подходит для выполнения глобальных миграций.
  • onDisabled(Context) : Вызывается при удалении последнего активного экземпляра поставщика.
  • onReceive(Context, Intent) : Перехватывает каждое широковещательное сообщение платформы перед определенными методами обратного вызова. Необходимо убедиться, что любая написанная вами пользовательская логика обработчика вызывает super.onReceive(context, intent) и никогда не вызывает goAsync самостоятельно, поскольку Glance автоматически выполняет маршрутизацию асинхронно.

Получать широковещательные намерения виджета

Внутри GlanceAppWidgetReceiver фильтрует и обрабатывает следующие базовые интенты широковещательной рассылки виджетов платформы:

Создать пользовательский интерфейс

Следующий фрагмент кода демонстрирует, как создать пользовательский интерфейс:

/* Import Glance Composables
 In the event there is a name clash with the Compose classes of the same name,
 you may rename the imports per https://kotlinlang.org/docs/packages.html#imports
 using the `as` keyword.

import androidx.glance.Button
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.text.Text
*/
class MyAppWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // Load data needed to render the AppWidget.
        // Use `withContext` to switch to another thread for long running
        // operations.

        provideContent {
            // create your AppWidget here
            MyContent()
        }
    }

    @Composable
    private fun MyContent() {
        Column(
            modifier = GlanceModifier.fillMaxSize(),
            verticalAlignment = Alignment.Top,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button(
                    text = "Home",
                    onClick = actionStartActivity<MyActivity>()
                )
                Button(
                    text = "Work",
                    onClick = actionStartActivity<MyActivity>()
                )
            }
        }
    }
}

Приведённый выше пример кода выполняет следующие действия:

  • В верхнем Column элементы располагаются вертикально один за другим.
  • Column расширяет свой размер в соответствии с доступным пространством (с помощью GlanceModifier , выравнивает свое содержимое по верхнему краю ( verticalAlignment ) и центрирует его по горизонтали ( horizontalAlignment ).
  • Содержимое Column определяется с помощью лямбда-функции. Порядок имеет значение.
    • Первый элемент в Column — это Text компонент с отступом 12.dp .
    • Второй элемент — это Row , где элементы располагаются горизонтально один за другим, а две Buttons центрированы по горизонтали ( horizontalAlignment ). Окончательный вид зависит от доступного пространства. На следующем изображении показан пример того, как это может выглядеть:
destination_widget
Рисунок 1. Пример пользовательского интерфейса.

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

Примените закругленные углы.

В Android 12 появились системные параметры для динамической настройки радиусов скругления углов виджетов вашего приложения:

  • system_app_widget_background_radius : Задает радиус скругления углов контейнера фона виджета (никогда не превышает 28 dp).
  • Внутренний радиус: Чтобы предотвратить обрезку содержимого, рассчитайте пропорциональный радиус для внутреннего содержимого на основе контура фона системы: systemRadiusValue - widgetPadding

В Glance можно динамически применять свойства изменения радиуса скругления углов в композиции, используя GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius) .

Для обеспечения обратной совместимости с устройствами под управлением Android 11 (уровень API 30) или более ранних версий реализуйте резервные варианты пользовательских атрибутов и пользовательских ресурсов темы:

  • /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>