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

IT-копирайтер
Время чтения: 5 минут
Сегодня сложно представить себе 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:
views.setBitmap(R.id.iv_widget_background, "setImageBitmap", newBitmap);
Для изменения прозрачности фона мы сначала попиксельно меняли альфу у Bitmap, а потом подставляли ее на бэкграунд.
Работа с альфой
Сначала мы просто заменяли прозрачность png на необходимую. То есть, всему изображению выставлялась альфа, равная 1, в результате чего углы приобрели чёрный цвет.
pixel = pixels[index]; A = alpha; R = Color.red(pixel); G = Color.green(pixel); B = Color.blue(pixel); pixels[index] = Color.argb(A,R,G,B);
Затем мы решили менять альфу только непрозрачным пикселям. Однако углы получились острыми, с торчащими по краям пикселями, потому что в png сглаженность углов достигается пикселями с разной альфой.
pixel = pixels[index]; A = Color.alpha(pixel); if (A > 0) { A = alpha; } R = Color.red(pixel); G = Color.green(pixel); B = Color.blue(pixel); pixels[index] = Color.argb(A,R,G,B);
Тогда решили прибегнуть к очередному улучшению – менять альфу только для пикселей с большей видимостью, чем текущая альфа. Это позволило сохранить альфу у пикселей с большей прозрачностью, которые и обеспечивают гладкий вид углов.
pixel = pixels[index]; A = Color.alpha(pixel); if (A > alpha) { A = alpha; } R = Color.red(pixel); G = Color.green(pixel); B = Color.blue(pixel); pixels[index] = Color.argb(A,R,G,B);
Результат
После нескольких попыток задать прозрачность с помощью альфы, мы наконец-то получили желаемый вид виджета:
С учётом вертикальной ориентации ячеек в Android, сам виджет занял больше места, чем его видимая часть. То есть для пользователя отображается только картинка определенного размера.
Конечно, этот способ поставил перед нами определенные рамки: чтобы виджет выглядел максимально нативно, нам пришлось под каждый размер создавать отдельный лейаут. Однако предложенное нами решение позволяет обойти присущие виджетам ограничения, а значит, выполняет свою задачу.
Комментарии