Оставайтесь гидратированными

Недавно Android объявил об Альфа-релизе Jetpack Glance, фреймворка для создания виджетов, созданного на основе Jetpack Compose.

С выпуском Android 12 виджеты приложений были обновлены, чтобы значительно улучшить работу как разработчиков, так и пользователей.

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

В качестве введения в использование Glance будет разработан простой виджет приложения, который будет отслеживать, сколько стаканов воды пользователь выпил за день. Репозиторий Github можно найти здесь.

Начальная настройка

Чтобы приступить к созданию виджета приложения, необходимо выполнить некоторые предварительные шаги.

Glance совместим с последней стабильной версией Android Studio, поэтому его необходимо соответствующим образом обновить.

Что касается проекта, потребуется несколько зависимостей gradle.

Поскольку Glance основан на фреймворке компоновки, вместе с самим Glance требуется несколько зависимостей компоновки. Кроме того, для проекта необходимо включить компоновку.

Поскольку Glance доступен только в виде SNAPSHOT, которые периодически создаются в репозитории androidx.dev, его необходимо будет включить в settings.gradle:

Объявление и предварительный просмотр виджета

Прежде чем писать какие-либо составные элементы, виджет должен быть объявлен в манифесте Android с дополнительной определяющей информацией.

Информация о виджете

Виджеты Glance, как и несоставные виджеты, требуют XML-определения для своих атрибутов.

В проекте можно создать новый каталог ресурсов с именем xml, который будет его содержать. В пределах xml будет создан новый файл water_widget_info.xml.

appwidget-provider предоставляет Android атрибуты виджета.

  • Имя виджета (в данном случае WaterWidget)
  • Информация о его минимальном размере в DP
  • Как пользователь может изменить размер виджета
  • Размер виджета по умолчанию, новый в Android 12, автоматически устанавливается как 2x2, но его можно масштабировать от 2x1 до 4x3.
  • Категория, определяющая тип виджета; это может быть домашний экран, KeyGuard или окно поиска

С этой базовой информацией эту информацию xml теперь можно включить в манифест.

Манифест Android

Чтобы правильно объявить виджет, получатель должен быть написан в манифесте Android.

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

Сначала будет создан файл с именем WaterWidget.kt, в котором будут размещены оба этих компонента.

WaterWidgetReceiver расширяет GlanceAppWidgetReceiver, который действует как поставщик виджета.

Этот предоставленный виджет WaterWidget содержит компонуемую функцию Content(), которая будет содержать макет виджета.

Как только эти классы определены, получатель может быть добавлен в манифест.

  • name будет использовать ранее созданный класс приемника: WaterWidgetReceiver
  • Фильтр намерений объявляется для получения обновлений виджета.
  • Метаданные будут использовать ранее определенные атрибуты в water_widget_info.xml

Исходный и предварительный макет

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

К сожалению, эти макеты нужно будет писать в xml, так как compose здесь пока не поддерживается. Поскольку эта часть кода по-прежнему основана на RemoteView, потребуется один из поддерживаемых макетов xml:

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • Макет сетки

ConstraintLayout не будет работать для начального или предварительного макета виджета.

Исходный макет — это устаревший элемент, содержащий макет для виджета, объявленный в XML.

На данный момент в документации для Glance поясняется, что требуется первоначальный макет, но в будущем он будет удален. Таким образом, простой макет фрейма будет выступать в качестве заполнителя до тех пор, пока он не будет удален с последующими обновлениями Glance.

Макет предварительного просмотра будет отображать предварительный просмотр виджета, когда пользователь выбирает его в меню виджетов и перетаскивает на главный экран.

Этот предварительный просмотр создан для имитации того, как будет выглядеть виджет, встроенный в Compose. Их можно добавить к атрибутам в water_widget_info.xml:

Примечание. До Android 12 в предварительных просмотрах использовался атрибут android:previewImage и использовалось изображение вместо xml для отображения предварительного просмотра. Для обратной совместимости также используйте этот метод.

Надеемся, что после обновления Glance устранится необходимость создавать какие-либо из этих xml-компонентов, поскольку создание как xml, так и составного макета противоречит здравому смыслу.

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

Когда все это готово, пришло время написать несколько составных частей для виджета. Сам виджет прост, он будет содержать четыре основных компонента:

  • Текст, показывающий счетчик того, сколько стаканов воды выпил пользователь.
  • Текст, показывающий рекомендуемое ежедневное количество стаканов
  • Кнопка для добавления стакана на прилавок
  • Кнопка сброса счетчика

