老版本的环信,实现消息撤回功能。

来源:互联网 发布:腾讯微信大数据 编辑:程序博客网 时间:2024/04/30 11:36

最近公司很久之前的一个集成过环信聊天功能的app,要增加消息撤回的功能,由于这个app是很久之前的。。所以,那个时候的easeUi里面并没有这个功能,于是就参照有这个功能的sdk,把代码搬了过来。。
Ps:当前的sdk版本是2.2.4

首先找到聊天页面ChatActivity页面,里面是一个EaseUi里面的EaseChatFragment,然后找到消息框点击事件,messageList有个item点击事件,(EaseChatMessageList.MessageListItemClickListener),有一系列事件,你可以写在消息框点击事件里(onBubbleClick),也可以写在消息框长按事件里面(onBubbleLongClick),我是选择了后者。

原理图:
这里写图片描述

参考链接:
http://docs.easemob.com/im/490integrationcases/revoke-messages

下面是效果图:
这里写图片描述

下面贴代码:

messageList.setItemClickListener(new EaseChatMessageList.MessageListItemClickListener() {            @Override            public void onUserAvatarClick(String username) {                if (chatFragmentListener != null) {                    chatFragmentListener.onAvatarClick(username);                }            }            @Override            public void onResendClick(final EMMessage message) {                new EaseAlertDialog(getActivity(), R.string.resend, R.string.confirm_resend, null, new AlertDialogUser() {                    @Override                    public void onResult(boolean confirmed, Bundle bundle) {                        if (!confirmed) {                            return;                        }                        resendMessage(message);                    }                }, true).show();            }            @Override            public void onBubbleLongClick(EMMessage message) {                contextMenuMessage = message;                //这个是用来控制撤回这个菜单是否显示                boolean isvisibilty = false;                /**                 * callback:这个用来判断是否已经是撤回的消息,                 * 在消息撤回以后,会发送一条新的消息,内容是XX                 * 撤回了一条消息(我暂且叫CallMessage)                 * 我在这个CallMessage中加了个扩展字段callback,来标识该条消息已经是                 * 撤回的消息了。即再长按该条消息不需要再显示撤回这个菜单了                 *                 */                boolean callback = false;                try {                    callback = message.getBooleanAttribute("callback");//如果已经是撤回的消息了,就不要显示撤回这个选项了                } catch (EaseMobException e) {                    e.printStackTrace();                }                /**                 * 这里需要判断下该条消息必须是自己发的,并且不是CallMessage                 * 才能显示撤回菜单                 */                if (message.getFrom().equals(user.getHuanxinId()) && !callback) {                    isvisibilty = true;                } else {                    isvisibilty = false;                }                //长按以后弹出一个选择框(包括复制消息,删除消息,和撤回消息)                startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class))                                .putExtra("message", message)                                .putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM)                                .putExtra("isvisibilty", isvisibilty),                        REQUEST_CODE_CONTEXT_MENU);            }            @Override            public boolean onBubbleClick(EMMessage message) {                if (chatFragmentListener != null) {                    return chatFragmentListener.onMessageBubbleClick(message);                }                return false;            }        });

选择框的样式:
这里写图片描述

下面是ContextMenuActivity.class(选择框)

import android.content.Intent;import android.os.Bundle;import android.view.MotionEvent;import android.view.View;import android.widget.TextView;import com.easemob.chat.EMMessage;import com.easemob.easeui.R;import com.easemob.easeui.utils.Constant;//import com.easemob.redpacketsdk.constant.RPConstant;//import com.hyphenate.chat.EMMessage;//import com.hyphenate.chatuidemo.Constant;//import com.hyphenate.chatuidemo.R;/** * 选择框页面,根据不同的type,来配置不同的布局,因为有的不需要有复制消息这个菜单 */public class ContextMenuActivity extends EaseBaseActivity {    public static final int RESULT_CODE_COPY = 1;    public static final int RESULT_CODE_DELETE = 2;    public static final int RESULT_CODE_FORWARD = 3;    public static final int RESULT_CODE_RECALL = 4;    private EMMessage message;    Intent intent;    boolean isVisibilty;//撤回是否可见    private TextView tvReCall;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        intent=getIntent();         message = intent.getParcelableExtra("message");        isVisibilty=intent.getBooleanExtra("isvisibilty",false);//      boolean isChatroom = getIntent().getBooleanExtra("ischatroom", false);        int type = message.getType().ordinal();        if (type == EMMessage.Type.TXT.ordinal()) {//          if(message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)//                  || message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false)//                  //red packet code : 屏蔽红包消息的转发功能//                  || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)){//                  //end of red packet code//              setContentView(R.layout.em_context_menu_for_location);//          }else            if(message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_BIG_EXPRESSION, false)){                setContentView(R.layout.em_context_menu_for_image);            }else{                setContentView(R.layout.em_context_menu_for_text);            }        } else if (type == EMMessage.Type.LOCATION.ordinal()) {            setContentView(R.layout.em_context_menu_for_location);        } else if (type == EMMessage.Type.IMAGE.ordinal()) {            setContentView(R.layout.em_context_menu_for_image);        } else if (type == EMMessage.Type.VOICE.ordinal()) {            setContentView(R.layout.em_context_menu_for_voice);        } else if (type == EMMessage.Type.VIDEO.ordinal()) {            setContentView(R.layout.em_context_menu_for_video);        } else if (type == EMMessage.Type.FILE.ordinal()) {            setContentView(R.layout.em_context_menu_for_location);        }        tvReCall= (TextView) findViewById(R.id.recall);        if(isVisibilty){            tvReCall.setVisibility(View.VISIBLE);        }else{            tvReCall.setVisibility(View.GONE);        }//      if (isChatroom//              //red packet code : 屏蔽红包消息的撤回功能//              || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)) {//              //end of red packet code//          View v = (View) findViewById(R.id.forward);//          if (v != null) {//              v.setVisibility(View.GONE);//          }//      }    }    @Override    public boolean onTouchEvent(MotionEvent event) {        finish();        return true;    }    public void copy(View view){        setResult(RESULT_CODE_COPY);        finish();    }    public void delete(View view){        setResult(RESULT_CODE_DELETE);        finish();    }    public void forward(View view){        setResult(RESULT_CODE_FORWARD);        finish();    }    public  void recall(View view){        setResult(RESULT_CODE_RECALL);        finish();    }}

下面是布局:em_context_menu_for_image:

<?xml version="1.0" encoding="UTF-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:layout_marginLeft="20dp"    android:layout_marginRight="20dp"    android:gravity="center_horizontal"    android:orientation="vertical" >    <!-- <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="1dp"        android:background="@drawable/em_context_menu_item_bg"        android:clickable="true"        android:gravity="center_vertical"        android:onClick="copy"        android:padding="10dp"        android:text="@string/copy"        android:textColor="@android:color/black"        android:textSize="20sp" /> -->    <View        android:layout_width="match_parent"        android:layout_height="1px"        android:background="@android:color/darker_gray" />    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:background="@drawable/em_context_menu_item_bg"        android:clickable="true"        android:gravity="center_vertical"        android:onClick="delete"        android:padding="10dp"        android:text="@string/delete"        android:textColor="@android:color/black"        android:textSize="20sp" />    <View        android:layout_width="match_parent"        android:layout_height="1px"        android:background="@android:color/darker_gray" />    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:id="@+id/forward"        android:background="@drawable/em_context_menu_item_bg"        android:clickable="true"        android:gravity="center_vertical"        android:onClick="forward"        android:padding="10dp"        android:text="@string/forward"        android:visibility="gone"        android:textColor="@android:color/black"        android:textSize="20sp" />    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:id="@+id/recall"        android:background="@drawable/em_context_menu_item_bg"        android:clickable="true"        android:gravity="center_vertical"        android:onClick="recall"        android:padding="10dp"        android:text="@string/recall"        android:textColor="@android:color/black"        android:textSize="20sp" /></LinearLayout>

其他的布局大同小异,就不一一贴出来了。
然后选择好是哪个菜单以后,选择框消失,回到聊天界面,
即EaseChatFragment的onActivityResult里面。。

if (requestCode == REQUEST_CODE_CONTEXT_MENU) {            switch (resultCode) {                case ContextMenuActivity.RESULT_CODE_COPY: // copy//                    MessageBody body = contextMenuMessage.getBody();                    EMMessage.Type type = contextMenuMessage.getType();                    if (type.equals(EMMessage.Type.TXT)) {                        TextMessageBody txtBody = (TextMessageBody) contextMenuMessage.getBody();                        clipboard.setText(txtBody.getMessage());                    }                    break;                case ContextMenuActivity.RESULT_CODE_DELETE: // delete                    conversation.removeMessage(contextMenuMessage.getMsgId());                    messageList.refresh();                    break;                case ContextMenuActivity.RESULT_CODE_FORWARD: // forward                    break;                case ContextMenuActivity.RESULT_CODE_RECALL://撤回                    recall();                    break;                default:                    break;            }        }

重点看下recall()方法,这个就是撤回功能。

 /**     * 撤回     * 原理:     * A用户发送消息。     * A用户需要撤回某条消息,将消息id通过扩展消息发送到用户B。     * B用户收到扩展消息,解析其中的messageid,从数据库删除对应消息。     */    private void recall() {//        sendTextMessage();        EMMessage cmdMsg = EMMessage.createSendMessage(EMMessage.Type.CMD);// 如果是群聊,设置chattype,默认是单聊        if (chatType == EaseConstant.CHATTYPE_GROUP) {            cmdMsg.setChatType(ChatType.GroupChat);        } else if (chatType == EaseConstant.CHATTYPE_CHATROOM) {            cmdMsg.setChatType(ChatType.ChatRoom);        }        String action = "REVOKE_FLAG";        CmdMessageBody cmdBody = new CmdMessageBody(action);// 设置消息body        cmdMsg.addBody(cmdBody);// 设置要发给谁,用户username或者群聊groupid        cmdMsg.setReceipt(toChatUsername);// 通过扩展字段添加要撤回消息的id        cmdMsg.setAttribute("msgId", contextMenuMessage.getMsgId());        cmdMsg.setAttribute("name", user.getName());        EMChatManager.getInstance().sendMessage(cmdMsg, new EMCallBack() {            @Override            public void onSuccess() {//                Log.e("zmm","发送成功-->");                /**                 * 注意这里一定要把需要撤回的消息删除,这里如果不删除,会出现,                 * 自己这边的聊天列表里面该条消息还在。                 */                //发送成功以后再这里把该撤回的消息删除,这里是为了刷新自己的聊天页面                conversation.removeMessage(contextMenuMessage.getMsgId());//                Log.e("zmm", "删除消息-->" + contextMenuMessage.getMsgId());                EMMessage message = EMMessage.createTxtSendMessage(user.getName() + "回撤一条消息",                        toChatUsername);                message.setAttribute("callback", true);                sendMessage(message, user.getName());            }            @Override            public void onProgress(int progress, String status) {            }            @Override            public void onError(int code, String error) {                Log.e("zmm", "发送失败-->" + code + "-->" + error);            }        });        messageList.refreshSelectLast();    }
 protected void sendMessage(EMMessage message, String name) {        if (chatFragmentListener != null) {            //设置扩展属性            chatFragmentListener.onSetMessageAttributes(message);        }        // 如果是群聊,设置chattype,默认是单聊        if (chatType == EaseConstant.CHATTYPE_GROUP) {            message.setChatType(ChatType.GroupChat);        } else if (chatType == EaseConstant.CHATTYPE_CHATROOM) {            message.setChatType(ChatType.ChatRoom);        }        message.setAttribute("name", name);//这里放入发送人的名字,在需要拿到发送者名字的时候再取出来。(XX)        //发送消息        EMChatManager.getInstance().sendMessage(message, null);        //刷新ui        messageList.refreshSelectLast();    }

然后就是在接收到该条CallMessage的地方处理下就可以了。
还是再EaseChatFragment这个页面,实现了EMEventListener这个接口,里有接收到新消息的回调:

{        EMMessage message = (EMMessage) event.getData();//        Log.e("zmm","onEvent-->"+event.getEvent());        switch (event.getEvent()) {            case EventNewMessage:                // 获取到message                String username = null;                // 群组消息                if (message.getChatType() == ChatType.GroupChat || message.getChatType() == ChatType.ChatRoom) {                    username = message.getTo();                } else {                    // 单聊消息                    username = message.getFrom();                }                // 如果是当前会话的消息,刷新聊天页面                if (username.equals(toChatUsername)) {                    messageList.refreshSelectLast();                    // 声音和震动提示有新消息                    EaseUI.getInstance().getNotifier().viberateAndPlayTone(message);                } else {                    // 如果消息不是和当前聊天ID的消息                    EaseUI.getInstance().getNotifier().onNewMsg(message);                }                break;            case EventDeliveryAck:            case EventReadAck:                // 获取到message                messageList.refresh();                break;            case EventOfflineMessage:                // a list of offline messages                // List<EMMessage> offlineMessages = (List<EMMessage>)                // event.getData();                messageList.refresh();                break;            case EventNewCMDMessage: // CMD消息(这个就是CallMessage返回的type)//                Log.e("zmm", "接收到回调消息-->");                CmdMessageBody cmdMsgBody = (CmdMessageBody) message.getBody();                String action = cmdMsgBody.action;//获取自定义action                String name = null;                try {                    name = message.getStringAttribute("name");                } catch (EaseMobException e) {                    Log.e("zmm", "-->" + e.toString() + "-->" + e.getMessage());                    e.printStackTrace();                }                if (action.equals("REVOKE_FLAG")) {                    try {                        String msgId = message.getStringAttribute("msgId");                        EMConversation conversation = EMChatManager.getInstance().getConversation(message.getFrom());//                        --删除消息来表示撤回--//                        Log.e("zmm",)                        conversation.removeMessage(msgId);//                        Log.e("zmm", "删除消息-->" + msgId);                        //删除消息以后增加一条XX撤回了一条消息。                        EMMessage newmessage = EMMessage.createTxtSendMessage(name + "回撤一条消息",                                toChatUsername);                        newmessage.setAttribute("callback", true);                        sendMessage(newmessage, name);                    } catch (EaseMobException e) {                        // TODO Auto-generated catch block                        Log.e("zmm", "1111-->" + e.toString() + "-->" + e.getMessage());                        e.printStackTrace();                    }                }                break;            default:                break;        }    }

以上就完成了消息撤回功能。。特此记录。望对大家有帮助。

最后献上一句最近喜欢的话:
最怕一生碌碌无为,还说难得平凡可贵。。。。
加油!!!

原创粉丝点击