Как обновить Уведомление с RemoteViews?

Я создаю уведомление с RemoteViews из обычая Service, который работает с уведомлением в режиме переднего плана (то есть служба будет оставаться активной, пока уведомление видимо пользователю). Уведомление установлено как Постоянное, поэтому пользователь не может его убрать.

Я хотел бы изменить, например, растровое изображение, показанное в ImageView, содержащиеся в макете удаленного просмотра или изменить текстовое значение в TextView, Макет в удаленном представлении устанавливается с помощью файла макета XML.

Моя проблема заключается в том, что после создания уведомления и видимости для пользователя, если я позвоню любой из RemoteViews функции как setImageViewResource() изменить Bitmap показано в ImageView, изменения не видны, если я не позвоню setImageViewResource() Я звоню потом:

NotificationManager.notify( id, notification );

или же

Service.startForeground(id,notification);

Это не звучит правильно для меня, хотя Я не могу поверить, что обновить RemoteViews Пользовательский интерфейс в уведомлении, которое уже создано, я должен повторно инициализировать уведомление. Если у меня есть Button контролировать в уведомлении, он обновляет себя на ощупь и отпустить. Так что должен быть способ сделать это правильно, но я не знаю как.

Вот мой код, который создает уведомления внутри моего Service пример:

this.notiRemoteViews = new MyRemoteViews(this,this.getApplicationContext().getPackageName(),R.layout.activity_noti1);

Notification.Builder notibuilder = new Notification.Builder(this.getApplicationContext());
notibuilder.setContentTitle("Test");
notibuilder.setContentText("test");
notibuilder.setSmallIcon(R.drawable.icon2);
notibuilder.setOngoing(true);

this.manager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
this.noti = notibuilder.build();
this.noti.contentView = this.notiRemoteViews;
this.noti.bigContentView = this.notiRemoteViews;
this.startForeground(NOTIFICATION_ID, this.noti);

И функция, которая "форсирует" изменения интерфейса в уведомлении:

public void updateNotiUI(){
    this.startForeground(NOTIFICATION_ID, this.noti);
}

В MyRemoteViews Класс, когда требуется, я делаю это, чтобы внести изменения в пользовательский интерфейс:

this.setImageViewResource(R.id.iconOFF, R.drawable.icon_off2);
this.ptMyService.updateNotiUI();

Может кто-нибудь сказать мне, что является правильным способом обновления компонентов пользовательского интерфейса RemoteViews в уведомлении?

4 ответа

Решение

Вот подробный пример обновления уведомления с помощью RemoteViews:

private static final int NOTIF_ID = 1234;
private NotificationCompat.Builder mBuilder;
private NotificationManager mNotificationManager;
private RemoteViews mRemoteViews;
private Notification mNotification;
...

// call this method to setup notification for the first time
private void setUpNotification(){

    mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    // we need to build a basic notification first, then update it
    Intent intentNotif = new Intent(this, MainActivity.class);
    intentNotif.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    PendingIntent pendIntent = PendingIntent.getActivity(this, 0, intentNotif, PendingIntent.FLAG_UPDATE_CURRENT);

    // notification's layout
    mRemoteViews = new RemoteViews(getPackageName(), R.layout.custom_notification_small);
    // notification's icon
    mRemoteViews.setImageViewResource(R.id.notif_icon, R.drawable.ic_launcher);
    // notification's title
    mRemoteViews.setTextViewText(R.id.notif_title, getResources().getString(R.string.app_name));
    // notification's content
    mRemoteViews.setTextViewText(R.id.notif_content, getResources().getString(R.string.content_text));

    mBuilder = new NotificationCompat.Builder(this);

    CharSequence ticker = getResources().getString(R.string.ticker_text);
    int apiVersion = Build.VERSION.SDK_INT;

    if (apiVersion < VERSION_CODES.HONEYCOMB) {
        mNotification = new Notification(R.drawable.ic_launcher, ticker, System.currentTimeMillis());
        mNotification.contentView = mRemoteViews;
        mNotification.contentIntent = pendIntent;

        mNotification.flags |= Notification.FLAG_NO_CLEAR; //Do not clear the notification
        mNotification.defaults |= Notification.DEFAULT_LIGHTS;

        // starting service with notification in foreground mode
        startForeground(NOTIF_ID, mNotification);

    }else if (apiVersion >= VERSION_CODES.HONEYCOMB) {
        mBuilder.setSmallIcon(R.drawable.ic_launcher)
                .setAutoCancel(false)
                .setOngoing(true)
                .setContentIntent(pendIntent)
                .setContent(mRemoteViews)
                .setTicker(ticker);

        // starting service with notification in foreground mode
        startForeground(NOTIF_ID, mBuilder.build());
    }
}

