Android add widget from app

Программное добавление и удаление виджета

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

Добавляем виджет

Допустим у нас есть простейшая разметка.

Пустая компоновка LinearLayout с идентификатором mainlayout не содержит вложенных виджетов. Через метод addView(view) класса LinearLayout мы можем добавить нужный нам элемент.

Удаляем виджет

Существует и обратный метод для удаления вида — removeView(), а также метод removeAllViews(), удаляющий все дочерние элементы родителя. Рассмотрим следующий пример. Создадим разметку, где компонент LinearLayout с идентификатором master будет родителем для будущих элементов, которые мы будем добавлять или удалять:

activity_main.xml

Создадим пару дополнительных макетов, которые будет дочерними элементами для FrameLayout. Мы будем управлять ими программно.

layer1.xml

layer2.xml

Напишем код, который будет добавлять или удалять компоновки через флажки.

Обратите внимание, что добавление идёт в том порядке, как мы отмечаем флажки. Если мы отметим флажком второй CheckBox, то сначала на экране появится блок с компоновкой layer2.xml, а уже ниже компоновка layer1.xml. На скриншоте представлен этот вариант.

Получить доступ к дочерним элементам можно через методы getChildCount() и getChildAt().

Источник

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

— создаем простой виджет
— разбираемся в его 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) надо получать текущее время и передавать эту инфу в виджет для отображения. В этом уроке мы пока не будем с этим возиться.

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

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

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

Источник

Code a Widget for Your Android App: Add a Configuration Activity

Application widgets provide your users with easy access to your application’s most frequently used features, while giving your app a presence on the user’s homescreen. By adding a widget to your project, you can provide a better user experience, while encouraging users to remain engaged with your application, as every single time they glance at their homescreen they’ll see your widget, displaying some of your app’s most useful and interesting content.

Читайте также:  Хонор уходит с андроида

In this three-part series, we’re building an application widget that has all the functionality you’ll find in pretty much every Android application widget.

We’re picking up right where we left off, so if you don’t have a copy of the widget we created in part one, you can download it from GitHub.

Enhancing Your Widget With a Configuration Activity

Although our widget functions out of the box, some widgets require initial setup—for example, a widget that displays the user’s messages will require their email address and password. You might also want to give your users the ability to customize the widget, such as changing its colour or even modifying its functionality, like how often the widget updates.

If your widget is customisable or requires some setup, then you should include a configuration Activity, which will launch automatically as soon as the user places the widget on their homescreen.

Configuration Activities may also come in handy if you have lots of ideas about the information and features that you want to include in your widget. Rather than cramming all this content into a complex and potentially confusing layout, you could provide a configuration Activity where users pick and choose the content that matters to them.

If you do include a configuration Activity, then don’t get carried away, as there’s a point where choice becomes too much choice. Setting up a widget shouldn’t feel difficult, so it’s recommended that you limit your widget to two or three configuration options. If you’re struggling not to exceed this limit, then consider whether all this choice is really necessary, or whether it’s just adding unnecessary complexity to the setup process.

To create a configuration Activity, you’ll need to follow the following steps.

1. Create the Activity Layout

This is exactly the same as building the layout for a regular Activity, so create a new layout resource file and add all your UI elements as normal.

A configuration Activity is where the user performs initial setup, so once they’ve completed this Activity they’re unlikely to need it again. Since a widget’s layout is already smaller than a regular Activity layout, you shouldn’t waste valuable space by giving users a way to relaunch the configuration Activity directly from the widget layout.

Here I’m creating a simple configuration_activity.xml layout resource file containing a button that, when tapped, will create the application widget.

2. Create Your Configuration Class

Your configuration Activity must include the App Widget ID passed by the Intent that launched the configuration Activity.

If you do include a configuration Activity, then note that the onUpdate() method won’t be called when the user creates a widget instance, as the ACTION_APPWIDGET_UPDATE broadcast isn’t sent when a configuration Activity is launched. It’s the configuration Activity’s responsibility to request this first update directly from the AppWidgetManager . The onUpdate() method will be called as normal for all subsequent updates.

Create a new Java class named configActivity and add the following:

3. Declare the Configuration Activity in Your Project’s Manifest

When you declare your configuration Activity in the Manifest, you need to specify that it accepts the ACTION_APPWIDGET_CONFIGURE action:

4. Declare the Configuration Activity in Your AppWidgetProviderInfo File

Since the configuration Activity is referenced outside of your package scope, you need to declare it using the fully qualified namespace:

Now, whenever you create a new instance of this widget, the configuration Activity will launch, and you’ll need to complete all the options in this Activity before your widget is created. In this instance, that simply means giving the Create Widget button a tap.

Remember, you can download the finished widget, complete with configuration Activity, from GitHub.

Application Widget Best Practices

