之前的工作开发中用到环信,感觉写的非常好,尤其是demohelper这个帮助类写的非常的全面,就是集成的时候比较麻烦
现在先说一下自定义消息拓展这一块。
以为工作中用到的消息类型,环信是没有提供的,也无法针对每一个用户提供消息类型,这就需要通过自定义消息拓展来实现自己想要的消息类型。直接说我开法的步骤
一.创建ChatFragment,继承EaseChatFragment并调用EaseChatFragmentHelper这个接口。
1.在registerExtendMenuItem()这个方法中设置拓展消息图标、名称、itemId、监听
2.在onExtendMenuItemClick()这个方法中修改扩展菜单项点击,如果你想覆盖,返回true。
3.如果是跳转页面后返回数据就在onActivityResult()中编辑
代码如下:
import java.util.Map;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.chat.EMTextMessageBody;
import com.hyphenate.chatuidemo.Constant;
import com.hyphenate.chatuidemo.DemoHelper;
import com.hyphenate.chatuidemo.domain.EmojiconExampleGroupData;
import com.hyphenate.chatuidemo.domain.RobotUser;
import com.hyphenate.easeui.EaseConstant;
import com.hyphenate.easeui.ui.EaseChatFragment;
import com.hyphenate.easeui.ui.EaseChatFragment.EaseChatFragmentHelper;
import com.hyphenate.easeui.widget.chatrow.EaseCustomChatRowProvider;
import com.hyphenate.easeui.widget.emojicon.EaseEmojiconMenu;
import com.hyphenate.util.EasyUtils;
import com.tyk.hxtext.R;
import android.app.Activity;
import android.content.ClipData;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
public class ChatFragment extends EaseChatFragment implements EaseChatFragmentHelper{
private static final int ITEM_VIDEO = 11;
private static final int ITEM_FILE = 12;
private static final int ITEM_VOICE_CALL = 13;
private static final int ITEM_VIDEO_CALL = 14;
private static final int ITEM_PROJECT=20;
private static final int REQUEST_CODE_SELECT_VIDEO = 11;
private static final int REQUEST_CODE_SELECT_FILE = 12;
private static final int REQUEST_CODE_GROUP_DETAIL = 13;
private static final int REQUEST_CODE_CONTEXT_MENU = 14;
private static final int REQUEST_CODE_SELECT_AT_USER = 15;
private static final int REQUEST_CODE_SELECT=20;
private static final int MESSAGE_TYPE_SENT_VOICE_CALL = 1;
private static final int MESSAGE_TYPE_RECV_VOICE_CALL = 2;
private static final int MESSAGE_TYPE_SENT_VIDEO_CALL = 3;
private static final int MESSAGE_TYPE_RECV_VIDEO_CALL = 4;
/**
* if it is chatBot
*/
private boolean isRobot;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// TODO Auto-generated method stub
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
protected void setUpView() {
setChatFragmentListener(this);
if (chatType == Constant.CHATTYPE_SINGLE) {
Map<String,RobotUser> robotMap = DemoHelper.getInstance().getRobotList();
if(robotMap!=null && robotMap.containsKey(toChatUsername)){
isRobot = true;
}
}
super.setUpView();
// set click listener
((EaseEmojiconMenu)inputMenu.getEmojiconMenu()).addEmojiconGroup(EmojiconExampleGroupData.getData());
}
//3.获取拓展消息返回值,并做后续操作
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
switch (resultCode) {
case ContextMenuActivity.RESULT_CODE_COPY: // copy
clipboard.setPrimaryClip(ClipData.newPlainText(null,
((EMTextMessageBody) contextMenuMessage.getBody()).getMessage()));
break;
case ContextMenuActivity.RESULT_CODE_DELETE: // delete
conversation.removeMessage(contextMenuMessage.getMsgId());
messageList.refresh();
break;
case ContextMenuActivity.RESULT_CODE_FORWARD: // forward
// Intent intent = new Intent(getActivity(), ForwardMessageActivity.class);
// intent.putExtra("forward_msg_id", contextMenuMessage.getMsgId());
// startActivity(intent);
break;
default:
break;
}
}
if (resultCode==Activity.RESULT_OK) {
switch (requestCode) {
case REQUEST_CODE_SELECT_FILE: //send the file
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
sendFileByUri(uri);
//sendFileMessage("");
}
}
break;
default:
break;
}
}
}
/**
* 注册拓展消息
*/
//1.设置拓展消息图标、名称、itemId、监听
@Override
protected void registerExtendMenuItem() {
// TODO Auto-generated method stub
super.registerExtendMenuItem();
inputMenu.registerExtendMenuItem("文件", R.drawable.em_chat_file_selector, ITEM_FILE, extendMenuItemClickListener);
inputMenu.registerExtendMenuItem("项目", R.drawable.ic_launcher, ITEM_PROJECT, extendMenuItemClickListener);
}
/**
* 设置消息属性
*/
@Override
public void onSetMessageAttributes(EMMessage message) {
// TODO Auto-generated method stub
}
/**
* 进入聊天细节
*/
@Override
public void onEnterToChatDetails() {
// TODO Auto-generated method stub
}
/**
* 点击头像
*/
@Override
public void onAvatarClick(String username) {
// TODO Auto-generated method stub
}
/**
* 长按头像
*/
@Override
public void onAvatarLongClick(String username) {
// TODO Auto-generated method stub
}
/**
* 点击气泡
*/
@Override
public boolean onMessageBubbleClick(EMMessage message) {
// TODO Auto-generated method stub
return false;
}
/**
* 长按气泡
*/
@Override
public void onMessageBubbleLongClick(EMMessage message) {
// TODO Auto-generated method stub
startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class)).putExtra("message",message)
.putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM),
REQUEST_CODE_CONTEXT_MENU);
}
/**
* 扩展菜单项点击,如果你想覆盖,返回true
*/
//2.设置拓展消息点击事件
@Override
public boolean onExtendMenuItemClick(int itemId, View view) {
// TODO Auto-generated method stub
switch (itemId) {
case ITEM_FILE:
selectFileFromLocal();
break;
case ITEM_PROJECT:
sendProject();
break;
default:
break;
}
return false;
}
private void sendProject() {
// TODO Auto-generated method stub
EMMessage message = EMMessage.createTxtSendMessage("project", toChatUsername);
// 增加自己特定的属性
message.setAttribute("type", "project");
sendMessage(message);
}
/**
* select file
*/
protected void selectFileFromLocal() {
Intent intent = null;
if (Build.VERSION.SDK_INT < 19) { //api 19 and later, we can't use this way, demo just select from images
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
} else {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(intent, REQUEST_CODE_SELECT_FILE);
}
/**
* 设置自定义的聊天行提供者
*/
@Override
public EaseCustomChatRowProvider onSetCustomChatRowProvider() {
// TODO Auto-generated method stub
return null;
}
}
二.创建ChatActivity,并将ChatFragment注入到ChatActivity中
chatFragment = new ChatFragment();
//pass parameters to chat fragment
chatFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(R.id.container, chatFragment).commit();
三.重点来了。想要创建一个自己想要的消息类型,在环信中是将消息类型“模块化”实现的。本人也是采取这种方式直接修改的ui库。
创建MyEaseChatRowProject继承EaseChatRow,创建出来后回自动创建构造方法
1.注入布局。在onInflateView()中注入事先写好的chatrow布局。(可以直接复制其他的chatrow布局,然后进行修改)
2.在onFindViewById()中把控件的id初始化出来
3.在onSetUpView()中添加显示消息和位置等属性
4.在onBubbleClick()中添加气泡的点击事件
5.在onUpdateView()中刷新列表视图状态更改时消息
代码如下
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.chat.EMMessage.ChatType;
import com.hyphenate.easeui.R;
import com.hyphenate.exceptions.HyphenateException;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
public class MyEaseChatRowProject extends EaseChatRow {
private LinearLayout ll_mychatlist_xiangmu;
private TextView tv_mychatlist_xiangmuname;
private ImageView iv_mychatlist_xiangmuice;
private TextView tv_mychatlist_xiangmucontent;
public MyEaseChatRowProject(Context context, EMMessage message, int position, BaseAdapter adapter) {
super(context, message, position, adapter);
// TODO Auto-generated constructor stub
}
/**
* 注入布局
*/
@Override
protected void onInflateView() {
// TODO Auto-generated method stub
inflater.inflate(message.direct() == EMMessage.Direct.RECEIVE ? R.layout.ease_row_received_project
: R.layout.ease_row_sent_project, this);
}
/**
* 寻找id
*/
@Override
protected void onFindViewById() {
// TODO Auto-generated method stub
ll_mychatlist_xiangmu = (LinearLayout) findViewById(R.id.ll_mychatlist_xiangmu);
tv_mychatlist_xiangmuname = (TextView) findViewById(R.id.tv_mychatlist_xiangmuname);
iv_mychatlist_xiangmuice = (ImageView) findViewById(R.id.iv_mychatlist_xiangmuice);
tv_mychatlist_xiangmucontent = (TextView) findViewById(R.id.tv_mychatlist_xiangmucontent);
}
/**
* 刷新列表视图状态更改时消息
*/
@Override
protected void onUpdateView() {
// TODO Auto-generated method stub
adapter.notifyDataSetChanged();
Log.e("onUpdateView", "刷新了");
}
/**
* 显示消息和位置等属性
*/
@Override
protected void onSetUpView() {
// TODO Auto-generated method stub
// 设置内容,通过扩展自文本获取消息内容,填充到相应的位置
tv_mychatlist_xiangmuname.setText("测试的项目");
tv_mychatlist_xiangmucontent.setText("测试项目的内容");
handleTextMessage();
}
protected void handleTextMessage() {
if (message.direct() == EMMessage.Direct.SEND) {
setMessageSendCallback();
switch (message.status()) {
case CREATE:
progressBar.setVisibility(View.GONE);
statusView.setVisibility(View.VISIBLE);
break;
case SUCCESS:
progressBar.setVisibility(View.GONE);
statusView.setVisibility(View.GONE);
break;
case FAIL:
progressBar.setVisibility(View.GONE);
statusView.setVisibility(View.VISIBLE);
break;
case INPROGRESS:
progressBar.setVisibility(View.VISIBLE);
statusView.setVisibility(View.GONE);
break;
default:
break;
}
}else{
if(!message.isAcked() && message.getChatType() == ChatType.Chat){
try {
EMClient.getInstance().chatManager().ackMessageRead(message.getFrom(), message.getMsgId());
} catch (HyphenateException e) {
e.printStackTrace();
}
}
}
}
/**
* 点击气泡
*/
@Override
protected void onBubbleClick() {
// TODO Auto-generated method stub
Toast.makeText(activity, "点击了项目", Toast.LENGTH_SHORT).show();
}
}
四.修改EaseMessageAdapter适配器
1.添加消息类型
private static final int MESSAGE_TYPE_RECV_PROJECT = 14;
private static final int MESSAGE_TYPE_SENT_PROJECT = 15;
2.修改getViewTypeCount()
/**
* get number of message type, here 16 = (EMMessage.Type) * 2
*/
public int getViewTypeCount() {
if(customRowProvider != null && customRowProvider.getCustomChatRowTypeCount() > 0){
return customRowProvider.getCustomChatRowTypeCount() + 16;
}
return 16;
}
3.将判断chatrow的消息类型添加到getItemViewType()和createChatRow()中
代码如下:
package com.hyphenate.easeui.adapter;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import com.hyphenate.chat.EMClient;
import com.hyphenate.chat.EMConversation;
import com.hyphenate.chat.EMMessage;
import com.hyphenate.easeui.EaseConstant;
import com.hyphenate.easeui.utils.EaseCommonUtils;
import com.hyphenate.easeui.widget.EaseChatMessageList.MessageListItemClickListener;
import com.hyphenate.easeui.widget.chatrow.EaseChatRow;
import com.hyphenate.easeui.widget.chatrow.EaseChatRowBigExpression;
import com.hyphenate.easeui.widget.chatrow.EaseChatRowFile;
import com.hyphenate.easeui.widget.chatrow.EaseChatRowImage;
import com.hyphenate.easeui.widget.chatrow.EaseChatRowLocation;
import com.hyphenate.easeui.widget.chatrow.EaseChatRowText;
import com.hyphenate.easeui.widget.chatrow.EaseChatRowVideo;
import com.hyphenate.easeui.widget.chatrow.EaseChatRowVoice;
import com.hyphenate.easeui.widget.chatrow.EaseCustomChatRowProvider;
import com.hyphenate.easeui.widget.chatrow.MyEaseChatRowProject;
public class EaseMessageAdapter extends BaseAdapter{
private final static String TAG = "msg";
private Context context;
private static final int HANDLER_MESSAGE_REFRESH_LIST = 0;
private static final int HANDLER_MESSAGE_SELECT_LAST = 1;
private static final int HANDLER_MESSAGE_SEEK_TO = 2;
private static final int MESSAGE_TYPE_RECV_TXT = 0;
private static final int MESSAGE_TYPE_SENT_TXT = 1;
private static final int MESSAGE_TYPE_SENT_IMAGE = 2;
private static final int MESSAGE_TYPE_SENT_LOCATION = 3;
private static final int MESSAGE_TYPE_RECV_LOCATION = 4;
private static final int MESSAGE_TYPE_RECV_IMAGE = 5;
private static final int MESSAGE_TYPE_SENT_VOICE = 6;
private static final int MESSAGE_TYPE_RECV_VOICE = 7;
private static final int MESSAGE_TYPE_SENT_VIDEO = 8;
private static final int MESSAGE_TYPE_RECV_VIDEO = 9;
private static final int MESSAGE_TYPE_SENT_FILE = 10;
private static final int MESSAGE_TYPE_RECV_FILE = 11;
private static final int MESSAGE_TYPE_SENT_EXPRESSION = 12;
private static final int MESSAGE_TYPE_RECV_EXPRESSION = 13;
private static final int MESSAGE_TYPE_RECV_PROJECT = 14;
private static final int MESSAGE_TYPE_SENT_PROJECT = 15;
public int itemTypeCount;
// reference to conversation object in chatsdk
private EMConversation conversation;
EMMessage[] messages = null;
private String toChatUsername;
private MessageListItemClickListener itemClickListener;
private EaseCustomChatRowProvider customRowProvider;
private boolean showUserNick;
private boolean showAvatar;
private Drawable myBubbleBg;
private Drawable otherBuddleBg;
private ListView listView;
public EaseMessageAdapter(Context context, String username, int chatType, ListView listView) {
this.context = context;
this.listView = listView;
toChatUsername = username;
this.conversation = EMClient.getInstance().chatManager().getConversation(username, EaseCommonUtils.getConversationType(chatType), true);
}
Handler handler = new Handler() {
private void refreshList() {
// you should not call getAllMessages() in UI thread
// otherwise there is problem when refreshing UI and there is new message arrive
java.util.List<EMMessage> var = conversation.getAllMessages();
messages = var.toArray(new EMMessage[var.size()]);
conversation.markAllMessagesAsRead();
notifyDataSetChanged();
}
@Override
public void handleMessage(android.os.Message message) {
switch (message.what) {
case HANDLER_MESSAGE_REFRESH_LIST:
refreshList();
break;
case HANDLER_MESSAGE_SELECT_LAST:
if (messages.length > 0) {
listView.setSelection(messages.length - 1);
}
break;
case HANDLER_MESSAGE_SEEK_TO:
int position = message.arg1;
listView.setSelection(position);
break;
default:
break;
}
}
};
public void refresh() {
if (handler.hasMessages(HANDLER_MESSAGE_REFRESH_LIST)) {
return;
}
android.os.Message msg = handler.obtainMessage(HANDLER_MESSAGE_REFRESH_LIST);
handler.sendMessage(msg);
}
/**
* refresh and select the last
*/
public void refreshSelectLast() {
final int TIME_DELAY_REFRESH_SELECT_LAST = 100;
handler.removeMessages(HANDLER_MESSAGE_REFRESH_LIST);
handler.removeMessages(HANDLER_MESSAGE_SELECT_LAST);
handler.sendEmptyMessageDelayed(HANDLER_MESSAGE_REFRESH_LIST, TIME_DELAY_REFRESH_SELECT_LAST);
handler.sendEmptyMessageDelayed(HANDLER_MESSAGE_SELECT_LAST, TIME_DELAY_REFRESH_SELECT_LAST);
}
/**
* refresh and seek to the position
*/
public void refreshSeekTo(int position) {
handler.sendMessage(handler.obtainMessage(HANDLER_MESSAGE_REFRESH_LIST));
android.os.Message msg = handler.obtainMessage(HANDLER_MESSAGE_SEEK_TO);
msg.arg1 = position;
handler.sendMessage(msg);
}
public EMMessage getItem(int position) {
if (messages != null && position < messages.length) {
return messages[position];
}
return null;
}
public long getItemId(int position) {
return position;
}
/**
* get count of messages
*/
public int getCount() {
return messages == null ? 0 : messages.length;
}
/**
* get number of message type, here 16 = (EMMessage.Type) * 2
*/
public int getViewTypeCount() {
if(customRowProvider != null && customRowProvider.getCustomChatRowTypeCount() > 0){
return customRowProvider.getCustomChatRowTypeCount() + 16;
}
return 16;
}
/**
* get type of item
*/
public int getItemViewType(int position) {
EMMessage message = getItem(position);
if (message == null) {
return -1;
}
if(customRowProvider != null && customRowProvider.getCustomChatRowType(message) > 0){
return customRowProvider.getCustomChatRowType(message) + 15;
}
if (message.getType() == EMMessage.Type.TXT) {
if(message.getBooleanAttribute(EaseConstant.MESSAGE_ATTR_IS_BIG_EXPRESSION, false)){
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_EXPRESSION : MESSAGE_TYPE_SENT_EXPRESSION;
}
if ("project".equals(message.getStringAttribute("type", ""))) {
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_PROJECT : MESSAGE_TYPE_SENT_PROJECT;
}
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_TXT : MESSAGE_TYPE_SENT_TXT;
}
if (message.getType() == EMMessage.Type.IMAGE) {
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_IMAGE : MESSAGE_TYPE_SENT_IMAGE;
}
if (message.getType() == EMMessage.Type.LOCATION) {
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_LOCATION : MESSAGE_TYPE_SENT_LOCATION;
}
if (message.getType() == EMMessage.Type.VOICE) {
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VOICE : MESSAGE_TYPE_SENT_VOICE;
}
if (message.getType() == EMMessage.Type.VIDEO) {
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VIDEO : MESSAGE_TYPE_SENT_VIDEO;
}
if (message.getType() == EMMessage.Type.FILE) {
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_FILE : MESSAGE_TYPE_SENT_FILE;
}
return -1;// invalid
}
protected EaseChatRow createChatRow(Context context, EMMessage message, int position) {
EaseChatRow chatRow = null;
if(customRowProvider != null && customRowProvider.getCustomChatRow(message, position, this) != null){
return customRowProvider.getCustomChatRow(message, position, this);
}
switch (message.getType()) {
case TXT:
if(message.getBooleanAttribute(EaseConstant.MESSAGE_ATTR_IS_BIG_EXPRESSION, false)){
chatRow = new EaseChatRowBigExpression(context, message, position, this);
}else if ("project".equals(message.getStringAttribute("type", ""))) {
chatRow=new MyEaseChatRowProject(context, message, position, this);
}else{
chatRow = new EaseChatRowText(context, message, position, this);
}
break;
case LOCATION:
chatRow = new EaseChatRowLocation(context, message, position, this);
break;
case FILE:
chatRow = new EaseChatRowFile(context, message, position, this);
break;
case IMAGE:
chatRow = new EaseChatRowImage(context, message, position, this);
break;
case VOICE:
chatRow = new EaseChatRowVoice(context, message, position, this);
break;
case VIDEO:
chatRow = new EaseChatRowVideo(context, message, position, this);
break;
default:
break;
}
return chatRow;
}
@SuppressLint("NewApi")
public View getView(final int position, View convertView, ViewGroup parent) {
EMMessage message = getItem(position);
if(convertView == null){
convertView = createChatRow(context, message, position);
}
//refresh ui with messages
((EaseChatRow)convertView).setUpView(message, position, itemClickListener);
return convertView;
}
public String getToChatUsername(){
return toChatUsername;
}
public void setShowUserNick(boolean showUserNick) {
this.showUserNick = showUserNick;
}
public void setShowAvatar(boolean showAvatar) {
this.showAvatar = showAvatar;
}
public void setMyBubbleBg(Drawable myBubbleBg) {
this.myBubbleBg = myBubbleBg;
}
public void setOtherBuddleBg(Drawable otherBuddleBg) {
this.otherBuddleBg = otherBuddleBg;
}
public void setItemClickListener(MessageListItemClickListener listener){
itemClickListener = listener;
}
public void setCustomChatRowProvider(EaseCustomChatRowProvider rowProvider){
customRowProvider = rowProvider;
}
public boolean isShowUserNick() {
return showUserNick;
}
public boolean isShowAvatar() {
return showAvatar;
}
public Drawable getMyBubbleBg() {
return myBubbleBg;
}
public Drawable getOtherBuddleBg() {
return otherBuddleBg;
}
}
之后就大功告成。可能有一些细节需要修改,根据自己的设定修改即可