在即时通讯 IM 场景中,“正在输入”状态提示是一个提升交互体验的核心基础功能之一。它通过在聊天界面实时显示“对方正在输入…”,为用户创造了一种动态的、临场式的对话感知,进一步增强了沟通的实时性和沉浸感。
本文将详细解析“正在输入”状态的技术原理,并以环信IM SDK实战为例,手把手带你构建一套可靠的“正在输入”状态解决方案。
技术原理
输入状态提示功能基于环信 SDK 的 透传消息消息类型机制实现,工作流程如下:
- 监听输入状态:监听用户 A 的输入状态,检测到用户开始输入时,触发输入状态发送逻辑。
- 发送透传消息:每隔固定时间间隔(默认 5 秒),通过透传消息将输入状态发送给用户 B,通知其开始输入文本。
- 接收与处理消息:用户 B 收到透传消息后,判断当前是否在与用户 A 聊天的页面,若是则显示用户 A 的输入状态。
- 自动隐藏输入状态提示:若用户 B 在指定时间内未收到新的输入状态消息,则自动隐藏输入状态提示。
- 透传消息是一种特殊类型消息,收发双方不会存数据库。在本方案中,透传消息只发送给在线用户。
- 消息发送方可根据需要设置透传消息发送间隔。
实现过程
本文以Android端代码为例,其他端请访问环信文档。
前提条件
接下来实现“对方正在输入...”
发送输入状态的透传消息
当用户在输入框中输入文字时,定期发送包含输入状态的透传消息:
// 设置透传消息发送间隔。默认为 5000 毫秒,即 5 秒。
private static final int INPUT_STATUS_INTERVAL = 5000;
private long lastSendTime = 0;
// 假设 editText 是你的输入框
EditText editText = findViewById(R.id.edit_text);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
long currentTime = System.currentTimeMillis();
// 限制发送频率
if (currentTime - lastSendTime > INPUT_STATUS_INTERVAL) {
sendInputStatusMessage();
lastSendTime = currentTime;
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void afterTextChanged(Editable s) {}
});
private void sendInputStatusMessage() {
String action = "input_status";
// 创建透传消息
EMMessage cmdMsg = EMMessage.createSendMessage(EMMessage.Type.CMD);
EMCmdMessageBody cmdBody = new EMCmdMessageBody(action);
// 将该透传消息只发送给在线用户
cmdBody.deliverOnlineOnly(true);
cmdMsg.addBody(cmdBody);
// 设置接收方(toChatUsername 为当前聊天对象的环信用户 ID)
cmdMsg.setTo(toChatUsername);
cmdMsg.setChatType(EMMessage.ChatType.Chat);
// 发送消息
EMClient.getInstance().chatManager().sendMessage(cmdMsg);
}
接收和解析透传消息
收到透传消息后,用户判断是否为输入状态消息,并在 UI 上进行相应提示。收到输入状态后,若在一定时间间隔内未再次收到,则自动隐藏提示。
// 用于处理自动隐藏输入状态的 Handler
private Handler inputStatusHandler = new Handler(Looper.getMainLooper());
private Runnable hideInputStatusRunnable = new Runnable() {
@Override
public void run() {
// 隐藏"对方正在输入..."提示
// 例如:inputStatusTextView.setVisibility(View.GONE);
}
};
EMMessageListener msgListener = new EMMessageListener() {
@Override
public void onCmdMessageReceived(List<EMMessage> messages) {
for (EMMessage message : messages) {
EMCmdMessageBody cmdBody = (EMCmdMessageBody) message.getBody();
String action = cmdBody.action();
if ("input_status".equals(action)) {
// 使用 Handler 在主线程更新 UI
inputStatusHandler.post(new Runnable() {
@Override
public void run() {
// 显示"对方正在输入..."提示
// 例如:inputStatusTextView.setVisibility(View.VISIBLE);
// 例如:inputStatusTextView.setText("对方正在输入...");
}
});
// 移除之前的自动隐藏任务,重新计时
inputStatusHandler.removeCallbacks(hideInputStatusRunnable);
// 5 秒后自动隐藏输入状态提示
inputStatusHandler.postDelayed(hideInputStatusRunnable, 5000);
}
}
}
// 其他回调方法...
@Override
public void onMessageReceived(List<EMMessage> messages) {}
@Override
public void onMessageRead(List<EMMessage> messages) {}
@Override
public void onMessageDelivered(List<EMMessage> messages) {}
@Override
public void onMessageRecalledWithExt(List<EMRecallMessageInfo> recallMessageInfo) {}
@Override
public void onMessageChanged(EMMessage message, Object change) {}
};
EMClient.getInstance().chatManager().addMessageListener(msgListener);
注意事项
为避免内存泄漏,务必在 Activity 销毁时移除已注册的监听器,并且清理 Handler 中的回调任务。
// 在 Activity 中
@Override
protected void onDestroy() {
// 移除消息监听器
if (msgListener != null) {
EMClient.getInstance().chatManager().removeMessageListener(msgListener);
}
// 清理 Handler 回调(针对输入状态提示功能)
if (inputStatusHandler != null) {
inputStatusHandler.removeCallbacksAndMessages(null);
}
super.onDestroy();
}
本次基于环信SDK的实战开发,我们实现了IM“正在输入”状态提示功能。如遇到其他问题,请至 环信官网 联系技术支持咨询。