Статьи

Как кодировать виджет Android

Одна из часто упоминаемых функций Android – виджеты на главном экране. Официальное определение виджета, взятое из документации Android:

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

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

Документация Android по App Widgets обеспечивает довольно хорошую отправную точку при изучении виджетов. Мы начнем с примера, который очень похож на их учебник, а затем перейдем к демонстрации некоторых более сложных функций. Мы предполагаем, что пользователь использует Eclipse с Android Development Extensions для создания своего приложения, но такие же результаты могут быть достигнуты в других IDE. Начало работы с Android не является бессмысленным руководством по настройке среды разработки Android.

Давайте начнем с создания нового проекта Android. Выберите Файл »Создать» Android Project . Заполните мастера, как считаете нужным, с некоторыми разумными ценностями. Вы можете выбрать любые значения, которые вам нравятся, но для моего примера я специально использовал следующие значения, оставив другие значения по умолчанию.

  • Название проекта: Пример виджета
  • Цель сборки: 2.2 [Уровень API 8]
  • Имя приложения: пример виджета
  • Имя пакета: com.eightbitcloud.example.widget
Android Widget Рисунок 1

фигура 1

Затем нажмите « Готово» . Это создаст вам полностью работающее и тестируемое приложение для Android. Запустите его в эмуляторе, если хотите, щелкнув правой кнопкой мыши по проекту и выбрав « Запуск от имени» »Android Application . Возможно, вам придется создать определение для эмулятора. После запуска приложение просто отображает черный экран со строкой «Hello World». Скучно, но это хорошая отправная точка.

В Android все компоненты, показанные в ОС, определены в файле манифеста. Загрузите AndroidManifest.xml с верхнего уровня вашего проекта. Eclipse покажет вам файл в структурированном редакторе, но если вы знакомы с XML, может быть проще перейти к исходному виду, выбрав последнюю вкладку в редакторе. Это должно выглядеть примерно так:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.eightbitcloud.example.widget" android:versionCode="1"
  android:versionName="1.0">
  <uses-sdk android:minSdkVersion="8" />

  <application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".WidgetExampleActivity"
      android:label="@string/app_name">
      <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

  </application>
</manifest>

Важной вещью, которая в данный момент находится в этом файле, является Activity, которая определяет основной вид приложения. Вы увидите это внутри тега приложения. Это сообщает Android, где находится класс, который определяет основной вид вашего приложения, какому имени оно должно быть присвоено (атрибутом label) и на какие намерения оно должно реагировать. Основное представление помечено как ОСНОВНОЕ действие, чтобы указать, что Android должен загрузить его при запуске приложения, и с помощью ЗАПУСКА, чтобы указать, что оно должно появиться в списке приложений. Ничто из этого не имеет прямого отношения к виджетам, но полезно для понимания того, как Android обрабатывает манифест.

Чтобы добавить наш виджет, нам нужно добавить в манифест новый элемент, который его определяет. Ниже действия добавьте следующее

 <receiver android:name=".ExampleAppWidgetProvider" android:label="8-bit cloud widget 1">
  <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  </intent-filter>
  <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget1_info" />
</receiver>

Это определяет новый класс ExampleAppWidgetProviderAPPWIDGET_UPDATE Нам нужно дать имя нашему виджету, что мы делаем, указав значение для атрибута android:label Обычно вы указали бы это в файле res/values/strings.xml Я использовал «8-битный облачный виджет 1». Остальная часть конфигурации виджета будет указана в файле метаданных , который будет находиться в файле res / xml / widget1_info.xml . Наш следующий шаг – создать этот файл метаданных. Создайте его, выбрав File »New» Other »Android» Android XML и заполните его следующим образом.

Android Widget Рисунок 2

фигура 2

После создания файла перейдите на вкладку источника и установите для содержимого файла следующее:

 <?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dp"
    android:minHeight="72dp"
    android:updatePeriodMillis="1000"
    android:initialLayout="@layout/widget1">
</appwidget-provider>

Параметры minWidthminHeight Существуют специальные правила относительно размеров, которые могут иметь виджеты Android, потому что Android упорядочивает свои домашние экраны в сетке 4 x 4. В документации указано, что вы рассчитываете размер с помощью следующей формулы:

размер = (количество ячеек * 74) – 2

В нашем примере мы хотим, чтобы наш виджет имел размер 4 * 1, поэтому мы указываем размер как (4 * 74) – 2 = 294 на 72 dps.

Представления Android обновляются только на периодической основе. Мы хотим отобразить часы, поэтому я updatePeriodMillis Оказывается, что такие маленькие значения не работают, но я вернусь к этому позже. Наконец, мы указываем, какой макет будет использоваться для виджета. Давайте создадим этот файл макета, используя тот же мастер создания XML для Android, который мы использовали ранее, за исключением того, что на этот раз мы выбираем тип макета и имя файла widget1. Как и раньше, перейдите на вкладку источника файла и используйте следующее содержимое:

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center"
  android:layout_margin="4dp"
  android:background="@drawable/background">
    <TextView android:id="@+id/widget1label" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_width="wrap_content"/>
    <Button android:text="Click Me!" android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content"/>