Throughout this series, we’ve been building an application widget that demonstrates all the core features you’ll find in pretty much every Android application widget. By this point, you know how to create a widget that retrieves and displays data, reacts to onClick events, updates with new information based on a schedule and in response to user interaction, and has a custom preview image.

These may be the fundamental building blocks of Android application widgets, but creating something that just works is never enough—you also need to ensure your widget is providing a good user experience, by following best practices.

Perform Time-Consuming Operations Off the Main Thread

Widgets have the same runtime restrictions as normal broadcast receivers, so they have the potential to block Android’s all-important main UI thread.

Android has a single thread where all your application code runs by default, and since Android can only process one task at a time, it’s easy to block this important thread. Perform any kind of long-running or intensive operation on the main thread, and your app’s user interface will be unresponsive until the operation completes. In the worst-case scenario, this can result in Android’s Application Not Responding (ANR) error and the application crashes.

Читайте также:  Тв приставка android зависает

If your widget does need to perform any time-consuming or labour-intensive tasks, then you should use a service rather than the main thread. You can create the service as normal and then start it in your AppWidgetProvider . For example, here we’re using a service to update our widget:

Create a Flexible Layout

Creating a layout that can adapt to a range of screen configurations is one of the most important rules of developing for Android, and it’s a rule that also extends to widgets. Just like regular Activities, your widget’s layout needs to be flexible enough to display and function correctly regardless of the screen configuration, but there are some additional reasons why widget layouts need to be as flexible as possible.

Firstly, if your widget is resizeable, then the user can manually increase and decrease the size of your widget, which is something you don’t have to worry about with traditional Activities.

Secondly, there’s no guarantee that your widget’s minWidth and minHeight measurements will perfectly align with a particular device’s homescreen grid. When a widget isn’t a perfect fit, the Android operating system will stretch that widget horizontally and/or vertically to occupy the minimum number of cells required to satisfy the widget’s minWidth and minHeight constraints. While this shouldn’t be a significant increase, it’s still worth noting that right from the start your widget may be occupying more space than you’d anticipated.

Creating a flexible widget layout follows the same best practices as designing a flexible Activity layout. There’s already plenty of information available on this topic, but here are some of the most important points to bear in mind when creating your widget layouts:

  • Avoid absolute units of measure, such as defining a button in pixels. Due to varying screen densities, 50 pixels doesn’t translate to the same physical size on every device. So a 50px button is going to appear larger on low-density screens and smaller on high-density screens. You should always specify your layout’s dimensions in density-independent units (dpi) and use flexible elements such as wrap_content and match_parent .
  • Provide alternate resources that are optimised for different screen densities. You can also provide layouts that are optimised for different screen sizes, using configuration qualifiers such as smallestWidth ( sw dp ). Don’t forget to provide a default version of every resource, so your app has something to fall back on if it ever encounters a screen with characteristics that it doesn’t have a specific resource for. You should design these default resources for normal, medium-density screens.
  • Test your widget across as many screens as possible, using the Android emulator. When creating an AVD, you can specify an exact screen size and resolution, using the Screen controls that appear in the Configure this hardware profile menu. Don’t forget to test how your widget handles being resized across all these different screens!

Don’t Wake the Device

Updating a widget requires battery power, and the Android operating system has no qualms about waking a sleeping device in order to perform an update, which amplifies the impact your widget has on the device’s battery.

If the user realises that your widget is draining their battery, then at the very least they’re going to delete that widget from their homescreen. In the worst-case scenario, they may even delete your app entirely.

You should always aim to update your widget as infrequently as possible while still displaying timely and relevant information. However, some widgets may have a legitimate reason for requiring frequent updates—for example, if the widget includes highly time-sensitive content.

In this scenario, you can reduce the impact these frequent updates have on battery life, by performing updates based on an alarm that won’t wake a sleeping device. If this alarm goes off while the device is asleep, then the update won’t be delivered until the next time the device wakes up.

To update based on an alarm, you need to use the AlarmManager to set an alarm with an Intent that your AppWidgetProvider will receive, and then set the alarm type to either ELAPSED_REALTIME or RTC , as these alarm types won’t wake a sleeping device. For example:

If you do use an alarm, then make sure you open your project’s AppWidgetProviderInfo file (res/xml/new_app_widget_info.xml) and set the updatePeriodMillis to zero (» 0 «). If you forget this step, the updatePeriodMillis value will override the AlarmManager and your widget will still wake the device every single time it requires an update.

Conclusion

In this three-part series, we’ve created an Android application widget that demonstrates how to implement all the most common features found in app widgets.

If you’ve been following along since the beginning, then by this point you’ll have built a widget that updates automatically and in response to user input, and that’s capable of reacting to onClick events. Finally, we looked at some best practices for ensuring your widget provides a good user experience, and discussed how to enhance your widget with a configuration Activity.

Thanks for reading, and while you’re here, check out some of our other great posts on Android app development!

Источник

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