Разработка под Android: Виджет со счетчиком непрочитанных сообщений

Часто встречающаяся задача — создание виджета со счетчиком непрочитанных сообщений/звонков и т.п. Однако в Android нет стандартного класса для создания таких виджетов.

image

Как же все-таки создать такой виджет?

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

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

Попытки найти уже готовое решение ни к чему не привели. Потому пришлось взять в руки APK Manager (спасибо статье про реверс-инжиниринг) и приложение Google Reader for Android — надеялся, что их виджет выглядит стандартно. Оказалось, что это не так:

image

Немного похоже, конечно, но не совсем то, что нужно. Пришлось доделывать:

<?xml version="1.0" encoding="UTF-8"?>
 <LinearLayout android:orientation="vertical" android:id="@+id/widget"
 android:background="@drawable/shortcut_selector" android:focusable="true"
 android:clickable="true" android:layout_width="74.0dip"
 android:layout_height="82.0dip" xmlns:android="http://schemas.android.com/apk/res/android">
 <FrameLayout android:layout_width="fill_parent"
 android:layout_height="0.0dip" android:layout_weight="1.0"
 android:paddingTop="3dip">
 <FrameLayout android:layout_gravity="center"
 android:layout_width="wrap_content" android:layout_height="wrap_content">
 <ImageView android:id="@+id/icon"
 android:layout_width="wrap_content" android:layout_height="wrap_content"
 android:src="@drawable/icon" android:scaleType="center" />
 <TextView android:textSize="14.0dip" android:textStyle="bold"
 android:textColor="#ffffffff" android:gravity="center"
 android:layout_gravity="bottom|right|center" android:id="@+id/widget_counter"
 android:background="@android:drawable/ic_notification_overlay"
 android:layout_width="24dip" android:layout_height="24dip" android:singleLine="true"
 android:shadowColor="#ff000000" android:shadowRadius="1.0"/>
 

<TextView android:textSize=«13.0dip» android:textColor="#ffffffff"
android:ellipsize=«marquee» android:gravity=«center_horizontal»
android:layout_gravity=«center_horizontal» android:id="@android:id/text1"
android:background="@drawable/appwidget_text_background"
android:layout_width=«wrap_content» android:layout_height=«wrap_content»
android:layout_marginTop=«1dip»
android:paddingBottom=«1dip»
android:singleLine=«true» android:shadowColor="#ff000000"
android:shadowRadius=«2.0» android:layout_weight=«0.0» android:text="@string/app_name" />

Высота виджета — 82dip — больше, чем рекомендуемая. Но иначе наш виджет будет меньше стандартной иконки.

Указанный в коде выше @drawable/shortcut_selector — xml-файл, описывающий, как виджет будет реагировать на нажатия/выделения:

<?xml version="1.0" encoding="UTF-8"?>
 <selector
 xmlns:android="http://schemas.android.com/apk/res/android">
 <item android:state_pressed="true" android:drawable="@drawable/pressed_application_background" />
 <item android:state_focused="true" android:state_window_focused="true" android:drawable="@drawable/focused_application_background" />
 <item android:state_focused="true" android:state_window_focused="false" android:drawable="@android:color/transparent" />
 

Тут мы сталкиваемся с первой проблемой — pressed_application_background и focused_application_background — изображения фона. Но в разных телефонах фоны разные. Например, в стандартном Android — фон оранжевый, в HTC — зеленый. Решением была бы возможность обратиться к стандартным ресурсам Android'a — благодаря сайту Android R Drawables, можно даже найти id этих ресурсов:
  1. pressed_application_background_static
  2. focused_application_background_static
Но эти ресурсы не public и использовать их в своем приложении нельзя. Можно попробовать считывать значение android.os.Build и определять тип OS. Но я решил, что для виджета это не так важно, потому оставил оранжевый фон (кстати, в виджете Google Reader сделано также).

Вторая проблема — аналогична первой. Фон круга с числом непрочитанных сообщений на стандартном Android — красный, на HTC — зеленый. Но тут можно обратиться к стандартному ресурсу — @android:drawable/ic_notification_overlay. Это не круг, но выглядит очень похоже (изображение будет чуть ниже).

Подсветка фона текста взята из виджета Google Reader без изменений (файл appwidget_text_background.xml):

<?xml version="1.0" encoding="UTF-8"?>
 <shape android:shape="rectangle"
 xmlns:android="http://schemas.android.com/apk/res/android">
 <solid android:color="#b2191919" />
 <padding android:left="5.0dip" android:top="1.0dip" android:right="5.0dip" android:bottom="1.0dip" />
 <corners android:radius="8.0dip" />
 


В моем приложении (думаю, как и в других приложениях с таким счетчиком) нет необходимости периодически обновлять значение счетчика. Потому updatePeriodMillis=«0» и его обновление происходит лишь в двух случаях:
  1. когда происходит обновление в базе данных;
  2. когда приложение становится невидимым (onStop()).
При этом для обновления виджета используется следующий код:

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
 int[] a = appWidgetManager.getAppWidgetIds(new ComponentName(this.getPackageName(), "WidgetProvider"));
 List b = appWidgetManager.getInstalledProviders();
 for (AppWidgetProviderInfo i : b) {
 if (i.provider.getPackageName().equals(this.getPackageName())) {
 a = appWidgetManager.getAppWidgetIds(i.provider);
 new WidgetProvider().onUpdate(this, appWidgetManager, a);
 }
 } 


Если число непрочитанных сообщений равно нулю, то круг с числом скрывается:
 <code> if (unreadRecordsCount == 0) {
 views.setViewVisibility(R.id.widget_counter, View.INVISIBLE);
 views.setTextViewText(R.id.widget_counter, "");
 } else {
 views.setTextViewText(R.id.widget_counter, Long.toString(unreadRecordsCount));
 views.setViewVisibility(R.id.widget_counter, View.VISIBLE);
 }
 


Вот что получилось в итоге:
image
Выглядит неидеально, но по-моему очень похоже. В эмуляторе выглядит тоже очень похоже на оригинал.


0 комментариев

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.