</LinearLayout>

Макет определяет, какие компоненты будут в виджете, и как они расположены. Однако, важные вещи, которые нужно увидеть здесь, – это компоненты TextViewButton Они соответствуют компонентам, которые будут показаны на экране.

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

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

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • AnalogClock
  • кнопка
  • Хронометр
  • ImageButton
  • ImageView
  • Индикатор
  • TextView
  • ViewFlipper

В макете вы заметите, что для виджета также задан фон. Вам нужно будет создать свой собственный фон для рисования в res / drawable / background , или вы можете использовать тот, который я создал для примера .

 package com.eightbitcloud.example.widget;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.RemoteViews;

public class ExampleAppWidgetProvider extends AppWidgetProvider {
  DateFormat df = new SimpleDateFormat("hh:mm:ss");

  public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    final int N = appWidgetIds.length;

    Log.i("ExampleWidget",  "Updating widgets " + Arrays.asList(appWidgetIds));

    // Perform this loop procedure for each App Widget that belongs to this
    // provider
    for (int i = 0; i < N; i++) {
      int appWidgetId = appWidgetIds[i];

      // Create an Intent to launch ExampleActivity
      Intent intent = new Intent(context, WidgetExampleActivity.class);
      PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

      // Get the layout for the App Widget and attach an on-click listener
      // to the button
      RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget1);
      views.setOnClickPendingIntent(R.id.button, pendingIntent);

      // To update a label
      views.setTextViewText(R.id.widget1label, df.format(new Date()));

      // Tell the AppWidgetManager to perform an update on the current app
      // widget
      appWidgetManager.updateAppWidget(appWidgetId, views);
    }
  }
}

Этот класс содержит код, который будет использоваться для обновления нашего представления каждый раз, когда истекает период ожидания. Он отвечает за обновление содержимого виджета. Виджеты запускаются в другом приложении (домашнем экране), чем наше приложение, так что это не просто вопрос обновления компонентов. Вместо этого вам предоставляется доступ к компоненту RemoteViews

В нашем случае мы стремимся сделать две вещи. Сначала мы хотим добавить слушатель действия для нашей кнопки, чтобы при нажатии на нее открывалось наше основное приложение. Это делается с помощью PendingIntentsetOnClickPendingIntent() Во-вторых, мы хотим обновить TextView Это делается с помощью вызова setTextViewText() Как только это будет сделано, мы обновляем виджет с помощью updateAppWidget()

Вуаля! Теперь у нас есть работающее приложение для виджетов. Установите его на свой эмулятор, выбрав профиль запуска, который вы создали ранее. Как и прежде, он покажет вам вид приложения. Выйдите на главный экран с помощью кнопки «Домой», затем нажмите и удерживайте фон, чтобы выбрать виджет . Теперь вам должен быть представлен список виджетов с вашим виджетом в списке. Выберите свой виджет, и он будет добавлен на домашний экран.

Android Виджет Рисунок 3

Рисунок 3

Android Widget Рисунок 4

Рисунок 4

Ура! Подождите, часы не обновляются! В нашем файле метаданных мы установили период обновления равным 1 секунде, поэтому он должен обновляться раз в секунду, верно? Оказывается, для этого есть очень веская причина. Каждый раз, когда виджет нуждается в обновлении, Android активирует ваше устройство, загружает ваше приложение и запускает класс WidgetProvider для обновления виджета. Это происходит, даже если ваш экран выключен, а телефон спит. Если период обновления установлен на запуск только несколько раз в час или реже, то это не будет значительным расходом заряда батареи. Если период обновления очень короткий, как в нашем примере, то виджет истощится очень быстро, и у вас будет длинный список недовольных клиентов. Чтобы избежать этого, Google ограничивает период обновления раз в полчаса или более.

К счастью, есть еще один способ обработки более частых обновлений; тот, который работает только при включенном экране, чтобы не разрядить аккумулятор. Для этого мы используем AlarmManager . Использование AlarmManager будет предметом моей следующей статьи.

Как и во всех моих статьях, я разместил источник на GitHub. Вы можете найти код как часть моего Android- репозитория в подкаталоге WidgetExample . Этот проект будет обновляться по мере развития учебной серии, поэтому я пометил этот первый пример как тег. Пожалуйста, не стесняйтесь задавать вопросы или оставить отзыв.

И если вам понравилось читать этот пост, вы полюбите Learnable ; место, чтобы узнать новые навыки и приемы у мастеров. Участники получают мгновенный доступ ко всем электронным книгам и интерактивным онлайн-курсам SitePoint, таким как « Начало разработки Android» .

Комментарии к этой статье закрыты. Есть вопрос по поводу Android? Почему бы не спросить об этом на наших форумах ?