Widget in android code

Полный список

— создаем простой виджет
— разбираемся в его Lifecycle

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

Для понимания темы виджетов желательно знать, что такое BroadcastReceiver. Я о нем упоминал в Уроках 96 и 100. Это просто слушатель, который регистрируется в системе, ловит с помощью настроенного Intent Filter сообщения (Intent) и выполняет какой-либо код. Почему-то не сделал я отдельного урока по нему. Но, думаю, еще сделаю. Есть там свои интересные особенности, о которых можно поговорить.

Думаю, нет особой необходимости подробно объяснять, что такое виджеты. Все их видели на своих девайсах. Это что-то типа мини-приложений расположенных на рабочем столе (Home). Они позволяют просмотреть какую-либо информацию из основных приложений, либо повлиять на поведение этих приложений. В качестве примера можно привести – прогноз погоды, текущее время, баланс какого-либо счета, список сообщений в различных мессенджерах, управление состоянием WiFi/3G/GPS/Bluetooth, яркость экрана и т.д. и т.п. В этом уроке сделаем простейший виджет, который отобразит статичный текст.

Чтобы создать простейший виджет нам понадобятся три детали:

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

2) XML-файл c метаданными

В нем задаются различные характеристики виджета. Мы пока что укажем следующие параметры:

— layout-файл (из п.1.), чтобы виджет знал, как он будет выглядеть
— размер виджета, чтобы виджет знал, сколько места он должен занять на экране
— интервал обновления, чтобы система знала, как часто ей надо будет обновлять виджет

3) Класс, наследующий AppWidgetProvider. В этом классе нам надо будет реализовать Lifecycle методы виджета.

Давайте создадим три этих детали. Activity нам не понадобится, поэтому не забудьте убрать галку Create Activity в визарде создания нового проекта

Создадим проект без Activity:

Project name: P1171_SimpleWidget
Build Target: Android 2.3.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) надо получать текущее время и передавать эту инфу в виджет для отображения. В этом уроке мы пока не будем с этим возиться.

Читайте также:  Не приходят уведомления instagram android

Осталось немного подрисовать манифест. Добавьте туда ваш класс как 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 [8]

Сработал onEnabled, т.к. мы добавили первый экземпляр виджета. И сразу после добавления, сработал метод onUpdate для этого экземпляра. Видим, что ему назначен (У вас, скорее всего, будет другой ID). Т.е. система добавила экземпляр виджета на экран и вызвала метод обновления с указанием ID экземпляра.

Добавим еще один экземпляр

onEnabled не сработал, т.к. добавляемый экземпляр виджета уже не первый. onUpdate же снова отработал для нового добавленного экземпляра и получил на вход >

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

Сработал onDeleted и получил на вход ID удаляемого экземпляра виджета.

Удаляем первый экземпляр. В логах:

onDeleted [8]
onDisabled

Снова сработал onDeleted — нас оповестили, что экземпляр виджета с был удален. И сработал onDisabled, т.е. был удален последний экземпляр виджета, больше работающих экземпляров не осталось.

Наш виджет обновляется (получает вызов метода onUpdate) раз в 40 минут. Если кому не лень, добавьте снова пару виджетов на экран и подождите. Когда они снова обновятся, в логах это отразится.

Так создается и работает простой виджет. Пока остановимся на этом, чтобы уложились в голове схема создания виджета и его lifecycle-методы. Из интересного хотелось бы еще отметить пару вещей.

BroadcastReceiver

Класс AppWidgetProvider является расширением класса BroadcastReceiver (в манифесте мы его и прописали как Receiver). Он просто получает от системы сообщение в onReceive, определяет по значениям из Intent, какое именно событие произошло (добавление, удаление или обновление виджета), и вызывает соответствующий метод (onEnabled, onUpdate и пр.).

В манифесте мы для нашего Receiver-класса настроили фильтр с action, который ловит события update. Каким же образом этот Receiver ловит остальные события (например, удаление)? Хелп пишет об этом так:

Т.е. ACTION_APPWIDGET_UPDATE – это единственный action, который необходимо прописать явно. Остальные события AppWidgetManager каким-то образом сам доставит до нашего AppWidgetProvider-наследника.

Отступы

Если мы расположим рядом несколько экземпляров виджета, увидим следующую картину

