Как стать автором
Обновить

Hello World widget для Android

Время на прочтение 9 мин
Количество просмотров 70K
Как ни странно, но на русском почти нет нормальных статей по виджетам для Android. Да и на англо язычных ресурсах почти нет простых примеров для старта, все примеры почему-то сложные и тяжелые для понимания. Спешу это исправить.

Структуру проекта и как его создать описывать не буду. Предполагается, что Вы это уже умеете, а для тех, кто не умеет советую почитать вот эту статью. Для виджета нам потребуется создать 3 файла:
  1. Widget provider info
  2. Widget provider
  3. Layout

Widget provider info – Это xml файл, описывающий метаданные виджета. К ним относятся размер виджета, частота его обновления, файл шаблона и класс конфигурации. Вот так будет выглядить наш файл (res/xml/hello_widget_provider.xml):
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <appwidget-provider xmlns:android="schemas.android.com/apk/res/android"
  3.     android:minWidth="146dip"
  4.     android:minHeight="72dip"
  5.     android:updatePeriodMillis="86400000"
  6.     android:initialLayout="@layout/main" />

Размер виджета можем указывать любой, но гугл рекомендует придерживаться формуле расчёта размера виджета (number of cells * 74) – 2. updatePeriodMillis — это частота обновления виджета, но не чаще чем раз в 30 минут, в целях экономии батарейки. initialLayout – это файл шаблона виджета.

Widget provider — Это java файл, он должен наследоваться от класса AppWidgetProvider. В нашем случае он пока останется пустым (src/ru/example/android/widget/ HelloWidget.java).
  1. package ru.example.android.widget;
  2. import android.appwidget.AppWidgetProvider;
  3.  
  4. public class HelloWidget extends AppWidgetProvider {
  5. }


Layout – Это шаблон виджета или слой View, кому как нравится. Выглядеть он будет так: (res/layout /main.xml).
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="schemas.android.com/apk/res/android"
  3.         android:layout_width="fill_parent"
  4.         android:orientation="vertical"
  5.         android:background="@android:color/white"
  6.         android:layout_gravity="center"
  7.         android:layout_height="wrap_content">
  8.  
  9. <TextView android:id="@+id/widget_textview"
  10.                 android:text="Hello Widget"
  11.                 android:layout_height="wrap_content"
  12.                 android:layout_width="wrap_content"
  13.                 android:layout_gravity="center_horizontal|center"
  14.                 android:textColor="@android:color/black"/>
  15. </LinearLayout>


Всё основное мы сделали, осталось зарегистрировать виджет в AndroidManifest.xml. Для этого добавим в него следующий код в раздел <application>...</application>:
  1. <receiver android:name=".widget.HelloWidget" android:label="@string/app_name">
  2.         <intent-filter>
  3.                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  4.         </intent-filter>
  5.         <meta-data android:name="android.appwidget.provider"
  6.                 android:resource="@xml/hello_widget_provider" />
  7. </receiver>


Теперь можем компилировать проект и смотреть результат в эмуляторе!


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

В виджете невозможно повесить полноценное событие на нажатие кнопки или еще на какое-либо событие, как это Вы привыкли делать в Activity. На этом примере Вы увидите, как можно обработать событие от нажатия кнопки. Давайте для начала добавим в наш шаблон кнопку (res/layout /main.xml).
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="schemas.android.com/apk/res/android"
  3.         android:layout_width="fill_parent"
  4.         android:orientation="vertical"
  5.         android:background="@android:color/white"
  6.         android:layout_gravity="center"
  7.         android:layout_height="wrap_content">
  8.  
  9.         <TextView android:id="@+id/widget_textview"
  10.                 android:text="Hello Widget"
  11.                 android:layout_height="wrap_content"
  12.                 android:layout_width="wrap_content"
  13.                 android:layout_gravity="center_horizontal|center"
  14.                 android:textColor="@android:color/black"/>
  15.         <Button android:id="@+id/widget_button"
  16.                 android:text="click me"
  17.                 android:layout_height="wrap_content"
  18.                 android:layout_width="wrap_content"/>
  19.  
  20. </LinearLayout>


Все взаимодействия с виджетом будем делать в классе provider (src/ru/example/android/widget/ HelloWidget.java). Вот как будет выглядеть простейшая обработка события:
  1. public class HelloWidget extends AppWidgetProvider {
  2.  
  3.         public static String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget";
  4.  
  5.         @Override
  6.         public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
  7.              //Создаем новый RemoteViews
  8.              RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
  9.  
  10.              //Подготавливаем Intent для Broadcast
  11.              Intent active = new Intent(context, HelloWidget.class);
  12.              active.setAction(ACTION_WIDGET_RECEIVER);
  13.              active.putExtra("msg""Hello Habrahabr");
  14.  
  15.              //создаем наше событие
  16.              PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
  17.  
  18.              //регистрируем наше событие
  19.              remoteViews.setOnClickPendingIntent(R.id.widget_button, actionPendingIntent);
  20.  
  21.              //обновляем виджет
  22.              appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
  23.         }
  24.  
  25.         @Override
  26.         public void onReceive(Context context, Intent intent) {
  27.  
  28.              //Ловим наш Broadcast, проверяем и выводим сообщение
  29.              final String action = intent.getAction();
  30.              if (ACTION_WIDGET_RECEIVER.equals(action)) {
  31.                   String msg = "null";
  32.                   try {
  33.                         msg = intent.getStringExtra("msg");
  34.                   } catch (NullPointerException e) {
  35.                         Log.e("Error""msg = null");
  36.                   }
  37.                   Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
  38.              } 
  39.              super.onReceive(context, intent);
  40.        }
  41.  
  42. }

В классе есть 2 метода — onUpdate и onReceive. Метод onUpdate вызывается при обновлении виджета. Частоту обновления мы настроили в файле res/xml/hello_widget_provider.xml атрибутом android updatePeriodMillis=«86400000». Метод onReceive унаследован от класса BroadcastReceiver.
В виджете нельзя обновить отдельный элемент, например текст, как в Activity. Всегда обновляется иерархия Views целиком. Для обновления виджета нам потребуется класс RemoteViews, с помощью которого мы и будем менять иерархию Views целиком. К сожалению, возможности этого класса скудные. Он позволяет нам изменять текст, картинки и вешать событие на клик. Событие в виджете событием можно назвать с натяжкой, api позволяет выполнять всего 3 дейстия:
  • Бросить Broadcast
  • Запустить Activity
  • Запустить Service
В нашем случае мы будем рассылать Broadcast (Широковещательное сообщение). В результате получится что-то вроде обычной обработки события. С помощью класса PendingIntent создаём наше событие и регистрируем его в RemoteViews. Затем обновляем виджет. А в методе onReceive ловим наше «событие» и обрабатываем, выводя сообщение с помощью класса Toast.

Добовляем изменения в файл AndroidManifest.xml:
  1. <receiver android:name=".widget.HelloWidget" android:label="@string/app_name">
  2.        <intent-filter>
  3.              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  4.              <action android:name="ru.example.android.widget.ACTION_WIDGET_RECEIVER" />
  5.        </intent-filter>
  6.        <meta-data android:name="android.appwidget.provider"
  7.                                    android:resource="@xml/hello_widget_provider" />
  8. </receiver>


Компилируем, и наслаждаемся резульатом.


Ссылки по теме:


P.S. Попытался описать все максимально просто, чтобы не забить голову, а получить работающий пример. В следующих статьях буду углубляться в тему.
Теги:
Хабы:
+40
Комментарии 29
Комментарии Комментарии 29

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн