Проектируем виджет
Цель нашего проекта — создать виджет, который периодически пингует указанные пользователем сайты и выводит соответствующую информацию на домашний экран. Традиционно будем использовать редактор кода Eclipse с плагином ADT.
В андроиде любой виджет представляет собой визуальный компонент, работающий в рамках того приложения, в которое он встраивается (как правило, это домашний экран). Кроме того, виджет умеет выводить устройство из режима ожидания, чтобы отобразить на экране актуальную информацию. Поэтому при разработке виджета нужно свести к минимуму время его обновления (можно, конечно, и пренебречь, но ANR в виджете — это уже моветон). Вполне логично напрашивается некоторый фоновый сервис, который будет пинговать сайты и записывать результат для дальнейшего анализа. Таким образом, задача виджета сведется к извлечению этих данных и выводу информации в виде текстовой строки — ссылки на сайт и некоторой графики — доступности сайта (рис. Также нам потребуется простенькая форма для ввода списка подконтрольных сайтов, то есть GUI. Кстати, настоятельно рекомендую ознакомиться со статьей в мартовском номере «Хакера» «Хакерский Cron на Android», поскольку там подробно рассмотрена работа фонового сервиса.
Ping? Нет, не знаю.
В Java есть замечательный класс InetAddress, имеющий в своем чреве не менее замечательный метод isReachable(). Этот метод проверяет доступность ресурса и принимает в качестве параметра тайм-аут, то есть время, по истечении которого не отвечающий ресурс считается недоступным:
if (InetAddress. getByName(«www. xakep. ru»). isReachable(5000)). // Сайт доступен
Разрешения
Так как наша цель — сайты в интернете, необходимо получить соответствующие разрешения у пользователя в манифесте проекта (AndroidManifest. xml):
Первая строка запрашивает разрешение на доступ в интернет, тогда как вторая позволяет отслеживать состояние подключения к сети (в терминологии Play Market — «просмотр состояния сети») — если сеть недоступна, пинговать что-либо особого смысла нет. Функция проверки подключения к сети выглядит следующим образом:
Для доступа к сервису управления сетевыми подключениями используется константа Context. CONNECTIVITY_SERVICE, после чего метод getActiveNetworkInfo возвращает объект типа NetworkInfo, который содержит информацию о текущем соединении. Если подключение установлено, метод isConnected вернет true.
GUI
Здесь все просто — несколько полей ввода (EditText) да кнопка (Button). Вся эта красота представлена на рис. Обработчик кнопки (см. Main. java) считывает содержимое полей (применяется коллекция ArrayList) и записывает их в файл общих настроек, используя метод setData описанного выше класса PingPref:
В качестве первоначального статуса доступности сайта, как и условились, устанавливаем –1. В заключение происходит запуск фонового сервиса startService, подробнее о котором поговорим далее.
Фоновый сервис
Начиная с Android 3. 0 (версия API 11), любой функционал, связанный с сетевой активностью, должен обязательно выполняться во вторичном потоке. Любая попытка подобной работы в главном (графическом) потоке приложения приведет к выбросу исключения NetworkOnMainThreadException и немедленному завершению программы. Это правило относится и к фоновому сервису, так как он тоже фактически выполняется в главном потоке. Как ты уже, наверное, догадался (а если нет — срочно покупай мартовский «Хакер»), мы будем использовать IntentService. Данный фоновый сервис берет на себя всю работу по созданию вторичного потока, позволяя нам сосредоточиться непосредственно на функционале.
Для начала зарегистрируем сервис в манифесте:
Основная работа сервиса кипит и бурлит внутри onHandleIntent, текст которого приведен ниже (отладочная печать присутствует):
Ближе к концу создаем уже знакомое тебе отложенное намерение на повторный запуск сервиса через не менее знакомый менеджер сигнализаций. Только вместо метода set будем использовать setRepeating, а точнее — setInexactRepeating. Последний помогает в некоторой степени уменьшить энергозатраты, собирая для выполнения близкие по времени сигнализации. Поэтому вместо точного интервала мы передаем константу AlarmManager. INTERVALFIFTEEN_MINUTES для опроса сайтов примерно через каждые 15 мин и AlarmManager. INTERVAL_HALF_HOUR (~30 мин) в случае отсутствия соединения для новой попытки. Возможно, ты захочешь указать другие константы объекта AlarmManager: INTERVAL_HOUR (час), INTERVAL_HALF_DAY (12 ч), INTERVAL_DAY (раз в сутки). Замечу, что эти интервалы очень_ условные, и при необходимости соблюдения более точного расписания следует использовать метод setRepeating, но, как уже отмечалось, он более прожорлив. К слову, будить устройство мы не станем — используем AlarmManager. ELAPSED_REALTIME, так как обновление информации для виджета при выключенном экране не только не требуется, но и, вероятно, вызовет укоризненный взгляд коллег из рубрики X-Mobile.
Добравшись до середины статьи, мы провели всю подготовительную работу и теперь можем с чистой совестью приступить к главной теме нашего изыскания — созданию виджета.
Виджет
В Android виджет реализуется в виде широковещательного приемника, реагирующего на некоторые события (строго говоря — намерения(Intent)), для наполнения визуальной разметки актуальными данными по таймеру, с помощью сервиса, по клику и так далее. Разработка виджета начинается с регистрации его класса (PingWidget) в манифесте проекта:
Здесь тег intent-filter содержит минимум одно стандартное действие — android. appwidget. action. APPWIDGET_UPDATE, используемое для обновления содержимого виджета (еще есть DELETED, ENABLED и DISABLED, но они необязательны). Так как обновлять виджет мы будем, во-первых, самостоятельно, во-вторых, в разные моменты времени, добавим еще одно действие — com. example. pinger. FORCE_WIDGET_UPDATE для нашей задачи. Кроме того, нам потребуется отдельный XML-файл, описывающий настройки виджета (файл res\xml\widget_provider. xml):
Атрибуты minHeight и minWidth определяют минимально допустимые высоту и ширину виджета. Для расчета этих значений применяется формула
min = 70 dp * (количество ячеек) – 30 dp.
Домашний экран в Android разделен виртуальной сеткой, состоящей из ячеек, размеры которых зависят от физических размеров устройства. Можно сказать, что ярлык приложения на домашнем экране соответствует одной ячейке. Наш виджет будет иметь размеры 4 х 2 или 250 dp x 110 dp (в аппаратно-независимых пикселях). Изменение размеров виджета пользователем мы не планируем, поэтому resizeMode устанавливаем в none.
Атрибут updatePeriodMillis задает минимальный период между обновлениями виджета (в миллисекундах) системой, но нам сейчас он неинтересен, так как виджет мы будем обновлять вручную, как только возникнет такая необходимость. Представь, наш фоновый сервис не запущен, а на рабочем экране висит виджет (типичное состояние устройства после перезагрузки) — Android вызовет процедуру его обновления незамедлительно, а уже потом через updatePeriodMillis миллисекунд. При первом обновлении просто запустим наш сервис, и как только он начнет работать, дальнейшее обновление информации в виджете будет инициировать именно он. Поэтому сейчас смело ставим 86 400 000 (то есть раз в сутки) и двигаемся дальше.
Если ты хочешь, чтобы в меню виджетов вместо иконки приложения красовалась симпатичная картинка (см. рис. 4), добавь ссылку на соответствующий ресурс в формате PNG в атрибуте previewImage. О том, как быстро и просто получить такое превью, читай врезку.
Атрибут initialLayout позволяет указать разметку виджета в формате XML. Да, ты не ошибся, разметка виджета во многом напоминает разметку активности или диалогового окна — те же метки, кнопки, картинки, менеджеры компоновки и прочее. Фрагмент разметки нашего виджета представлен ниже (widget. xml):
Наш виджет состоит из небольших картинок (im1, im2, im3, im4) типа ImageView и текстовых меток (txt1, txt2, txt3, txt4) типа TextView. Для компоновки используем RelativeLayout, то есть все компоненты визуально выровнены друг относительно друга. Разумеется, картинки и надписи мы будем менять при отрисовке виджета. Поле alpha задает непрозрачность виджета в диапазоне от 0 (прозрачный) до 1 (непрозрачный), а вот background позволяет задать картинку для фона с эффектами стекла, бликами и тенями (да, я сторонник скевоморфизма). Для генерации такой текстуры можно воспользоваться онлайн-редакторами, которых в интернете очень много (см. полезные ссылки). Кстати, для правильного масштабирования виджета на разных экранах фон желательно перевести в формат NinePatch, о котором расскажет врезка.
Итак, от визуальной стороны виджета (см. рис. 5) плавно переходим к логике его работы (класс PingWidget. java):
Класс AppWidgetProvider, являющийся широковещательным приемником, предоставляет нам удобные обработчики жизненного цикла виджета: onUpdate и onReceive (есть и другие — onDeleted, onDisabled, onEnabled, но мы их не рассматриваем). Первый вызывается при обновлении интерфейса виджета с периодичностью updatePeriodMillis (см. выше), как и договорились — просто запускаем сервис (как вариант, можно использовать уже полученные, но, возможно, неактуальные данные — все зависит от задачи). Второй, onReceive, срабатывает при получении определенного действия — FORCE_WIDGET_UPDATE, зарегистрированного нами ранее в манифесте проекта. Именно этот код и отвечает за манипуляции с картинками и текстовыми полями в UI виджета. Функция updateWidget сначала запрашивает объект класса AppWidgetManager, который применяется для обновления виджетов и предоставляет информацию о них. В частности, нас интересуют идентификаторы всех виджетов (массив appWidgetIds), ведь их может быть несколько и обновить нужно каждый из них:
В единственном цикле функции drawWidget происходит итерация по всем работающим в данный момент виджетам. Класс RemoteViews используется в качестве компонента для доступа к разметке, размещенной внутри процесса другого приложения (мне одному это напоминает Inject?). Если бы мы работали с активностью или фрагментом, то могли бы использовать вполне обычное findViewById. Но наш виджет работает в рамках домашнего экрана, поэтому для получения доступа к разметке необходимо в конструкторе RemoteViews указать название пакета (context. getPackageName()) и ресурс с разметкой (R. layout. widget). Используя views. setTextViewText и views. setImageViewResource, изменяем надпись и картинку (вспомогательная функция getPicture возвращает ссылку на подходящий ресурс). Как только мы внесли правки по всем строкам, фиксируем их в виджете, вызывая updateAppWidget.
Добавить виджет с картинками на телефоны Samsung
Примечание. Действия, описанные в этом разделе статьи, действительны только для новозеландских товаров. Настройки в вашем телефоне будут другими. За дополнительной информацией обращайтесь Страница поддержки Samsung.
Чтобы добавить виджет изображения на свой телефон Samsung, вы должны использовать предустановленное приложение Picture Frame Widget. Не все версии Android поставляются с виджетом Picture Frame. Так что, если на вашем телефоне Samsung его еще нет, установите его из Galaxy Store.
Теперь перейдем к процедуре добавления виджета изображения на домашний экран Samsung.
Шаг 1: Нажмите и удерживайте главный экран телефона Samsung.
Шаг 2: Выберите «Виджет» и нажмите «Рамка изображения».
Шаг 3: Выберите изображения или альбом, которые вы хотите добавить на главный экран в качестве виджета, и нажмите OK.
Шаг 4: Наконец, нажмите ОК после выбора изображений по вашему выбору.
Вот и все! Вы увидите свои любимые картинки на главном экране в виде виджетов.
В телефонах сторонних производителей есть два варианта добавления виджетов изображений на главный экран Android. Читайте дальше, чтобы узнать, что это такое и как ими пользоваться!
Использование виджета Notes
Как вы знаете, виджет Notes уже доступен на Android. Чтобы добавить изображение на главный экран Android, выполните следующие действия.
Шаг 1: На телефоне Android откройте приложение «Заметки».
Шаг 2: Создать новую заметку, нажав значок +
Шаг 3: В новой заметке добавьте изображения, которые вы хотите видеть на главном экране, щелкнув значок изображения
Шаг 4: Затем нажмите значок меню (три точки одна над другой) в правом верхнем углу и выберите «Поместить на главный экран».
Вуаля! Ваша новая заметка с любимыми изображениями появится на главном экране.
Это было довольно просто, не так ли?
Что ж, у большинства людей возникает вопрос, какие изображения можно разместить на главном экране? Ответ довольно прост — как хотите. Это может быть ваше любимое воспоминание с любимым человеком, ваш распорядок дня, мотивационная цитата, ваш образец для подражания или что-то еще в этом отношении.
10 лучших виджетов аналоговых часов для Android
10 лучших виджетов даты и времени для Android
28 способов решить проблемы с виджетами Android
Использование стороннего приложения.
В Play Store доступно множество сторонних приложений, которые помогут вам добавить виджеты с изображениями на главный экран Android. Из всех, я лично выбрал MagicWidgets — Photo Widgets, iOS Widgets, Custom app для той же цели.
Почему? Что ж, вот причины:
Функции
- Загрузить локальные изображения как виджеты
- Различные виджеты часов и виджеты в стиле календаря
- Несколько шрифтов и цветов
- Показать или скрыть элементы в пользовательских виджетах
- Различные стили виджетов iOS
- Виджеты большого, маленького или среднего размера
- Обратный отсчет и виджеты шагов
Приложение позволяет настроить домашний экран Android и сделать его более эстетичным. Лучшая часть этого приложения заключается в том, что оно поддерживает загрузку локальных изображений, чтобы у вас были самые лучшие воспоминания прямо перед вами.
Во-вторых, функция виджета обратного отсчета позволяет добавлять настраиваемые таймеры обратного отсчета для предстоящих событий, таких как дни рождения, годовщины, праздники, вечеринки, Хэллоуин, Рождество, экзамены, встречи, цели и т.
Приложение имеет рейтинг 4,5 звезды в Play Store и его установили более 100 000 раз. Установите приложение, нажав кнопку ниже.
В новой утечке Android 12 представлены обновленные виджеты и уведомления в преддверии Google I / O 2021
4 удивительных виджета, которые помогут вам сохранить мотивацию
8 лучших погодных виджетов для Android для быстрой проверки на шторм
FAQs
С помощью приемов, упомянутых в статье, вы можете легко добавить виджеты с изображениями на домашний экран Android. Для Samsung виджет «Рамка изображения» — это самый простой способ добавить пользовательские локальные изображения на домашний экран в качестве виджета.
Пользователи Android, отличные от Samsung, могут сделать это с помощью приложения Notes или стороннего приложения. MagicWidgets — Photo Widgets, iOS Widgets, Custom app отлично справляется со своей задачей, обеспечивая при этом дополнительные функции.
Here’s all you need to know about adding picture widgets on Android home screen. I hope I brought some value to your Android usage. Let me know in the comments section below which trick you used to add a picture widget on your Android home screen. Until next time, take care and stay safe! Cheers!
В этом уроке:
— создаем простой виджет — разбираемся в его Lifecycle
Добрались таки и до этой долгожданной темы. Вообще, интересно было бы глянуть статистику, какое количество из всех существующих приложений содержит в себе виджеты. Подозреваю, что не чаще, чем одно из десяти. Возможно, ближайшие несколько уроков эту статистику изменят )
Для понимания темы виджетов желательно знать, что такое BroadcastReceiver. Я о нем упоминал в Уроках 96 и 100. Это просто слушатель, который регистрируется в системе, ловит с помощью настроенного Intent Filter сообщения (Intent) и выполняет какой-либо код. Почему-то не сделал я отдельного урока по нему. Но, думаю, еще сделаю. Есть там свои интересные особенности, о которых можно поговорить.
Думаю, нет особой необходимости подробно объяснять, что такое виджеты. Все их видели на своих девайсах. Это что-то типа мини-приложений расположенных на рабочем столе (Home). Они позволяют просмотреть какую-либо информацию из основных приложений, либо повлиять на поведение этих приложений. В качестве примера можно привести – прогноз погоды, текущее время, баланс какого-либо счета, список сообщений в различных мессенджерах, управление состоянием WiFi/3G/GPS/Bluetooth, яркость экрана и т. и т. В этом уроке сделаем простейший виджет, который отобразит статичный текст.
Чтобы создать простейший виджет нам понадобятся три детали:
1) Layout-файл.
В нем мы формируем внешний вид виджета. Все аналогично layout-файлам для Activity и фрагментов, только набор доступных компонентов здесь ограничен следующим списком:
2) XML-файл c метаданными
В нем задаются различные характеристики виджета. Мы пока что укажем следующие параметры:
— layout-файл (из п. ), чтобы виджет знал, как он будет выглядеть- размер виджета, чтобы виджет знал, сколько места он должен занять на экране- интервал обновления, чтобы система знала, как часто ей надо будет обновлять виджет
3) Класс, наследующий AppWidgetProvider. В этом классе нам надо будет реализовать Lifecycle методы виджета.
Давайте создадим три этих детали. Activity нам не понадобится, поэтому не забудьте убрать галку Create Activity в визарде создания нового проекта
Создадим проект без Activity:
Project name: P1171_SimpleWidget Build Target: Android 2. 3 Application name: SimpleWidget Package name: ru. startandroid. develop. p1171simplewidget
Добавим строки в strings. xml:
Создаем layout-файл widget. xml:
RelativeLayout, а внутри зеленый TextView с текстом по центру. виджет просто будет показывать текст на зеленом фоне.
Создаем файл метаданных res/xml/widget_metadata. xml:
В атрибуте initialLayout указываем layout-файл для виджета.
Атрибуты minHeight и minWidth содержат минимальные размеры виджета по высоте и ширине.
Есть определенный алгоритм расчета этих цифр. Как вы наверняка замечали, при размещении виджета, экран делится на ячейки, и виджет занимает одну или несколько из этих ячеек по ширине и высоте. Чтобы конвертнуть ячейки в dp, используется формула 70 * n – 30, где n – это количество ячеек. если мы, например, хотим, чтобы наш виджет занимал 2 ячейки в ширину и 1 в высоту, мы высчитываем ширину = 70 * 2 – 30 = 110 и высоту = 70 * 1 – 30 = 40. Эти полученные значения и будем использовать в атрибутах minWidth и minHeight.
Атрибут updatePeriodMillis содержит количество миллисекунд. Это интервал обновления виджета. Насколько я понял хелп, указать мы тут можем хоть 5 секунд, но чаще, чем раз в 30 минут (1 800 000) виджет обновляться все равно не будет — это системное ограничение. Давайте пока что поставим интервал 40 минут (2 400 000). В следующих уроках мы разберемся, как самим обновлять виджет с необходимым интервалом.
Осталось создать класс, наследующий AppWidgetProvider.
onEnabled вызывается системой при создании первого экземпляра виджета (мы ведь можем добавить в Home несколько экземпляров одного и того же виджета).
onUpdate вызывается при обновлении виджета. На вход, кроме контекста, метод получает объект AppWidgetManager и список ID экземпляров виджетов, которые обновляются. Именно этот метод обычно содержит код, который обновляет содержимое виджета. Для этого нам нужен будет AppWidgetManager, который мы получаем на вход.
onDeleted вызывается при удалении каждого экземпляра виджета. На вход, кроме контекста, метод получает список ID экземпляров виджетов, которые удаляются.
onDisabled вызывается при удалении последнего экземпляра виджета.
Во всех методах выводим в лог одноименный текст и список ID для onUpdate и onDeleted.
Повторюсь — в onUpdate мы, по идее, должны накодить какое-то обновление виджета. если наш виджет отображает, например, текущее время, то при очередном обновлении (вызове onUpdate) надо получать текущее время и передавать эту инфу в виджет для отображения. В этом уроке мы пока не будем с этим возиться.
Осталось немного подрисовать манифест. Добавьте туда ваш класс как Receiver
— укажите для него свои label и icon. Этот текст и эту иконку вы увидите в списке выбираемых виджетов, когда будете добавлять виджет на экран.
— настройте для него фильтр с action = android. appwidget. action. APPWIDGET_UPDATE
— добавьте метаданные с именем android. appwidget. provider и указанием файла метаданных xml/widget_metadata. xml в качестве ресурса
После этого секция receiver в манифесте должна получиться примерно такая:
Виджет готов. Все сохраняем и запускаем. Никаких Activity, разумеется, не всплывет. В консоли должен появиться текст:
\P1171_SimpleWidget\bin\P1171_SimpleWidget. apk installed on device Done!
Открываем диалог создания виджета и видим в списке наш виджет с иконкой и текстом, которые мы указывали в манифесте для receiver.
Выбираем его и добавляем на экран.
Виджет появился, смотрим логи.
Сработал onEnabled, т. мы добавили первый экземпляр виджета. И сразу после добавления, сработал метод onUpdate для этого экземпляра. Видим, что ему назначен ID = 8. (У вас, скорее всего, будет другой ID). система добавила экземпляр виджета на экран и вызвала метод обновления с указанием ID экземпляра.
Добавим еще один экземпляр
Теперь давайте удалим с экрана два этих экземпляра виджета. Сначала второй. В логах увидим:
Сработал onDeleted и получил на вход ID удаляемого экземпляра виджета.
Удаляем первый экземпляр. В логах:
Снова сработал onDeleted — нас оповестили, что экземпляр виджета с ID=8 был удален. И сработал onDisabled, т. был удален последний экземпляр виджета, больше работающих экземпляров не осталось.
Наш виджет обновляется (получает вызов метода onUpdate) раз в 40 минут. Если кому не лень, добавьте снова пару виджетов на экран и подождите. Когда они снова обновятся, в логах это отразится.
Так создается и работает простой виджет. Пока остановимся на этом, чтобы уложились в голове схема создания виджета и его lifecycle-методы. Из интересного хотелось бы еще отметить пару вещей.
BroadcastReceiver
Класс AppWidgetProvider является расширением класса BroadcastReceiver (в манифесте мы его и прописали как Receiver). Он просто получает от системы сообщение в onReceive, определяет по значениям из Intent, какое именно событие произошло (добавление, удаление или обновление виджета), и вызывает соответствующий метод (onEnabled, onUpdate и пр.
В манифесте мы для нашего Receiver-класса настроили фильтр с action, который ловит события update. Каким же образом этот Receiver ловит остальные события (например, удаление)? Хелп пишет об этом так:
The element must include an element with the android:name attribute. This attribute specifies that the AppWidgetProvider accepts the ACTION_APPWIDGET_UPDATE broadcast. This is the only broadcast that you must explicitly declare. The AppWidgetManager automatically sends all other App Widget broadcasts to the AppWidgetProvider as necessary.
ACTION_APPWIDGET_UPDATE – это единственный action, который необходимо прописать явно. Остальные события AppWidgetManager каким-то образом сам доставит до нашего AppWidgetProvider-наследника.
Отступы
Если мы расположим рядом несколько экземпляров виджета, увидим следующую картину
Не очень приятное зрелище. Надо бы сделать отступ.
Добавим android:padding=»8dp» к RelativeLayout в нашем layout-файле
Сохраняем, запускаем.
Виджеты на экране поменялись автоматически и теперь выглядят более пристойно.
Кстати, для них сработал onUpdate, смотрите логи. В метод был передан массив ID всех работающих экземпляров виджета.
В Android 4. 0 (API Level 14) и выше этот недостаток с отступами был устранен, и вручную делать отступы больше не надо. Давайте проверим. Уберите ранее добавленный в RelativeLayout отступ. И укажите в манифесте android:targetSdkVersion версию 14 (или выше), чтобы система знала, что можно использовать стандартные возможности, а не режим совместимости.
Все сохраняем, запускаем наш виджет на эмуляторе с 4. Добавим три экземпляра.
Система сама делает отступы между виджетами.
Получается для версий, ниже 4 надо делать отступ в layout, а для старших версий не надо. Хелп дает подсказку, как сделать так, чтобы ваш виджет корректно работал на всех версиях. Для этого используются квалификаторы версий.
В layout для RelativeLayuot указываете:
И создаете два файла.
res/values/dimens. xml с записью:
и res/values-v14/dimens. xml с записью:
В манифесте android:targetSdkVersion должен быть 14 или выше.
Таким образом, на старых версиях (без системного отступа) отступ будет 8dp, а на новых – 0dp и останется только системный отступ.
На следующем уроке:
— настраиваем виджет при размещении- работаем с view-компонентами виджета при обновлении
В этом уроке:
— настраиваем виджет при размещении — работаем с view-компонентами виджета при обновлении
Для этих целей и существует конфигурационный экран (конфигурационное Activity). Он предложит пользователю поля для ввода и сохранит куда-либо (БД, Preferences, …) введенные данные, а при обновлении виджета эти данные будут считаны и использованы для отображения актуальной информации.
Либо, например, мы хотим настроить внешний вид виджета при размещении. Давайте реализуем такой функционал в этом уроке. Возьмем проект из прошлого урока и добавим к нему возможность конфигурирования. Будем настраивать цвет фона виджета и отображаемый текст.
Создадим приложение с виджетом аналогично предыдущему уроку.
Параметры нового проекта:
Project name: P1181_CustomWidget Build Target: Android 2. 3 Application name: CustomWidget Package name: ru. startandroid. develop. p1181customwidget
Я не буду дублировать все исходники прошлого урока, чтобы не загромождать этот. Напомню только, что надо будет создать layout-файл, файл метаданных, класс-наследник AppWidgetProvider и настроить манифест. Ну и про файл strings. xml не забывайте.
В strings. xml давайте поменяем параметр widget_name (чтобы виджеты прошлого и этого урока отличались друг от друга в списке виджетов):
и добавим строки:
Теперь будем добавлять возможность конфигурирования. Для этого нам надо создать Activity. Это Activity будет запускаться системой при добавлении нового экземпляра виджета и на вход получать ID этого экземпляра.
Конфигурационное Activity — совершенно обычное Activity, состоящее из layout-файла и класса.
Начнем с создания layout-файла config. xml:
Три радиокнопки для задания цвета и текстовое поле для текста. Этими настройками будем задавать фон и текст TextView виджета. По нажатию кнопки Ok будем закрывать экран настроек.
Создаем класс для Activity
Для начала озвучу пару фактов из жизни конфигурационного Activity.
1) При вызове оно получает Intent, в котором содержится ID создаваемого экземпляра виджета.
2) При закрытии оно должно формировать результат методом setResult. И в этом ответе передавать Intent с ID экземпляра. Этот механизм мы рассматривали в Уроке 29. Хелп рекомендует при создании Activity сразу формировать отрицательный результат. В этом случае, если пользователь нажмет Назад, система получит ответ, что виджет создавать не надо.
В onCreate мы из Intent (параметр EXTRA_APPWIDGET_ID) извлекаем ID экземпляра виджета, который будет конфигурироваться этим экраном. Если видим, что получен некорректный ID, выходим. Если все ок, то формируем Intent с ID для метода setResult и говорим, что результат отрицательный. Теперь, если пользователь передумает создавать виджет и нажмет в конфигурационном экране Назад, то система будет знать, что виджет создавать не надо.
В onClick мы читаем выбранный цвет и введенный в поле текст и пишем эти значения в Preferences. В имени записываемого параметра мы используем ID, чтобы можно было отличать параметры разных экземпляров друг от друга. Далее мы говорим системе, что результат работы положительный, и виджет можно создавать. Закрываем Activity.
Добавим Activity в манифест и настроим ему фильтр с action = android. appwidget. action. APPWIDGET_CONFIGURE.
Также необходимо добавить в файл метаданных (xml/widget_metadata. xml) параметр android:configure и указать в нем полный путь к классу Activity
android:configure=»ru. startandroid. develop. p1181customwidget. ConfigActivity»
Теперь система будет знать, что ей надо вызвать Activity (указанное в метаданных) при добавлении очередного экземпляра виджета. А т. при вызове она использует action android. appwidget. action. APPWIDGET_CONFIGURE, поэтому мы прописали его в манифесте.
Итак, мы в Activity получили ID экземпляра виджета и записали настройки с экрана в Preferences. Теперь нам надо при обновлении виджета эти настройки читать и применять к внешнему виду.
Меняем MyWidget. java:
В onUpdate мы перебираем все ID экземпляров, которые необходимо обновить и для каждого из них вызываем наш метод обновления, который рассмотрим чуть ниже.
onDeleted у нас вызывается, когда виджет удаляется с экрана. Если виджет удален, то логично будет удалить и все настройки для него из Preferences. Это и делаем.
Метод updateWidget обновляет конкретный экземпляр виджета, получая на вход его ID. Здесь мы читаем настройки (и сразу выходим, если нет настройки WIDGET_TEXT), которые записало нам конфигурационное Activity для этого экземпляра виджета. Нам надо применить эти параметры к view-компонентам нашего виджета. Но (насколько я это понимаю) за отображение виджета отвечает один процесс (какой-нибудь Home), а наш код из MyWidget будет выполняться в другом, своем собственном процессе. Поэтому у нас нет прямого доступа к view-компонентам виджета. И мы не можем вызывать метода типа setText и setBackgroundColor напрямую. Поэтому используется класс RemoteViews, он предназначен для межпроцессной работы с view.
Создаем RemoteViews. На вход он принимает имя пакета нашего приложения и ID layout-файла виджета. Теперь RemoteViews знает view-структуру нашего виджета. Осталось понять, как настраивать конкретные view. Это немного отличается от привычной нам работы с view-компонентами.
RemoteViews имеет несколько методов работы с view, где мы указываем ID нужного нам view-компонента и значение, которое хотим передать. Из названия этих методов понятно, что они делают. В нашем случае мы используем метод setTextViewText. По названию понятно, что этот метод вставит текст в TextView. Мы вызываем его и передаем ID нашего TextView (из layout-файла виджета) и текст, который хотим в него поместить. Система потом найдет в виджете view с указанным ID (R. tv), и вызовет для него метод setText с указанным текстом (widgetText).
Но таких явных методов немного. Они созданы просто для удобства и являются оболочками общих методов, которые позволяют вызвать любой метод view.
В названии общего метода содержится тип данных, которые вы хотите передать. А на вход методу кроме ID view и значения, необходимо будет указать имя метода. В нашем случае мы хотим присвоить цвет фону TextView. Цвет у нас типа int, поэтому мы вызываем метод setInt и передаем ему ID view, имя метода (который бы вызывали в случае доступа к view – setBackgroundColor) и значение цвета. Система найдет в виджете view с указанным ID (R. tv) и вызовет для него указанный метод (setBackgroundColor) с указанным значением (widgetColor).
RemoteViews сформирован. Используем AppWidgetManager, чтобы применить к виджету наши сформированные view-настройки. Для этого используется метод updateAppWidget, который на вход берет ID экземпляра виджета и объект RemoteViews. Система найдет указанный экземпляр виджета и настроит его так, как мы только что накодили.
В итоге у нас получилось, что Activity настройки пишет, а MyWidget читает и применяет при обновлении виджета.
Все сохраняем и запускаем (инсталлим) приложение.
В списке виджетов выбираем наш My second widget (не перепутайте с виджетом с прошлого урока)
и видим наше Activity. Давайте цвет сделаем синий, и введем какой-нибудь текст.
Жмем Ok. Виджет не изменился.
Давайте смотреть логи, чтобы понять причину.
onEnabled – размещение первого экземпляра.
Затем вызвался onUpdate и попытался обновить (updateWidget) экземпляр с ID 28 (у вас может быть другой). Но т. никаких Preferences для него мы еще на тот момент не настроили, то обновление не произошло. Напомню, что такое поведение мы накодили в методе updateWidget. Если он не находит текст в WIDGET_TEXT, то он завершается.
А вот уже после этого запустилось Activity (onCreate). В нем мы сделали необходимые настройки и нажали Ок, отработал finish.
что получилось? onUpdate вызвался ПЕРЕД конфигурированием (когда настройки виджета еще не были сохранены), но не вызвался ПОСЛЕ завершения работы конфигурационного экрана (когда настройки сохранены и их можно использовать для обновления). настройки мы в Activity сохранили, но виджет их пока не прочел. Теперь эти настройки применятся только при следующем обновлении, т. через 40 минут, как мы указывали в файле метаданных.
Зачем делается onUpdate перед вызовом Activity – непонятно. К тому же хелп английским по белому цинично и жирным шрифтом заявляет, что не может такого быть:
«… when you implement the Activity: … The onUpdate() method will not be called when the App Widget is created
В общем, это известный баг, смиримся с ним. К тому же у нас в методе updateWidget стоит защита от этого – мы проверяем, что в Preferences записан WIDGET_TEXT. Если его нет, то мы ничего не делаем.
Нам осталось придумать, как сделать так, чтобы наш виджет сразу подхватывал изменения после закрытия конфигурационного экрана, а не ожидал следующего обновления по расписанию. Для этого мы можем обновлять виджет вручную при успешном закрытии Activity, ведь мы знаем ID экземпляра. Будем вызывать метод updateWidget из класса MyWidget. Именно для этого мы и сделали его static.
В ConfigActivity в метод onClick надо добавить пару строк:
AppWidgetManager appWidgetManager = AppWidgetManager. getInstance(this);
MyWidget. updateWidget(this, appWidgetManager, sp, widgetID);
В результате виджет обновится при закрытии Activity.
Все сохраним, запустим. При переустановке приложения сработал onUpdate (см. логи) для первого экземпляра, он прочел свои настройки и стал таким, каким мы его настраивали.
Добавим второй экземпляр. Настроим его
Жмем Ок. Видим, что обновление сработало.
Видим, что сработал updateWidget примерно в одно время с закрытием Activity (очередность зависит от того, куда вы поместили две строки обновления виджета).
Напоследок попробуйте добавить еще один экземпляр виджета, но нажмите Назад в конфигурационном Activity. Виджет не должен появиться. А логи покажут следующее:
виджет таки создался, но после закрытия Activity был удален системой, т. мы передали RESULT_CANCELED в setResult.
В общем не очень тривиально и схема может поначалу показаться запутанной. Поэкспериментируйте, подобавляйте свои настройки виджета и все должно стать понятнее.
Чтобы двигаться дальше по теме виджетов, нужны будут некоторые доп. знания. Поэтому следующий урок будет небольшим отступлением от темы. Но это отступление само по себе будет интересным и полезным.
На следующем уроке:
— подробно разбираемся с PendingIntent- используем AlarmManager
Операционная система Аndroid имеет множество функций и возможностей. Одной из них являются виджеты — окошки, которые позволяют использовать часть функционала приложения, без его запуска.
В основном виджеты идут в комплекте с приложением. Однако есть программы, которые лишены данной опции. А значит, нужно брать дело в свои руки!
Программа выступает в качестве платформы для запуска сторонних виджетов от разных разработчиков. Помимо этого вы можете изменить внешний вид виджетов, задать им дополнительный функционал.
Сперва можно подумать, что “KWGT Kustom Widget Maker” сложное приложение, которое напоминает программы для разработки софта. И да и нет: интерфейс похож, но сложного тут ничего нет.
Чтобы начать редактировать виджеты:
- заходим на рабочий стол и вызываем меню редактирования рабочих столов путем удержания кнопки меню или долгим тапом по экрану;
- находим рамку виджетов “KWGT” и выставляем необходимый размер, например — 4х2. После этого на рабочем столе образуется окошко;
- Кликаем по окошку, тем самым открывая меню выбора виджетов;
- Выбираем понравившийся макет и нажимаем на него (Дополнительные макеты можно скачивать с Play Маркет. Для этого в строке поиска приложений указываем “KWGT виджеты”);
- Элементы, отвечающие за настройку геометрических параметров виджета;
- Фон, отвечающий за фоновую картинку на виджета;
- Касание, задающий действия которые будут выполняться при активации виджета;
- Слой, отвечающий за масштаб виджета;
В итоге мы получим уникальный виджет, с необходимым функционалом.
Смартфоны в каталоге Xistore
Будьте уникальными вместе с Xistore!