Не очень приятное зрелище. Надо бы сделать отступ.

Добавим android:padding=»8dp» к RelativeLayout в нашем layout-файле

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

Кстати, для них сработал onUpdate, смотрите логи. В метод был передан массив ID всех работающих экземпляров виджета.

В Android 4.0 (API Level 14) и выше этот недостаток с отступами был устранен, и вручную делать отступы больше не надо. Давайте проверим. Уберите ранее добавленный в RelativeLayout отступ. И укажите в манифесте android:targetSdkVersion версию 14 (или выше), чтобы система знала, что можно использовать стандартные возможности, а не режим совместимости.

Все сохраняем, запускаем наш виджет на эмуляторе с 4.1. Добавим три экземпляра.

Система сама делает отступы между виджетами.

Получается для версий, ниже 4 надо делать отступ в layout, а для старших версий не надо. Хелп дает подсказку, как сделать так, чтобы ваш виджет корректно работал на всех версиях. Для этого используются квалификаторы версий.

В layout для RelativeLayuot указываете:

И создаете два файла.

res/values/dimens.xml с записью:

и res/values-v14/dimens.xml с записью:

В манифесте android:targetSdkVersion должен быть 14 или выше.

Таким образом, на старых версиях (без системного отступа) отступ будет 8dp, а на новых – 0dp и останется только системный отступ.

На следующем уроке:

— настраиваем виджет при размещении
— работаем с view-компонентами виджета при обновлении

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

Читайте также:  Free wifi для андроида

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Android Widgets

This article covers a complete tutorial about how to add Android Widgets into your application. Understanding the logic and depth diving into Android Widgets with 4 sample projects. You will learn how to code your widget from scratch to advanced. Afterward, at the end article we are going to see what the common mistakes.

Before getting started, what is an Android Widget?

Widgets are an essential aspect of home screen customization. You can imagine them as “at-a-glance” views of an app’s most important data and functionality that is accessible right from the user’s home screen.

You can also read this article in Chinese(中文) .

So what we are going to achieved?

  • Widget Creation Steps with a basic example (‘Simple Widget’ — Opening a website on widget clicked)
  • Broadcast Widget Example (‘Broadcast Widget’ — Counting clicks on widget)
  • Configurable Widget Example (‘Configurable Widget’ — Getting data before creation and using the data when it’s clicked)
  • Updating Widget by a Service (‘Updating Widget’ — Updating every minute brings random numbers on widget)

Widget Creation Steps :

  1. create layout for the widget.
  2. create XML for defining the widget properties.
  3. create class for the widget actions.
  4. add all these to AndroidManifest.xml file.

With that knowledge, our first example is a widget that will be opening a website on widget clicks.

Step 1: Creating a very simple layout for a widget.

This layout is displayed as a widget on user’s home-screen.

Step 2: create an XML that defines widget properties.

In res folder create a new XML folder.

Quick look for these properties

  • initialLayout: reference for the widget layout(that we already created).
  • minHeight and minWidth: Every 60dp means 1 cell in android home-screen. For this example, the widget takes min 1×1 cell(s).
  • previewImage: The image that will be shown on android’s widget select screen. We can not draw a layout for preview. We have to set an image.
  • resizeMode: The configuration for resizing the widget.
  • updatePeriodMillis: The widget’s update method is called when the specified time is reached in a millisecond.
  • widgetCategory: home_screen or keyguard.

Android Studio has an amazing interface for creating widgets.

Step 3: Create a class for the widget lifecycle.

AppWidgetProvider extends BroadcastReceiver. SimpleAppWidget is indirectly a child of BroadcastReceiver. So our widget class is a receiver class.

Step 4: Adding into AndroidManifest as a receiver.

Yay! You have implemented a widget for your app 🙂

Let’s take it to the next level.

RemoteView

Let’s continue with learning RemoteView.

A class that describes a view hierarchy that can be displayed in another process. The hierarchy is inflated from a layout resource file, and this class provides some basic operations for modifying the content of the inflated hierarchy.

RemoteView supports only these layouts:

RemoteView supports only these views:

If you use another view, RemoteView has no operation for the view.

Let’s add some codes to our widget class.

As we can see there is an override method onUpdate which is called every updatePeriodMillis has reached.

