web-dev-qa-db-fra.com

Comment mettre à jour Notification avec RemoteViews?

Je crée une notification avec RemoteViews à partir d'un Service personnalisé, qui s'exécute avec une notification en mode avant-plan (le service restera actif tant que la notification sera visible par l'utilisateur). La notification est définie sur En cours pour que l'utilisateur ne puisse pas l'extraire.

J'aimerais modifier, par exemple, le bitmap affiché dans ImageView, contenu dans la présentation de la vue distante ou modifier le texte dans un TextView. La mise en page dans la vue distante est définie avec un fichier de mise en page XML.

Mon problème est qu'une fois que la notification est créée et visible pour l'utilisateur, si j'appelle une fonction de RemoteViews telle que setImageViewResource() pour changer Bitmap affiché dans un ImageView, la modification n'est pas visible, sauf si j'appelle setImageViewResource().

NotificationManager.notify( id, notification );

ou 

Service.startForeground(id,notification);

Cela ne me semble pas correct cependant. Je ne peux pas croire que pour mettre à jour l'interface utilisateur RemoteViews dans une notification déjà créée, je dois réinitialiser la notification. Si j'ai le contrôle Button dans une notification, il se met à jour de manière tactile et relâchée. Il doit donc y avoir un moyen de faire cela correctement, mais je ne sais pas comment.

Voici mon code qui crée une notification dans mon instance 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);

Et la fonction qui «force» les modifications de l'interface utilisateur à la notification:

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

Dans la classe MyRemoteViews, lorsque cela est nécessaire, je le fais pour apporter des modifications à l'interface utilisateur:

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

Quelqu'un peut-il me dire quel est le bon moyen de mettre à jour les composants d'interface utilisateur d'un RemoteViews dans la notification?

25
Sinisa

Voici un exemple détaillé pour vous permettant de mettre à jour la notification en utilisant 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());
    }
}

Disposition pour la notification, c.-à-d. 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>

J'espère que cet exemple vous aide beaucoup!

REMARQUE: Vous ne pouvez pas mettre à jour la variable NotificationCompat personnalisée avant Honeycomb. J'ai donc ajouté un autre moyen de le mettre à jour avant Honeycomb, c'est-à-dire vérifier le niveau de l'API en premier et utiliser plutôt la Notification obsolète. 

38
Anggrayudi H

Vous devez appeler NotificationManager.notify(id, notification) pour que Notification System sache que vous souhaitez mettre à jour la vue des notifications. Voici le lien de la documentation http://developer.Android.com/training/notify-user/managing.html .

Avoir une méthode qui retourne l'objet Notification.

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
}

Notez également que bigContentView et RemoteViews ne sont pas complètement redessinés. Si la visibilité de certains éléments de bigContentView est définie sur GONE et si vous souhaitez l'afficher la prochaine fois, vous devez définir explicitement la visibilité sur VISIBLE.

2
Froyo

ATTENTION!

Le seul moyen correct de mettre à jour la notification consiste à recréer RemoteViews avant chaque notification NotificationManager #. Pourquoi? Il y a une fuite de mémoire menant à TransactionTooLargeException, comme indiqué dans ces questions:

Chaque appel sur RemoteViews tel que setViewVisibility (...), etc., ajoute l'action correspondante à la file d'attente d'actions à appliquer. Après notification, la vue distante est gonflée et les actions sont réellement appliquées. Mais la file d'attente n'est pas effacée!

Jetez un coup d'œil à la capture d'écran prise lors du débogage de ce cas.

 enter image here

Là, je mets à jour la notification du lecteur audio avec les données provenant de ViewModel. L'application est arrêtée sur la ligne 81 et vous pouvez voir l'instance RemoteViews qui contient un tableau d'actions de taille 51! Mais je n'ai changé que deux fois de piste audio et appuyé sur pause! Bien sûr, j'ai dû observer un blocage d'application avec TransactionTooLargeException après un certain temps.

Une recherche superficielle a confirmé qu'aucune API publique ne pouvait effacer directement ou indirectement la file d'attente d'actions. Le seul moyen de mettre à jour la vue de notification consiste donc à conserver son état séparément et à recréer l'instance RemoteViews transmise à Notification.Builder.

1
Max Elkin

Ne stockez pas l'objet Notification, mais l'objet Notification.Builder. Produire une nouvelle notification à chaque fois avant de la pousser à 

NotificationManager.notify( id, notification );
0
dykzei eleeot