Привет! Я с разработкой виджетов никогда не пересекался, честно говоря даже забыл о них, но на работе возникла задача — изучить, рассказать и интегрировать виджеты в приложение. После того, как задача была закончена решил поделиться своим опытом. Сталкивался с трудностями, буду рад, если статья поможет их разрешить.
Что такое виджеты и почему они особенные
Android виджеты используют RemoteViews — специальный механизм для отображения UI за пределами своего процесса. RemoteViews это вью, имеющие ограниченную функциональность по сравнению с обычными View, которыми мы привыкли пользоваться. Работа с виджетами с каждой версией улучшалась и в итоге вышел новый подход к проектированию виджетов. Библиотека Glance. Она похожа на декларативную верстку через Compose, но все же стоит понимать, что это не он.
Почему glance это не compose:
-
Ограниченный набор компонентов
-
Отсутствие прямых обработчиков событий
-
Ограниченные модификаторы
-
Отсутствие рекомпозиции
-
Разная система тем и стилей
На самом деле различий намного больше, это только часть из них
Почему нельзя использовать обычные view:
-
Производительность — виджеты должны быть легковесными
-
Виджеты работают в процессе launcher из-за чего нет доступа к твоему приложению. RemoteViews — это механизм Android для кросс-процессного рендеринга UI
-
Согласованность — единый подход для всех виджетов на устройстве.
По сути Glance — это современный API-мост между декларативным стилем Compose и системными ограничениями Android виджетов, которые требуют использования RemoteViews для кросс-процессного рендеринга.
Исторический контекст
Android 1.5 (API 3) — Рождение виджетов
Первая версия виджетов была простой и функционально ограниченной:
-
Отображение статической информации
-
Базовые взаимодействия (открытие приложения)
-
Минимальные возможности кастомизации
Эволюция: от API 3 до Android 11
С каждой версией Android виджеты становились мощнее:
-
Поддержка настройки размеров — гибкая адаптация под сетки
-
Списки и коллекции —
ListView,GridViewв виджетах -
Auto-resize — автоматическое масштабирование контента
-
Material Design — современные визуальные стандарты
-
Обновления по расписанию — фоновые работы
Android 12 (API 31) — Революция в мире виджетов
Android 12 принес кардинальные изменения:
-
Новый гибкий API — переработанная архитектура
-
Сложные анимации — плавные переходы и эффекты
-
Кастомные вью — расширенные возможности кастомизации
-
Динамические цвета — адаптация под тему системы
-
Улучшенная производительность — оптимизация рендеринга
Добавление виджета
Мы будем пользоваться новым API для создания виджета, поэтому сразу подключим библиотеку Glance с актуальной версией:
implementation("androidx.glance:glance-appwidget:1.1.1")
После чего у нас появятся все необходимые инструменты.
Далее необходимо сделать важные 4 пункта:
-
Создать наследника GlanceAppWidget
-
Создать наследника GlanceAppWidgetReceiver
-
Создать конфигурационный файл, описывающий для системы настройки виджета
-
Подключить это все в manifest
GlanceAppWidget
Этот класс будет отвечать за весь UI и взаимодействие с ним. Напишем простой контейнер для начала.
class AppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { provideContent { GlanceTheme { } } } }
И по сути мы можем описывать внутри GlanceTheme UI в таком же декларативном стиле, как и Compose.
GlanceAppWidgetReceiver
Этот класс отвечает за регистрацию твоего виджета в манифесте, позволяет обрабатывать системные события, связанные с твоим виджетом. Так же возвращает экземпляр твоего виджета
Почему его нужно регистрировать в Manifest.xml? Потому что он наследник BroadcastReceiver:
class AppWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget = AppWidget() }
Файл конфигурации виджета
Это глобальный файл с настройками твоего виджета, там можно много чего указать, разберем несколько свойств
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/app_widget_description" android:targetCellHeight="2" android:targetCellWidth="2" android:resizeMode="vertical|horizontal" android:previewLayout="@layout/widget_preview_layout" android:previewImage="@drawable/widget_preview_image" android:maxResizeWidth="600dp" android:maxResizeHeight="120dp" android:minResizeWidth="120dp" android:minResizeHeight="40dp" android:minHeight="120dp" android:minWidth="120dp" android:updatePeriodMillis="0" android:widgetFeatures="reconfigurable" />
-
description — описание виджета, которое пользователь увидит в списке виджетов, когда решит его добавить
-
targetCellHeight/targetCellWidth — новая функциональность для Android 12 (API 31) +. Весь экран представляет собой сетку. Каждая иконка приложения это по сути виджет размера 1х1. Мы можем задать размер виджета, используя сетку
-
updatePeriodMillis — частота обновления виджета. В данном случае мы указываем 0, поскольку за обновление виджета в статье будет отвечать WorkManager
-
widgetFeatures — говорит о том, что у виджета есть возможность изменять настройки. В будущем мы добавим конфигурационную activity, в которой можно будет менять настройки виджета. Эта activity будет вызвана при первом добавлении виджета на экран, а значение reconfigurable говорит о том, что мы можем сколько угодно раз изменять конфигурацию виджета
-
previewLayout/previewImage — image для Android 11 (API 30) -, layout для версий выше. Это изображение, которое видит пользователь, когда находится в списке виджетов. Раньше была просто статическая картинка, теперь это полноценный xml файл, который и размер системы учтет, и dark mode режим и т.д.
Manifest
Соберем это все безобразие в один файл и подключим к нашему приложению
<receiver android:name=".widgets.AppWidgetReceiver" android:exported="false" android:enabled="true" android:label="@string/app_widget_name"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/app_widget_provider" /> </receiver>
Я так же опустил здесь подключение конфигурационной activity, но доберемся до этого позже
UI
Теперь мы можем добавить наш виджет на экран. Но, чтобы увидеть какой-нибудь результат — добавим заглушку.
Для начала добавим data class для того, чтобы держать какие-то данные, которые мы можем отобразить. Я создал следующий класс:
data class AppWidgetDataContent( @StringRes val titleResId: Int, val description: String, val value: Int )
Создал из него заглушку и сверстал следующий контент:
@Composable fun AppWidgetContent( state: AppWidgetDataContent, onRefreshClick: () -> Unit ) { Column( modifier = GlanceModifier .fillMaxSize() .padding(12.dp) .background(GlanceTheme.colors.widgetBackground) ) { AppWidgetTitleContent(state.titleResId, false, onRefreshClick) } } @Composable private fun AppWidgetTitleContent( @StringRes titleResId: Int, isLoading: Boolean, onRefreshClick: () -> Unit ) { val context = LocalContext.current Row( modifier = GlanceModifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { Text( text = context.getString(titleResId), modifier = GlanceModifier.defaultWeight(), maxLines = 1, style = TextStyle( fontSize = 16.sp, fontWeight = FontWeight.Medium, fontFamily = FontFamily.SansSerif, color = GlanceTheme.colors.onSurface ) ) when (isLoading) { true -> { CircularProgressIndicator( modifier = GlanceModifier.size(24.dp), color = GlanceTheme.colors.outline ) } false -> { Image( provider = ImageProvider(R.drawable.vec_refresh_primary), contentDescription = null, modifier = GlanceModifier.clickable { onRefreshClick() } ) } } } }
И добавил эту верстку в наш AppWidget в блок GlanceTheme. Важный нюанс верстки. Для отрисовки UI мы пользуемся библиотекой Glance. То есть импорты у нас следующие:
import androidx.glance.layout.Column import androidx.glance.layout.Alignment import androidx.glance.text.Text ...
Не перепутай.
Итог
Я показал как создать и добавить простой виджет для Android с использованием библиотеки Glance.
От автора
Это моя первая статья. Контента для нее оказалось слишком много и я решил разбить ее на несколько частей. Пока не знаю, сколько получится глав, но в планах показать следующее:
-
Добавление конфигурационной activity, в которой можно изменять настройки виджета;
-
Обновление виджета при помощи WorkManager. Например, у нас есть сетевой слой, там загружаются какие-то данные. Эти данные будут сохраняться в локальное хранилище, откуда виджет будет их доставать;
-
Взаимодействие с виджетом.
Возможно будут еще идеи и я их допишу. Часть кода со временем будет меняться, поэтому в итоговой статье я приложу ссылку на гитхаб.
ссылка на оригинал статьи https://habr.com/ru/articles/942886/
Добавить комментарий