This example is basic and fast dive for the widget.

Understanding Override Methods

Before going to the next example, we need to understand widget classes that override the following methods.

  • onUpdate(): This is called to update the App Widget at intervals defined by the updatePeriodMillis attribute.
  • onAppWidgetOptionsChanged() : This is called when the widget is first placed and also whenever the widget is resized.
  • onDeleted(Context, int[]): This is called every time an App Widget is deleted from the App Widget host.
  • onEnabled(Context): This is called when an instance the App Widget is created for the first time.
  • onDisabled(Context): This is called when the last instance of your App Widget is deleted from the App Widget host.
  • onReceive(Context, Intent): This is called for every broadcast and before each of the above callback methods.

Confused? No problem. Below video shows you when these methods will calls.

Let’s see these fellas in action.

This knowledge gives you full control of on your widget.

Broadcast Widget

We have learned all steps of widget creation. We will not be repeating these steps.

Читайте также:  Своя bootanimation для android

Every click on the widget will send a broadcast and onReceive increases the counter.

In previous example we used .getActivity() , this time we are going to use .getBroadcast() .

BroadcastWidget is our widget class. Let’s see what is inside?

Using static fields with widgets is not recommended. But I am trying to make the example as simple as possible. You can use SharedPreferences instead of static fields.

Here is the result.

You can send whatever you want with broadcast and you can catch the broadcast wherever you want.

Configurable Widgets

Some widgets can be configurable at creation. In this example, we are getting a link from the user and whenever it’s clicked we open this link on the browser.

Create ConfigureActivity for user configuration.

The Basic layout for activity.

In activity’s onCreate method, the first thing we do is setting setResult(RESULT_CANCELED) . Why? Android triggers the configure activity that belongs to your widget and awaits result data. If the user did not configure as we expected, let’s say she pressed back button without entering a data, we don’t need to create a widget.

The last thing we do is modify the XML of the widget. With that modification, the Android Operating System will know this widget has configuration activity. So before creating the widget, it will trigger the activity.

As you notice we didn’t talk about widget class yet. We do not need to add any code for widget class because all actions done by ConfigurableWidgetConfigureActivity . But we have to create anyway.

Here is the result.

Updated By a Service Widget Example

This project will generate random numbers in every minute and displays it on a widget. First of all, we need a service to generate random numbers.

And we have to add this to AndroidManifest.

Service does not start by itself. We need to start the service(in every minute for this example).

But why we do not just use ‘ updatePeriodMillis ’?

If the device is asleep when it is time for an update (as defined by updatePeriodMillis ), then the device will wake up in order to perform the update. If you don’t update more than once per hour, this probably won’t cause significant problems for the battery life. If, however, you need to update more frequently and/or you do not need to update while the device is asleep, then you can instead perform updates based on an alarm that will not wake the device. To do so, set an alarm with an Intent that your AppWidgetProvider receives, using the AlarmManager . Set the alarm type to either ELAPSED_REALTIME or RTC , which will only deliver the alarm when the device is awake. Then set updatePeriodMillis to zero ( «0» ).

In Previous examples, we used .getActivity() and .getBroadcast() methods. This time we are going to use .getService() method.

The minimum interval time is 60000 millis for AlarmManager. If you need to call your service less than 60 sec with an alarm manager, there are some workarounds like this. But I have to warn you, this action drains the battery and makes users delete your app.

Here is the result. I cropped the video to not keep you wait 2 minutes.

Common questions / mistakes about Widgets

Question:

Programmatically update widget from activity or service

Answer:

Simple, you have to reach your widgets. Afterwards send broadcast that has information about your widgets. See the code below,

Question:

Every widget instances not doing their job? or doing same job?

Answer:

PendingIntent operations(.getActivity, .getBroadcast etc.) need a parameter requestCode for identification.

You have to set the requestCode with your appW idgetId. With that way, your pending intent becomes unique. Every instance of your widget will use their PendingIntent.

More about Widgets

How the material design should be on widget?

Read further? Helpful links

If you made it at this far, personally thank you for reading. Here is the promised Github link includes whole projects in this article. And feel free for pull requests.

To help others to reach the article don’t forget to recommend 💚 . It means a lot to me.

Источник

Оцените статью