Как стать автором
Поиск
Написать публикацию
Обновить

Hello World widget для Android

Время на прочтение9 мин
Количество просмотров71K
Как ни странно, но на русском почти нет нормальных статей по виджетам для 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. Попытался описать все максимально просто, чтобы не забить голову, а получить работающий пример. В следующих статьях буду углубляться в тему.
Теги:
Хабы:
Всего голосов 72: ↑56 и ↓16+40
Комментарии29

Публикации

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