25 February 2011

Hello World widget для Android

Development for Android
Как ни странно, но на русском почти нет нормальных статей по виджетам для 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. public class HelloWidget extends AppWidgetProvider {
  4. }


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. <TextView android:id="@+id/widget_textview"
  9. android:text="Hello Widget"
  10. android:layout_height="wrap_content"
  11. android:layout_width="wrap_content"
  12. android:layout_gravity="center_horizontal|center"
  13. android:textColor="@android:color/black"/>
  14. </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. <TextView android:id="@+id/widget_textview"
  9. android:text="Hello Widget"
  10. android:layout_height="wrap_content"
  11. android:layout_width="wrap_content"
  12. android:layout_gravity="center_horizontal|center"
  13. android:textColor="@android:color/black"/>
  14. <Button android:id="@+id/widget_button"
  15. android:text="click me"
  16. android:layout_height="wrap_content"
  17. android:layout_width="wrap_content"/>
  18. </LinearLayout>


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

В классе есть 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. Попытался описать все максимально просто, чтобы не забить голову, а получить работающий пример. В следующих статьях буду углубляться в тему.
Tags:AndroidwidgetHello World
Hubs: Development for Android
+40
67.2k 224
Comments 29