android通知悬浮通知
Android Notification Direct Reply action lets us reply to the notification message, it’s very popular with chat notifications such as Whatsapp and Facebook messenger notification messages.
Android Notification Direct Reply操作使我们能够回复通知消息,它在诸如Whatsapp和Facebook Messenger通知消息之类的聊天通知中非常流行。
Android Nougat has introduced several new features. It offers some awesome features such as Inline Reply Actions and Bundled Notifications. In this tutorial, we’ll be implementing Inline Replies in our application
Android Nougat引入了几个新功能。 它提供了一些很棒的功能,例如内联回复操作和捆绑通知。 在本教程中,我们将在应用程序中实现内联回复
(Android Notification Direct Reply)
Inline Reply Actions (also known as Direct Replies) allows us to reply to messages from the notifications itself. It makes life easier by removing the need to open applications for providing input. Such features are commonly seen in messaging applications. Direct Replies uses a combination of Notification Actions and Remote Input. Remote Input API provides a mechanism to access the entered text from the notification in our application.
内联回复操作(也称为直接回复)使我们能够从通知本身中回复消息。 通过消除打开用于提供输入的应用程序的需求,它使生活更轻松。 这些功能通常在消息传递应用程序中看到。 直接答复使用通知动作和远程输入的组合。 远程输入API提供了一种从我们的应用程序中的通知中访问输入文本的机制。
RemoteInput requires the following Strings as input.
RemoteInput需要以下字符串作为输入。
- Unique Key: This is used to correctly identify the text entered from the inline notification later on. 唯一键 :此键用于正确识别以后从内联通知中输入的文本。
- Label: This is displayed to the user as a hint text. 标签 :这作为提示文本显示给用户。
Let’s implement a basic application that triggers a Notification with Inline Reply set as the Action.
让我们实现一个基本应用程序,该应用程序将内联回复设置为操作来触发通知。
(Android Notification Direct Reply Project Structure)
(Android Notification Direct Reply Code)
The code for the activity_main.xml
layout is given below.
下面给出了activity_main.xml
布局的代码。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.journaldev.directreplynotification.MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BASIC INLINE REPLY NOTIFICATION"
android:id="@+id/btn_basic_inline_reply"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txt_inline_reply"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Replied text will be displayed here"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/btn_basic_inline_reply"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginRight="8dp"
app:layout_constraintRight_toRightOf="parent" />
<Button
android:id="@+id/btn_inline_replies_with_history"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="INLINE REPLIES WITH HISTORY"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/txt_inline_reply" />
</android.support.constraint.ConstraintLayout>
Note: We’ll deal with the second Button later in the tutorial.
注意:我们将在本教程的后面部分处理第二个Button。
The code for the MainActivity.java
class is given below.
下面给出MainActivity.java
类的代码。
package com.journaldev.directreplynotification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
String KEY_REPLY = "key_reply";
public static final int NOTIFICATION_ID = 1;
Button btnBasicInlineReply;
TextView txtReplied;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
clearExistingNotifications();
btnBasicInlineReply = (Button) findViewById(R.id.btn_basic_inline_reply);
txtReplied = (TextView) findViewById(R.id.txt_inline_reply);
btnBasicInlineReply.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_basic_inline_reply:
//Create notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("Inline Reply Notification");
String replyLabel = "Enter your reply here";
//Initialise RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
.setLabel(replyLabel)
.build();
//PendingIntent that restarts the current activity instance.
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Notification Action with RemoteInput instance added.
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", resultPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
//Notification.Action instance added to Notification Builder.
builder.addAction(replyAction);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("notificationId", NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent dismissIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "DISMISS", dismissIntent);
//Create Notification.
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
builder.build());
break;
}
}
private void clearExistingNotifications()
{
int notificationId = getIntent().getIntExtra("notificationId", 0);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
}
Two Notification Actions namely “REPLY” and “DISMISS” are set on the notification. Clicking REPLY would trigger the Direct Reply feature. Running the above code on the emulator would give the following output.
在通知上设置了两个通知动作,即“ REPLY”和“ DISMISS”。 单击REPLY将触发直接回复功能。 在模拟器上运行上面的代码将提供以下输出。
In the above gif, clicking the send icon from the notification shows an indicator. This implies that the notification is waiting for an acknowledgment from the activity. We haven’t processed the replied text in our activity yet. The inline replied text is retrieved using the key set in the RemoteInput instance as shown in the updated code for MainActivity.java
below.
在上面的gif中,单击通知中的发送图标会显示一个指示器。 这意味着通知正在等待活动的确认。 我们尚未在活动中处理回复的文本。 内联回复文本是使用RemoteInput实例中的键集检索的,如下面MainActivity.java
的更新代码所示。
package com.journaldev.directreplynotification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
String KEY_REPLY = "key_reply";
public static final int NOTIFICATION_ID = 1;
Button btnBasicInlineReply;
TextView txtReplied;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBasicInlineReply = (Button) findViewById(R.id.btn_basic_inline_reply);
txtReplied = (TextView) findViewById(R.id.txt_inline_reply);
btnBasicInlineReply.setOnClickListener(this);
clearExistingNotifications()
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processInlineReply(intent);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_basic_inline_reply:
//Create notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("Inline Reply Notification");
String replyLabel = "Enter your reply here";
//Initialise RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
.setLabel(replyLabel)
.build();
int randomRequestCode = new Random().nextInt(54325);
//PendingIntent that restarts the current activity instance.
Intent resultIntent = new Intent(this, MainActivity.class);
//Set a unique request code for this pending intent
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, randomRequestCode, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Notification Action with RemoteInput instance added.
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", resultPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
//Notification.Action instance added to Notification Builder.
builder.addAction(replyAction);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("notificationId", NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent dismissIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "DISMISS", dismissIntent);
//Create Notification.
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
builder.build());
break;
}
}
private void clearExistingNotifications() {
int notificationId = getIntent().getIntExtra("notificationId", 0);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
private void processInlineReply(Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
String reply = remoteInput.getCharSequence(
KEY_REPLY).toString();
//Set the inline reply text in the TextView
txtReplied.setText("Reply is "+reply);
//Update the notification to show that the reply was received.
NotificationCompat.Builder repliedNotification =
new NotificationCompat.Builder(this)
.setSmallIcon(
android.R.drawable.stat_notify_chat)
.setContentText("Inline Reply received");
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
repliedNotification.build());
}
}
}
In the above code, onNewIntent is invoked when the reply key is pressed(Thanks to the Intent flags SINGLE_TOP and CLEAR_TOP).
在上面的代码中,当按下回复键时会调用onNewIntent(这要归功于Intent标志SINGLE_TOP和CLEAR_TOP)。
The method processInlineReply()
is where we fetch the text entered in the notification and set it in the TextView. The notification is then updated (with the same NOTIFICATION_ID as when created) to get rid of the progress indicator and display that the reply was received.
方法processInlineReply()
是我们获取通知中输入的文本并在TextView中进行设置的地方。 然后更新通知(使用与创建时相同的NOTIFICATION_ID)以摆脱进度指示器并显示已收到回复。
In another world, instead of updating the notification we could have cancelled the notification by using the below snippet.
在另一个世界中,我们可以通过使用以下代码片段取消通知,而不是更新通知。
//Cancel notification
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
//Remove this from the MainActivity.java
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
repliedNotification.build());
The updated code above would give the following output in the emulator.
上面更新的代码将在模拟器中提供以下输出。
Note: Multiple Inline Reply Actions can be added in a notification by adding multiple addAction(remoteInput)
instances on the builder instance. Though it’s recommended to have just one inline reply action button per notification.
注意:通过在构建器实例上添加多个addAction(remoteInput)
实例,可以在通知中添加多个内联回复动作。 尽管建议每个通知只有一个内联回复操作按钮。
(Notifications With Reply History)
We can display previous inline responses in the Notification too with the help of the method setRemoteInputHistory
.
我们也可以借助setRemoteInputHistory
方法在Notification中显示以前的内联响应。
In this application we’ll be storing the Charsequences returned in inline replies in the form of a LinkedList as shown below.
在此应用程序中,我们将以LinkedList的形式存储以内联回复形式返回的Charsequences,如下所示。
package com.journaldev.directreplynotification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
String KEY_REPLY = "key_reply";
String KEY_REPLY_HISTORY = "key_reply_history";
public static final int NOTIFICATION_ID = 1;
Button btnBasicInlineReply, btnInlineReplyHistory;
TextView txtReplied;
private static List<CharSequence> responseHistory = new LinkedList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBasicInlineReply = (Button) findViewById(R.id.btn_basic_inline_reply);
btnInlineReplyHistory = (Button) findViewById(R.id.btn_inline_replies_with_history);
txtReplied = (TextView) findViewById(R.id.txt_inline_reply);
btnBasicInlineReply.setOnClickListener(this);
btnInlineReplyHistory.setOnClickListener(this);
clearExistingNotifications();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processInlineReply(intent);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_basic_inline_reply:
createInlineNotification();
break;
case R.id.btn_inline_replies_with_history:
if (!responseHistory.isEmpty()) {
CharSequence[] history = new CharSequence[responseHistory.size()];
createInlineNotificationWithHistory(responseHistory.toArray(history));
} else {
createInlineNotificationWithHistory(null);
}
break;
}
}
private void clearExistingNotifications() {
int notificationId = getIntent().getIntExtra("notificationId", 0);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(notificationId);
}
private void processInlineReply(Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
CharSequence charSequence = remoteInput.getCharSequence(
KEY_REPLY);
if (charSequence != null) {
//Set the inline reply text in the TextView
String reply = charSequence.toString();
txtReplied.setText("Reply is " + reply);
//Update the notification to show that the reply was received.
NotificationCompat.Builder repliedNotification =
new NotificationCompat.Builder(this)
.setSmallIcon(
android.R.drawable.stat_notify_chat)
.setContentText("Inline Reply received");
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
repliedNotification.build());
/**Uncomment the below code to cancel the notification.
* Comment the above code too.
* **/
/*NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);*/
} else {
String reply = remoteInput.getCharSequence(KEY_REPLY_HISTORY).toString();
responseHistory.add(0, reply);
if (!responseHistory.isEmpty()) {
CharSequence[] history = new CharSequence[responseHistory.size()];
createInlineNotificationWithHistory(responseHistory.toArray(history));
} else {
createInlineNotificationWithHistory(null);
}
}
}
}
private void createInlineNotification() {
//Create notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("Inline Reply Notification");
String replyLabel = "Enter your reply here";
//Initialise RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY)
.setLabel(replyLabel)
.build();
int randomRequestCode = new Random().nextInt(54325);
//PendingIntent that restarts the current activity instance.
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
//Set a unique request code for this pending intent
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, randomRequestCode, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Notification Action with RemoteInput instance added.
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", resultPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
//Notification.Action instance added to Notification Builder.
builder.addAction(replyAction);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("notificationId", NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent dismissIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "DISMISS", dismissIntent);
//Create Notification.
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
builder.build());
}
private void createInlineNotificationWithHistory(CharSequence[] history) {
//Create notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle("Inline Reply Notification With History");
String replyLabel = "Enter your reply here";
//Initialise RemoteInput
RemoteInput remoteInput = new RemoteInput.Builder(KEY_REPLY_HISTORY)
.setLabel(replyLabel)
.build();
int randomRequestCode = new Random().nextInt(54325);
//PendingIntent that restarts the current activity instance.
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
//Set a unique request code for this pending intent
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, randomRequestCode, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//Notification Action with RemoteInput instance added.
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", resultPendingIntent)
.addRemoteInput(remoteInput)
.setAllowGeneratedReplies(true)
.build();
//Notification.Action instance added to Notification Builder.
builder.addAction(replyAction);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("notificationId", NOTIFICATION_ID);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent dismissIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel, "DISMISS", dismissIntent);
if (history != null) {
builder.setRemoteInputHistory(history);
}
//Create Notification.
NotificationManager notificationManager =
(NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID,
builder.build());
}
}
We’ve added a new Button to handle Inline Replies With History.
In the above code, responseHistory
holds the history of replies.
The following snippet is used to set the previous inline replies in the notification.
我们添加了一个新按钮来处理带历史记录的内联回复。
在上面的代码中, responseHistory
保存答复的历史记录。
以下代码段用于设置通知中的先前内联回复。
if (history != null) {
builder.setRemoteInputHistory(history);
}
The else
part of method processInlineReply() updates the current notification with the new history of inline replies as shown below:
方法processInlineReply()的else
部分使用新的内联回复历史记录更新当前通知,如下所示:
else {
String reply = remoteInput.getCharSequence(KEY_REPLY_HISTORY).toString();
responseHistory.add(0, reply);
if (!responseHistory.isEmpty()) {
CharSequence[] history = new CharSequence[responseHistory.size()];
createInlineNotificationWithHistory(responseHistory.toArray(history));
} else {
createInlineNotificationWithHistory(null);
}
}
responseHistory.add(0, reply);
adds the latest inline reply in the history of replies.
responseHistory.add(0, reply);
在回复历史记录中添加最新的内联回复。
The output that the above code would give is:
上面的代码将给出的输出是:
Note: The above concept is for demonstration purposes only. You can tweak it according to your requirements.
注意 :以上概念仅用于演示目的。 您可以根据需要进行调整。
Inline Replies do not work on versions prior to Android N. So to make the application functional on them, you can add a Basic EditText in the activity.
内联回复不适用于Android N之前的版本。因此,要使应用程序在其上起作用,可以在活动中添加基本EditText。
This brings an end to this tutorial. You can download the final Android DirectReplyNotifications Project from the link below.
本教程到此结束。 您可以从下面的链接下载最终的Android DirectReplyNotifications项目 。
Download Android Notification Direct Reply Project 下载Android通知直接回复项目
android通知悬浮通知