我在自定义Service
中使用RemoteViews
创建通知,该通知以前台模式运行通知(即只要通知可见,服务将保持活动状态用户)。通知设置为正在进行,因此用户无法将其滑动。
我想更改ImageView
中显示的位图,包含在远程视图的布局中或更改TextView
中的文本值。远程视图中的布局使用XML布局文件设置。
我的问题是,一旦通知被创建并且对用户可见,如果我调用RemoteViews
的任何Bitmap
函数来更改ImageView
中显示的setImageViewResource()
1}},更改不可见,除非我打电话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);
}
并强调' UI更改为通知:
MyRemoteViews
在this.setImageViewResource(R.id.iconOFF, R.drawable.icon_off2);
this.ptMyService.updateNotiUI();
类中,如果需要,我这样做是为了更改UI:
RemoteViews
有谁能告诉我更新通知中{{1}}的UI组件的正确方法是什么?
答案 0 :(得分:46)
以下是使用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>
希望这个例子可以帮到你很多!
注意:您无法在Honeycomb之前更新自定义NotificationCompat
,因此我添加了另一种方法来在Honeycomb之前更新它,即首先检查API级别并使用已弃用的{{1}而是。
答案 1 :(得分:2)
您必须致电NotificationManager.notify(id, notification)
让Notification System知道您要更新通知视图。这是文档链接http://developer.android.com/training/notify-user/managing.html。
有一个返回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
}
另请注意,bigContentView
和RemoteViews
未完全重绘。如果bigContentView的某些元素的可见性设置为GONE
,并且您希望下次显示,则必须明确设置VISIBLE
的可见性。
答案 2 :(得分:2)
警告!
更新通知的唯一正确方法是在每个NotificationManager#notify之前重新创建RemoteView。为什么?有内存泄漏导致TransactionTooLargeException,正如在以下问题中所报告的那样:
对RemoteView的每次调用(例如setViewVisibility(...)等)都将相应的操作添加到要应用的操作队列中。通知后,远程视图会膨胀,并且实际上会执行操作。但是队列没有清除!
看看在调试这种情况下拍摄的屏幕截图。
我正在用来自ViewModel的数据更新音频播放器通知。应用程序在第81行停止,您可以看到具有大小为51的动作数组的RemoteViews实例!但是我只切换了两次音轨并按下了暂停键!当然,过一会儿,我不得不通过TransactionTooLargeException来观察应用程序崩溃。
浅层研究证实,没有公共API可直接或间接清除操作队列,因此更新通知视图的唯一方法是单独保留其状态并重新创建传递给Notification.Builder的RemoteViews实例,无论如何这不会使UI线程超载很多
答案 3 :(得分:0)
不要存储Notification
个对象,而是Notification.Builder
个对象。在每次推送之前都会生成新通知
NotificationManager.notify( id, notification );