When I was working on a project recently, I encountered a requirement: send a notification with plus and minus buttons and a number. After clicking the button, the number of the notification should be changed accordingly.
It sounds simple, isn't it enough to add click events to the two buttons? However, the notification does not belong to the App process, but the process maintained by the Android system. The custom layout in the notification should RemoteViews
be implemented through :
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_number);
NotificationCompat.Builder.setContent(remoteViews);
RemoteViews
It is used to handle the layout scheme of cross-process display, generally used for notifications and desktop widgets. Due to different processes, RemoteViews
the applied layout cannot be directly used findViewById()
to find the control. It encapsulates some methods to update UI
, for example:
void setTextViewText(int viewId, CharSequence text);
void setViewVisibility(int viewId, int visibility);
void setProgressBar(int viewId, int max, int progress, boolean indeterminate);
void setImageViewResource(int viewId, int srcId);
For more methods, please refer to the official API document.
It does not provide a method to directly add a click event to the control. So I encountered a dilemma.
So, can we directly write the layout of the entire notification as a custom control, and realize the click of the button in the custom control? Unfortunately, it is not possible. When sending a notification, the following error will be reported directly:
android.app.RemoteServiceException: Bad notification posted from package
com.example.xujiafeng.notificationrefresh
In fact, it is normal to think about it carefully. If you can set the click event in such a simple way, RemoteViews
it is impossible for us to set the click event method for the control without exposing it to us.
So what else is there?
RemoteViews
Although there is no method to directly set the click event for the control, the following methods are provided:
void setOnClickPendingIntent (int viewId, PendingIntent pendingIntent)
Official documents explanation of this method are: the equivalent of calling
setOnClickListener(android.view.View.OnClickListener)
to start the offer PendingIntent
.
And PendingIntent
is a delay Intent
, it contains an internal Intent
, when certain conditions are met he will not start Intent
, it has four ways to get the object:
getActivity(Context, int, Intent, int);
getActivities(Context, int, Intent[], int);
getBroadcast(Context, int, Intent, int);
getService(Context, int, Intent, int);
It can be understood from the method name that when it Intent
is started, it will start Activity
, send a broadcast, and start the service.
So if we want to implement a click RemoteViews
on a button to start later Activity
, the code is as follows:
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 21, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.iv_add, pendingIntent);
So back to our previous question, how to realize the change of the number on the notification by clicking the button?
The method is to control the button to send a broadcast, and then the App monitors the broadcast, and sends the same id
notification again after receiving the broadcast . The new notification changes the number. This is the specific idea. The following is the specific code implementation.
Code to send notification:
private void sendNotification()
{
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.layout_number);
Intent addIntent = new Intent();
addIntent.setAction("com.example.xujiafeng.notificationrefresh.add");
remoteViews.setOnClickPendingIntent(R.id.iv_add, PendingIntent.getBroadcast(MainActivity.this, 10, addIntent, PendingIntent.FLAG_UPDATE_CURRENT));
Intent minusIntent = new Intent();
addIntent.setAction("com.example.xujiafeng.notificationrefresh.minus");
remoteViews.setOnClickPendingIntent(R.id.iv_add, PendingIntent.getBroadcast(MainActivity.this, 10, minusIntent, PendingIntent.FLAG_UPDATE_CURRENT));
remoteViews.setTextViewText(R.id.tv_value, String.valueOf(value));
notification = new NotificationCompat.Builder(MainActivity.this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("ContentTitle")
.setContentText("ContentText")
.setWhen(System.currentTimeMillis())
.setTicker("Ticker")
.setContent(remoteViews)
.build();
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(12, notification);
}
Broadcast receiver:
public class NotificationReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
switch (action)
{
case "com.example.xujiafeng.notificationrefresh.add":
value++;
sendNotification();
break;
case "com.example.xujiafeng.notificationrefresh.minus":
value--;
sendNotification();
break;
}
}
}
Register to broadcast:
IntentFilter filter = new IntentFilter();
filter.addAction(ADD_ACTION);
filter.addAction(MINUS_ACTION);
mReceiver = new NotificationReceiver();
registerReceiver(mReceiver, filter);
This is done, the running effect is as follows: