Реализация Android виджетов: дизайн, пропорции, прозрачность

Реализация Android виджетов: дизайн, пропорции, прозрачностьСегодня сложно представить себе Android-устройство без виджетов. Они появились в Android 1.5 и были сильно переработаны в Android 3.0 и 3.1. Виджеты служат для быстрого доступа к информации приложения, управляют функциями приложения без входа в него и могут быть настроены индивидуально под пользователя.

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

Условно виджеты можно разделить на три основных типа:

Информационные: предоставляют самую актуальную информацию о состоянии приложения. К этому типу относятся виджеты погоды, часов, котировок валют и другие.

Виджеты-списки: служат для отображения коллекций однотипной информации. Например, список писем, новостей или список дел на текущий день.

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

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

На примере нашего недавнего проекта мы покажем вам, как реализовать Android виджет под конкретные требования клиента и преодолеть проблемы, возникающие во время разработки.

Задача

От одного из постоянных клиентов Azoft – компании Новотелеком (интернет-провайдера «Электронный город») – поступил запрос на разработку приложения «Личный кабинет». Приложение предназначено для пользователей интернет-провайдера. С его помощью можно узнавать баланс счёта, пополнять его, активировать отсрочку платежа, а также подключать новые опции, менять тарифный план и многое другое.

В рамках проекта была задача по реализации виджета для приложения.

Создание виджета предполагало выполнение следующих требований:

  • Виджет должен отображать актуальные данные счета и показывать время последнего обновления.
  • На виджете должны располагаться 2 кнопки: переход на экран настроек и принудительное обновление по запросу пользователя.
  • В нём должен быть реализован экран конфигурации с возможностью установки времени обновления данных лицевого счета и изменения прозрачности виджета.

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

Реализация

При реализации Android-виджетов разработчику всегда приходится оперировать четырьмя ключевыми инструментами:

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

Подробности реализации описаны на developer. android. com. здесь же мы рассказываем только о вопросах отображения виджета, которые нас больше всего зацепили.

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

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

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

Убедившись в том, что стандартным подходом создать виджет не получится, мы стали искать решение для приведения виджета в соответствие с желаемым дизайном:

Из-за того, что края фона скругленные, можно было использовать либо png, либо shape. Остановились на png.

В дизайне виджет был изображен горизонтально, а ячейки на устройствах Android – вертикальные. Чтобы сохранить пропорции виджета, мы жестко задали высоту и ширину для лейаута. А для того, чтобы на всех девайсах виджет был соответствующего размера, разметку виджета пришлось указать для нескольких разрешений.

Прозрачность фона меняется динамически и зависит от настроек виджета. Чтобы изменить объект RemoteViews, у виджета есть методы setInt, setFloat, setBitmap и другие, куда в качестве параметров передаются идентификатор View, название метода и параметр для этого метода.

Следующим шагом для установки фона мы использовали метод setBitmap:

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

Работа с альфой

Сначала мы просто заменяли прозрачность png на необходимую. То есть, всему изображению выставлялась альфа, равная 1, в результате чего углы приобрели чёрный цвет.

Затем мы решили менять альфу только непрозрачным пикселям. Однако углы получились острыми, с торчащими по краям пикселями, потому что в png сглаженность углов достигается пикселями с разной альфой.

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

Результат

После нескольких попыток задать прозрачность с помощью альфы, мы наконец-то получили желаемый вид виджета:

С учётом вертикальной ориентации ячеек в Android, сам виджет занял больше места, чем его видимая часть. То есть для пользователя отображается только картинка определенного размера.

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