Написание компонуемого контента

Можно создать отдельный файл с именем WaterWidgetContent.kt, в котором будут находиться компонуемые функции.

Примечание: поскольку это компонуемые компоненты Glance, используемые компоненты будут взяты из фреймворка Glance, а не из стандартной компоновки. Таким образом, они будут использовать GlanceModifier, а не Modifier, чтобы изменить свое поведение.

Начиная с текста счетчика, эта функция извлечет форматированную строку и передаст ей количество выпитых стаканов в качестве аргумента.

Эта форматированная строка принимает числовой тип (в данном случае Int) для отображения прогресса пользователя. Обратите внимание, что эти Composables предоставляются Glance, а не обычной библиотекой Androidx. Эти компоненты пользовательского интерфейса и модификатор Glance ведут себя очень похоже на свои аналоги в Androidx.

Далее текст цели будет содержать логику для выбора строки в зависимости от того, достиг пользователь цели или нет.

Константа RECOMMENDED_DAILY_GLASSES может быть определена еще в WaterWidget.kt в сопутствующем объекте.

Теперь можно объявить кнопки добавления и сброса счетчика. Во-первых, векторные ресурсы для каждого из изображений необходимо добавить в пакет drawable res.

Для добавления к счетчику будет использоваться ассет plus, а для сброса счетчика будет использоваться ассет «мусорный бак». Эти изображения можно объявить внутри Row, чтобы выровнять их по горизонтали.

Чтобы написать обработку щелчка, необходимо создать Glance ActionCallback для каждой из двух кнопок.

Обратные вызовы действий

Взгляд ActionCallbacks именно то, что они подразумевают; они выполняют действие при получении уведомления.

В новом файле с именем WaterWidgetActions можно создать два класса, которые будут расширять ActionCallbacks.

Эти функции используют функцию приостановки родительского класса onRun(); при вызове обратного вызова будет выполняться логика onRun().

Эти обратные вызовы могут быть прикреплены к соответствующим кнопкам изображения с помощью actionRunCallback<T>() в сочетании с clickable(…) .

Однако перед реализацией фактической логики в любом из этих обратных вызовов необходимо изучить обработку состояния.

Использование состояния во взгляде

Чтобы виджет правильно отслеживал, сколько раз пользователь увеличивал счетчик, ему необходимо отслеживать состояние; это можно сделать с помощью компонентов, предоставляемых Glance.

Вернувшись в AddWaterClickAction, можно изменить функцию onRun():

Здесь функция приостановки updateAppWidgetState(…) предоставляет возможность доступа и редактирования хранилища данных.

В этом случае, поскольку в качестве параметра было передано PreferencesGlanceStateDefinition, используется хранилище данных Android. Значения из DataStore можно получить с помощью соответствующего Preferences.Key в зависимости от ожидаемого типа возвращаемого значения. В этом случае (для количества стаканов воды) используется intPreferencesKey.

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

Константы для WATER_WIDGET_PREFS_KEY и MAX_GLASSES можно определить в сопутствующем объекте для класса WaterWidget:

Для ClearWaterClickAction логика проще:

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

При правильном сохранении этих значений к ним можно получить доступ из состояния внутри самого виджета.

Собираем все вместе

В WaterWidgetContent.kt можно создать компонуемую функцию для инкапсуляции каждого из отдельных компонентов виджета, а также предоставления контекста и состояния для каждого из них.

Здесь Glance использует LocalContext.current для предоставления контекста виджета каждому составному объекту, а stateDefinition переопределяется для предоставления состояния. Поскольку хранилище данных ранее использовалось для хранения значений в обратных вызовах, для доступа к ним снова используется PreferencesGlanceStateDefinition. Теперь currentState<Preferences>() можно использовать в составной функции для получения необходимых данных. Как и раньше, количество стаканов, которые пользователь выпил сегодня, можно получить с помощью WATER_WIDGET_PREFS_KEY и передать каждому из текстовых компонентов.

Как упоминалось ранее, GlanceAppWidget содержит компонуемую функцию Content(), из которой эта компонуемая функция может быть вызвана. После завершения макета работающий виджет можно добавить на главный экран!

Поскольку Glance все еще находится на ранней стадии, этот код может быть изменен, но, надеюсь, этот ранний взгляд может помочь с грядущим!