// use this method to update the Notification's UI
private void updateNotification(){

    int api = Build.VERSION.SDK_INT;
    // update the icon
    mRemoteViews.setImageViewResource(R.id.notif_icon, R.drawable.icon_off2);
    // update the title
    mRemoteViews.setTextViewText(R.id.notif_title, getResources().getString(R.string.new_title));
    // update the content
    mRemoteViews.setTextViewText(R.id.notif_content, getResources().getString(R.string.new_content_text));

    // update the notification
    if (api < VERSION_CODES.HONEYCOMB) {
        mNotificationManager.notify(NOTIF_ID, mNotification);
    }else if (api >= VERSION_CODES.HONEYCOMB) {
        mNotificationManager.notify(NOTIF_ID, mBuilder.build());
    }
}

Макет для уведомления, т.е. res/layout/custom_notification_small.xml:

<!-- We have to set the height to 64dp, this is the rule of the small notification -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="64dp"
    android:orientation="horizontal"
    android:id="@+id/notif_small"
    android:background="@drawable/notification_background">

    <ImageView
        android:id="@+id/notif_icon"
        android:contentDescription="@string/notif_small_desc"
        android:layout_width="47dp"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_alignParentLeft="true"
        android:src="@drawable/ic_launcher"
        android:layout_marginLeft="7dp"
        android:layout_marginRight="9dp"/>

    <TextView
        android:id="@+id/notif_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/notif_icon"
        android:singleLine="true"
        android:paddingTop="8dp"
        android:textSize="17sp"
        android:textStyle="bold"
        android:textColor="#000000"
        android:text="@string/app_name"/>

    <TextView
        android:id="@+id/notif_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/notif_icon"
        android:paddingBottom="9dp"
        android:layout_alignParentBottom="true"
        android:singleLine="true"
        android:textSize="13sp"
        android:textColor="#575757"
        android:text="Content" />
</RelativeLayout>

Надеюсь, этот пример вам очень поможет!

ПРИМЕЧАНИЕ: вы не можете обновить пользовательский NotificationCompat в pre-Honeycomb, поэтому я добавил альтернативный способ обновить его в pre-Honeycomb, то есть сначала проверить уровень API и использовать устаревший Notification вместо.

ПРЕДУПРЕЖДЕНИЕ!

Единственный правильный способ обновить уведомление - это воссоздать RemoteViews перед каждым NotificationManager#notify. Зачем? Утечка памяти приводит к TransactionTooLargeException, о чем было сообщено в следующих вопросах:

Каждый вызов RemoteViews, такой как setViewVisibility(...) и т. Д., Добавляет соответствующее действие в очередь действий. После уведомления удаленное представление раздувается, и действия фактически применяются. Но очередь не очищается!

Посмотрите на скриншот, сделанный во время отладки этого случая.

введите изображение здесь

Там я обновляю уведомления аудиоплеера данными, поступающими из ViewModel. Приложение остановлено в строке #81, и вы можете увидеть экземпляр RemoteViews, который имеет массив действий с размером 51! Но я только дважды переключил звуковую дорожку и нажал паузу! Конечно, мне пришлось наблюдать сбой приложения через TransactionTooLargeException через некоторое время.

Мелкие исследования подтвердили, что не существует общедоступного API для прямой или косвенной очистки очереди действий, поэтому единственный способ обновить представление уведомлений - отдельно сохранить его состояние и воссоздать экземпляр RemoteViews, переданный в Notification.Builder, в любом случае это не сильно перегружает поток пользовательского интерфейса.

Вам бы пришлось позвонить NotificationManager.notify(id, notification) уведомить систему уведомлений о том, что вы хотите обновить представление уведомлений. Вот ссылка на документы http://developer.android.com/training/notify-user/managing.html.

Есть метод, который возвращает объект уведомления.

private Notification getNotification(NotificationCompat.Builder mBuilder) {
    RemoteViews mRemoteViews = new RemoteViews(getPackageName(), R.layout.notification_layout);
    // Update your RemoteViews
    mBuilder.setContent(mRemoteView);
    Notification mNotification = mBuilder.build();
    // set mNotification.bigContentView if you want to
    return mNotification;

}

private void refreshNotification() {
    mNotificationManager.notify(getNotification(mNotificationBuilder),
                        NOTIFICATION_ID);
    // mNotificationBuilder is initialized already
}

Также обратите внимание, что bigContentView а также RemoteViews не полностью перерисованы. Если для некоторых элементов bigContentView видимость установлена ​​на GONEи если вы хотите показать его в следующий раз, вы должны явно установить видимость VISIBLE,

Не хранить Notification объект, но Notification.Builder объект. Создавайте новые уведомления каждый раз, прежде чем отправлять их

NotificationManager.notify( id, notification );
Другие вопросы по тегам