Разработка под Android: Виджет со счетчиком непрочитанных сообщений
Часто встречающаяся задача — создание виджета со счетчиком непрочитанных сообщений/звонков и т.п. Однако в Android нет стандартного класса для создания таких виджетов.
Как же все-таки создать такой виджет?
О том как создавать виджеты и как они работают можно найти в этой и этой статьях, потому не будем останавливаться на деталях создания виджетов как таковых.
Основная задача, которую нам необходимо выполнить — создать виджет, максимально похожий на стандартные иконки и виджеты стандартных приложений.
Попытки найти уже готовое решение ни к чему не привели. Потому пришлось взять в руки APK Manager (спасибо статье про реверс-инжиниринг) и приложение Google Reader for Android — надеялся, что их виджет выглядит стандартно. Оказалось, что это не так:
Немного похоже, конечно, но не совсем то, что нужно. Пришлось доделывать:
<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-файл, описывающий, как виджет будет реагировать на нажатия/выделения:
Тут мы сталкиваемся с первой проблемой — pressed_application_background и focused_application_background — изображения фона. Но в разных телефонах фоны разные. Например, в стандартном Android — фон оранжевый, в HTC — зеленый. Решением была бы возможность обратиться к стандартным ресурсам Android'a — благодаря сайту Android R Drawables, можно даже найти id этих ресурсов:
Вторая проблема — аналогична первой. Фон круга с числом непрочитанных сообщений на стандартном Android — красный, на HTC — зеленый. Но тут можно обратиться к стандартному ресурсу — @android:drawable/ic_notification_overlay. Это не круг, но выглядит очень похоже (изображение будет чуть ниже).
Подсветка фона текста взята из виджета Google Reader без изменений (файл appwidget_text_background.xml):
В моем приложении (думаю, как и в других приложениях с таким счетчиком) нет необходимости периодически обновлять значение счетчика. Потому updatePeriodMillis=«0» и его обновление происходит лишь в двух случаях:
Вот что получилось в итоге:
Выглядит неидеально, но по-моему очень похоже. В эмуляторе выглядит тоже очень похоже на оригинал.
Как же все-таки создать такой виджет?
О том как создавать виджеты и как они работают можно найти в этой и этой статьях, потому не будем останавливаться на деталях создания виджетов как таковых.
Основная задача, которую нам необходимо выполнить — создать виджет, максимально похожий на стандартные иконки и виджеты стандартных приложений.
Попытки найти уже готовое решение ни к чему не привели. Потому пришлось взять в руки APK Manager (спасибо статье про реверс-инжиниринг) и приложение Google Reader for Android — надеялся, что их виджет выглядит стандартно. Оказалось, что это не так:
Немного похоже, конечно, но не совсем то, что нужно. Пришлось доделывать:
<?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 этих ресурсов:
- pressed_application_background_static
- focused_application_background_static
Вторая проблема — аналогична первой. Фон круга с числом непрочитанных сообщений на стандартном 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» и его обновление происходит лишь в двух случаях:
- когда происходит обновление в базе данных;
- когда приложение становится невидимым (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);
}
Вот что получилось в итоге:
Выглядит неидеально, но по-моему очень похоже. В эмуляторе выглядит тоже очень похоже на оригинал.
0 комментариев