Полный список
Notifications — это уведомления, которые пользователь видит в верхней части экрана, когда ему приходит новое письмо, сообщение, обновление и т.п. В ближайшие несколько уроков мы подробно разберем, какие возможности предоставлены разработчикам для показа уведомлений.
В этом уроке рассмотрим основы — отображение/обновление/удаление уведомления и обработка нажатия на него.
Отображение
Код создания простого уведомления выглядит так:
Используем билдер, в котором указываем иконку, заголовок и текст для уведомления. Методом build получаем готовое уведомление.
Далее используем NotificationManager и его метод notify, чтобы показать созданное уведомление. Кроме notification, требуется передать id. Это необходимо, чтобы в дальнейшем мы могли использовать этот id для обновления или удаления уведомления.
Конструктор new NotificationCompat.Builder(Context) будет помечен как Deprecated, если вы используете библиотеку appCompat версии 26 и выше. Так получилось потому, что в Android API 26 появился новый конструктор и рекомендуется использовать его. Пока не обращайте внимание на это. В одном из следующих уроков мы рассмотрим использование правильного конструктора.
Запустив этот код, мы увидим уведомление
Оно отображает иконку и два текста, которые мы указывали в билдере. Нажатие на него ни к чему не приведет, т.к. мы не реализовали обработчик нажатия. Мы это сделаем чуть позже.
Обновление
Мы отобразили уведомление и теперь хотим его обновить. Для этого нужно просто снова показать уведомление методом notify и использовать при этом тот же id.
Это будет выглядеть так:
Код полностью аналогичен коду, что мы использовали при отображении уведомления. Только в билдере используем другие тексты и иконку. Самое главное, что в методе notify мы снова используем NotificationManager по этому id найдет уведомление, которое мы отобразили чуть раньше и заменит его новым
Несколько уведомлений
Чтобы показать новое уведомление, а не обновить уже существующее, надо использовать другой id в методе notify.
Мы использовали разные id в методе notify и получили два разных уведомления
Удаление
Чтобы удалить уведомление, используем NotificationManager и его метод cancel с указанием id уведомления.
Либо методом cancelAll можем удалить все уведомления сразу
При удалении уведомления нет необходимости проверять, отображается оно или нет. Если уведомления по каким-то причинам уже нет, то просто ничего не произойдет.
Обработка нажатия
Чтобы выполнить какое-либо действие по нажатию на уведомление, необходимо использовать PendingIntent. PendingIntent — это контейнер для Intent. Этот контейнер может быть использован для последующего запуска вложенного в него Intent.
Мы будем создавать Intent для запуска, например, Activity, упаковывать этот Intent в PendingIntent и передавать PeningIntent в уведомление. По нажатию на уведомление, система достанет из него PedningIntent и использует вложенный в него Intent, чтобы запустить Activity.
Давайте посмотрим, как это выглядит на практике:
Создаем Intent для запуска Activity и упаковываем его в PedningIntent.
Подробно о PedningIntent и его параметрах вы можете почитать в Уроке 119. Там я подробно рассмотрел различные кейсы на примерах с уведомлением и вызовом BroadcastReceiver.
Созданный PendingIntent нам надо будет передать в билдер уведомления. Полный код создания уведомления будет выглядеть так:
Передаем PendingIntent в метод setContentIntent билдера уведомления.
По нажатию на уведомление откроется MainActivity
Обратите внимание, что уведомление не удаляется автоматически после нажатия на него. Чтобы исправить это, можно в билдере уведомления использовать включить параметр autoCancel
Уведомление, созданное с этим флагом будет закрываться после нажатия на него.
Билдер уведомления имеет еще несколько методов, которые могут быть полезны.
setNumber — позволяет добавить число в уведомление
setContentInfo — добавит текст справа
В старых версиях это выглядит так
В последних версиях он переехал в верхнюю часть уведомления
setColor — добавит фоновый цвет к иконке
setWhen — можно указать свое время для уведомления (время when). По умолчанию when = времени создания уведомления
setShowWhen — показывать ли время в уведомлении
setUsesChronometer — вместо статичного времени в уведомлении будет отображаться счетчик (00:00), показывающий сколько прошло от времени when. Может быть полезно для уведомления секундомера или звонка.
setOngoing — такое уведомление пользователь не сможет закрыть или смахнуть. Оно будет отображаться поверх обычных уведомлений.
setVibrate, setSound, setLights — настройки вибры, звука и LED индикатора устройства
setPriority — возможность установить приоритет. Доступные значения от -2 (NotificationCompat.PRIORITY_MIN) до 2 (NotificationCompat.PRIORITY_MAX). Поведение может отличаться на разных версиях Android, но общий смысл одинаков — чем выше приоритет, тем выше вероятность того, что пользователь увидит ваше уведомление.
setTimeoutAfter — возможность установить таймаут (в мсек), после которого уведомление само удалится. Добавлен в API 26.
setLargeIcon — возможность задать свою картинку в качестве иконки уведомления.
Иконка из setSmallIcon будет видна в статусбаре, когда панель уведомлений не раскрыта.
А само уведомление будет выглядеть так:
setProgress — возможность отобразить прогрессбар в уведомлении
У метода три параметра:
max — максимальное значение прогрессбара. Укажите 0, если надо скрыть прогрессбар.
progress — текущее значение прогрессбара. Может быть от 0 до max.
indeterminate — если true, то будет показан «бесконечный» прогрессбар
Сначала отображаем бесконечный прогрессбар и текст Preparing. Т.е. делаем вид, что идет подготовка к выполнению операции.
Затем в отдельном потоке имитируем выполнение операции. Каждые 300 мсек увеличиваем значение progress и обновляем уведомление, чтобы прогрессбар показал текущий прогресс. А также в тексте показываем значение прогресса и максимума.
После выполнения операции скрываем прогрессбар и показываем текст Completed.
Повторюсь, очень рекомендую прочесть и понять Урок 119. В нем я подробно рассматриваю, почему PendingIntent последнего уведомления заменяет PendingIntent предыдущих уведомлений, и как этого можно избежать, используя, например, requestCode.
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Android Notifications. Оповещения через Status Bar
Добрый день, хабровчане. Давно занимаюсь разработкой под Android и хотелось бы рассказать сообществу о правильном подходе к созданию уведомлений.
На хабре уже есть статья по уведомлениям в статус баре для андроид . В ней рассматриваются основы отображения стандартного и конфигурируемого layout в статус баре.
Ниже, помимо описанного ранее, мы рассмотрим добавление прогрессбара, обработку события по нажатию на уведомлений, различные варианты состояний уведомлений. Рассмотрим добавленный на днях в Compatibility library Notification.Builder. А также поговорим о рекомендациям по UI (design guidlines), которые гугл рекомендует соблюдать при создании уведомлений.
Guidlines
Как советуют разработчики Android в официальном гайдлайне
Когда показывать уведомления:
- Мы показываем уведомления, когда не хотим отвлекать пользователя, перекрывая ему экран нашими диалогами или переходом на экран уведомления. Мы не отвлекаем пользователя, но при этом не лишаем его возможности узнать содержание нашего уведомления в любой момент.
- Чаще всего уведомления не всплывают спонтанно, а появляются в моменты, когда пользователь ожидает реакции от приложения.
- В первую очередь уведомления должны отражать события, зависящие от времени. Как то: события календаря, входящие сообщения, запросы из социальных сетей.
Когда не стоит показывать уведомления:
- Не нужно показывать уведомления для не важных псевдо-зависящих от времени событий. Например, новости из социальных сетей.
- Нет необходимости показывать то, что уже отображено в UI приложения.
- Не стоит отображать ход низкоуровневых операций, вроде обращения к БД.
- Если приложение быстро само исправляет ошибку, то не нужно вовсе показывать эту ошибку, тем более уведомлением.
- Не показывайте уведомления о сервисах, которые пользователь не может контролировать.
- Плохим подходом является создание большого числа уведомлений, с целью напоминать пользователю о приложении, показывая постоянно его иконку и имя.
Хорошая практика:
- По клику на уведомление, пользователю должен открываться соответствующий экран приложения. В некоторых случаях достаточно, чтобы по клику уведомление просто убиралось.
- Указание времени события в уведомлении, также является хорошим подходом.
- Рекомендуется схожие события складывать в одно уведомление, а не отображать на каждое событие своё.
- Всегда убирать из статус-бара уведомления, с которыми пользователь уже ознакомился и произвел соответствующие действия.
- Показывать маленькое превью уведомления при его создании в свёрнутом статус-баре
- Позволять пользователю отключать уведомления в настройках приложения.
- Использовать иконки, обозначающие принадлежность уведомления определённому приложению. Иконки делать монохромными. Для этого рекомндуется воспользоваться специальным онлайн-редактором
- В случае, если событие требует непосредственной реакции пользователя — вместо уведомлений использовать диалоги.
Архитектура:
В качестве утилитки, отвечающей за уведомления, я в своих приложениях использую singleton, к которому можно обратиться из любого класса приложения, нужно лишь иметь ссылку на context.
В ней всегда хранятся ссылки на все созданные во время работы приложения уведомления, которые ещё отображены в статус-баре.
А для присвоения новому уведомлению уникального id используется нехитрый механизм обращения к приватному целочисленному полю, которое каждый раз увеличивается на единицу.
private static final String TAG = NotificationUtils. class .getSimpleName();
private static NotificationUtils instance;
private static Context context;
private NotificationManager manager; // Системная утилита, упарляющая уведомлениями
private int lastId = 0; //постоянно увеличивающееся поле, уникальный номер каждого уведомления
private HashMap notifications; //массив ключ-значение на все отображаемые пользователю уведомления
//приватный контструктор для Singleton
private NotificationUtils(Context context) <
this .context = context;
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notifications = new HashMap ();
>
/**
* Получение ссылки на синглтон
*/
public static NotificationUtils getInstance(Context context) <
if(instance== null ) <
instance = new NotificationUtils(context);
> else <
instance.context = context;
>
return instance;
>
* This source code was highlighted with Source Code Highlighter .
Создание уведомления с помощью NotificationCompat.Builder:
Для того чтобы воспользоваться классами, входящими в библиотеку поддержки прошлых версий (Compatibility library), нужно добавить в проект библиотеку из папки /extras/android/support/v4/android-support-v4.jar
Если же проект нацелен на Android 3.0 и выше, то добавлять ничего не нужно достаточно обратиться к Notification.Builder
public int createInfoNotification( String message) <
Intent notificationIntent = new Intent(context, HomeActivity. class ); // по клику на уведомлении откроется HomeActivity
NotificationCompat.Builder nb = new NotificationCompat.Builder(context)
//NotificationCompat.Builder nb = new NotificationBuilder(context) //для версии Android > 3.0
.setSmallIcon(R.drawable.ic_action_picture) //иконка уведомления
.setAutoCancel( true ) //уведомление закроется по клику на него
.setTicker(message) //текст, который отобразится вверху статус-бара при создании уведомления
.setContentText(message) // Основной текст уведомления
.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT))
.setWhen(System.currentTimeMillis()) //отображаемое время уведомления
.setContentTitle( «AppName» ) //заголовок уведомления
.setDefaults(Notification.DEFAULT_ALL); // звук, вибро и диодный индикатор выставляются по умолчанию
Notification notification = nb.getNotification(); //генерируем уведомление
manager.notify(lastId, notification); // отображаем его пользователю.
notifications.put(lastId, notification); //теперь мы можем обращаться к нему по id
return lastId++;
>
* This source code was highlighted with Source Code Highlighter .
Создание уведомления с произвольным отображением (Custom layout):
/**
* Создание уведомления с прогрессбаром о загрузке
* @param fileName — текст, отображённый в заголовке уведомления.
*/
public int createDownloadNotification( String fileName) <
String text = context.getString(R. string .notification_downloading).concat( » » ).concat(fileName); //текст уведомления
RemoteViews contentView = createProgressNotification(text, context.getString(R. string .notification_downloading)); //View уведомления
contentView.setImageViewResource(R.id.notification_download_layout_image, R.drawable.ic_stat_example); // иконка уведомления
return lastId++; //увеличиваем id, которое будет соответствовать следующему уведомлению
>
/**
* генерация уведомления с ProgressBar, иконкой и заголовком
*
* @param text заголовок уведомления
* @param topMessage сообщение, уотображаемое в закрытом статус-баре при появлении уведомления
* @return View уведомления.
*/
private RemoteViews createProgressNotification( String text, String topMessage) <
Notification notification = new Notification(R.drawable.ic_stat_example, topMessage, System.currentTimeMillis());
RemoteViews contentView = new RemoteViews(context.getPackageName(), R.layout.notification_download_layout);
contentView.setProgressBar(R.id.notification_download_layout_progressbar, 100, 0, false );
contentView.setTextViewText(R.id.notification_download_layout_title, text);
notification.contentView = contentView;
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_ONLY_ALERT_ONCE;
Intent notificationIntent = new Intent(context, NotificationUtils. class );
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.contentIntent = contentIntent;
manager.notify(lastId, notification);
notifications.put(lastId, notification);
return contentView;
>
xml version =»1.0″ encoding =»utf-8″ ? >
LinearLayout xmlns:android =»http://schemas.android.com/apk/res/android»
android:layout_width =»fill_parent»
android:layout_height =»65sp»
android:padding =»10dp»
android:orientation =»vertical» >
LinearLayout
android:layout_width =»fill_parent»
android:layout_height =»wrap_content»
android:orientation =»horizontal» >
ImageView
android:id =»@+id/notification_download_layout_image»
android:layout_width =»wrap_content»
android:layout_height =»wrap_content»
android:src =»@drawable/ic_stat_example»
android:layout_gravity =»center_vertical»/>
TextView
android:id =»@+id/notification_download_layout_title»
style =»@style/NotificationTitle»
android:layout_width =»wrap_content»
android:layout_height =»wrap_content»
android:layout_alignParentTop =»true»
android:layout_marginLeft =»10dip»
android:singleLine =»true»
android:text =»notification_download_layout_title»
android:layout_gravity =»center_vertical»/>
LinearLayout >
ProgressBar
android:id =»@+id/notification_download_layout_progressbar»
style =»?android:attr/progressBarStyleHorizontal»
android:layout_width =»fill_parent»
android:layout_height =»wrap_content»
android:layout_marginTop =»4dp»
android:progress =»0″/>
в андроид 2.3 и выше ( API >10) был создан специальный ресурс, в котором системная тема указывает цвета текста уведомений. Из-за этого в старых версиях приходится использовать костыль:
В файл res/values/styles.xml прописываем:
xml version =»1.0″ encoding =»utf-8″ ? >
resources >
style name =»NotificationText» >
item name =»android:textColor» > ?android:attr/textColorPrimary item >
style >
style name =»NotificationTitle» >
item name =»android:textColor» > ?android:attr/textColorPrimary item >
item name =»android:textStyle» > bold item >
style >
А для поддержки API >10 Создаем файл res/values-v9/styles.xml и вписываем:
xml version =»1.0″ encoding =»utf-8″ ? >
resources >
style name =»NotificationText» parent =»android:TextAppearance.StatusBar.EventContent»/>
style name =»NotificationTitle» parent =»android:TextAppearance.StatusBar.EventContent.Title»/>
resources >
Теперь из кода нашего приложения обращаемся к утилите:
NotificationUtils n = NotificationUtils.getInstance(getActivity());
n.createInfoNotification( «info notification» );
Создаем уведомление с прогресс-баром:
int pbId = NotificationUtils.getInstance(getActivity()).createDownloadNotification( «downloading video» );
И во время выполнения потока постоянно обновляем прогресс вызовом:
В итоге получаем:
Как видно — нижнее уведомление, созданное нами при помощи билдера может быть удалено в любой момент. А уведомление с прогресс-баром размещается в верхнем блоке уведомлений, в котором пользователь не может очистить уведомления.
И напоследок маленькая хитрость:
Если не хотите дублирования в стеке одних и тех же Activity — поставьте в манифесте к нужной activity
android:launchMode=»singleTop»
Источник