在即時通訊 IM 場景中,“正在輸入”狀態提示是一個提升交互體驗的核心基礎功能之一。它通過在聊天界面實時顯示“對方正在輸入…”,為用戶創造了一種動態的、臨場式的對話感知,進一步增強了溝通的實時性和沉浸感。

本文將詳細解析“正在輸入”狀態的技術原理,并以環信IM SDK實戰為例,手把手帶你構建一套可靠的“正在輸入”狀態解決方案。

技術原理

輸入狀態提示功能基于環信 SDK 的 透傳消息消息類型機制實現,工作流程如下:
在這里插入圖片描述

  1. 監聽輸入狀態:監聽用戶 A 的輸入狀態,檢測到用戶開始輸入時,觸發輸入狀態發送邏輯。

  2. 發送透傳消息:每隔固定時間間隔(默認 5 秒),通過透傳消息將輸入狀態發送給用戶 B,通知其開始輸入文本。

  3. 接收與處理消息:用戶 B 收到透傳消息后,判斷當前是否在與用戶 A 聊天的頁面,若是則顯示用戶 A 的輸入狀態。

  4. 自動隱藏輸入狀態提示:若用戶 B 在指定時間內未收到新的輸入狀態消息,則自動隱藏輸入狀態提示。

  1. 透傳消息是一種特殊類型消息,收發雙方不會存數據庫。在本方案中,透傳消息只發送給在線用戶。

  2. 消息發送方可根據需要設置透傳消息發送間隔。

實現過程

本文以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“正在輸入”狀態提示功能。如遇到其他問題,請至 環信官網 聯系技術支持咨詢。

參考文檔: