前言
现在应用中即时通信的功能在app中应用的十分广泛,在即时通信这一块,公司在开发初期多数会选择第三方的库:环信/融云,特别是初创型公司的最爱,关于环信的基本使用,官方文档已经十分清晰了,下面我记录下我开发中的使用之:发送自定义消息—名片
先上效果图:
开发之路
集成easeui
easeui的集成,官方文档以及写得很详细了,这里不再多述。
easeui为我们提供了一个EaseChatFragment,我们可以直接使用这个EaseChatFragment,也可以自己写一个ChatFragment,继承EaseChatFragment并调用EaseChatFragmentHelper接口。这里我们为了方便,直接使用easeui提供的EaseChatFragment。
进入聊天页面
直接使用EaseChatFragment,通过Bundle,传入一系列参数(聊天模式,聊天对象)
//new出EaseChatFragment或其子类的实例
mChatFragment = new EaseChatFragment();
//传入参数
Bundle args = new Bundle();
//设置为单聊,否则单聊时,消息发送不成功
args.putInt(EaseConstant.EXTRA_CHAT_TYPE, EaseConstant.CHATTYPE_SINGLE);
userName = SPUtils.getString(this, SPUtils.KEY_USERNAME, "123");
//555,123是已经注册的环信账号
args.putString(EaseConstant.EXTRA_USER_ID, userName.equals("123")?"555":"123");
mChatFragment.setArguments(args);
replaceFragment(R.id.fl_container, mChatFragment);
发送扩展消息
根据官方文档,我们需要发送一个扩展消息,如下
//发名片(扩展消息)
EMMessage message = EMMessage.createTxtSendMessage("我的名片",userName.equals("123")?"555":"123");
//设置聊天模式为单聊
message.setChatType(EMMessage.ChatType.Chat);
//设置消息携带的一系列参数,为以后显示名片做准备
message.setAttribute("title", "title_title_title_title");
message.setAttribute("records", true);
message.setAttribute("icon", R.mipmap.ic_launcher);
message.setAttribute("senderid", userName.equals("123")?"555":"123");
message.setAttribute("text", "您的名片已经发送");
EMClient.getInstance().chatManager().sendMessage(message);
ToastUtil.showShort(getApplicationContext(),"名片发送成功");
消息布局
我们需要写一个接收消息的布局,写一个发送消息的布局。两个布局文件中的相对应的控件id要保持一致,方便之后代码中设置内容,这里只贴出发送消息的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingTop="13dp" >
<TextView
android:id="@+id/timestamp"
style="@style/chat_text_date_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:paddingLeft="20dp" >
<ImageView
android:id="@+id/iv_userhead"
android:layout_width="@dimen/size_avatar"
android:layout_height="@dimen/size_avatar"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="5dp"
android:background="@drawable/ease_default_avatar"
android:scaleType="fitXY" />
<LinearLayout
android:id="@+id/bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_toLeftOf="@id/iv_userhead"
android:layout_toRightOf="@+id/ll_loading"
android:background="@drawable/ease_chatto_bg" >
<ImageView
android:scaleType="fitXY"
android:id="@+id/ease_chat_item_share_img"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"
android:src="@drawable/ease_chat_item_file" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="7dp"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_title_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:ellipsize="middle"
android:singleLine="true"
android:textColor="#000"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/bubble"
android:visibility="visible" />
<ImageView
android:id="@+id/msg_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/bubble"
android:clickable="true"
android:src="@drawable/ease_msg_state_failed_resend"
android:visibility="invisible" />
<TextView
android:id="@+id/tv_ack"
style="@style/chat_text_name_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/tv_chatcontent"
android:text="@string/text_ack_msg"
android:textSize="12sp"
android:visibility="invisible" />
<TextView
android:id="@+id/tv_delivered"
style="@style/chat_text_name_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/tv_chatcontent"
android:text="@string/text_delivered_msg"
android:textSize="12sp"
android:visibility="invisible" />
</RelativeLayout>
</LinearLayout>
自定义消息内容
有了消息布局,我们需要在发送和接收时,控制消息内容,easeui提供了EaseChatRow,我们只需要继承这个类,就可以在消息发送和接收时,控制消息体:在onInflateView()方法中加载布局,在onFindViewById()方法中获取控件,在onSetUpView()中改变展示内容,在onBubbleClick()方法中写消息被点击时的逻辑
public class EaseChatRowCard extends EaseChatRow {
ImageView img;//图片
TextView title;//title
private TextView text;
public EaseChatRowCard(Context context, EMMessage message, int position, BaseAdapter adapter) {
super(context, message, position, adapter);
}
/**
* 加载布局
*/
@Override
protected void onInflatView() {
inflater.inflate(message.direct() == EMMessage.Direct.RECEIVE ?
R.layout.ease_row_received_card : R.layout.ease_row_sent_card, this);
}
/**
* 实例化控件
*/
@Override
protected void onFindViewById() {
title = (TextView) findViewById(R.id.tv_title_name);
img = (ImageView) findViewById(R.id.ease_chat_item_share_img);
text = (TextView) findViewById(R.id.timestamp);
}
/**
* 更新适配器
*/
@Override
protected void onUpdateView() {
adapter.notifyDataSetChanged();
}
/**
* 设置内容
*/
@Override
protected void onSetUpView() {
try {
String titleString = message.getStringAttribute("title");
title.setText(titleString);
img.setImageResource(message.getIntAttribute("icon",R.drawable.ic_launcher));
//消息状态变化
handleTextMessage();
} catch (HyphenateException e) {
e.printStackTrace();
}
}
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() == EMMessage.ChatType.Chat){
try {
EMClient.getInstance().chatManager().ackMessageRead(message.getFrom(), message.getMsgId());
} catch (HyphenateException e) {
e.printStackTrace();
}
}
}
}
@Override
protected void onBubbleClick() {
Toast.makeText(context, "点击了", Toast.LENGTH_SHORT).show();
}
}
展示消息—EaseMessageAdapter
消息体已经写好了,那么在消息发送和接收时,我们还需要一个适配器去展示消息。这就需要easeui中的一个重要的类—EaseMessageAdapter。
在EaseMessageAdapter中的getViewTypeCount()方法中将默认的14种方法,改成16(包括自定义接收消息和发送消息)
public int getViewTypeCount() {
if(customRowProvider != null && customRowProvider.getCustomChatRowTypeCount() > 0){
//14改成16
return customRowProvider.getCustomChatRowTypeCount() + 16;
}
//14改成16
return 16;
}
在getItemViewType(int position)方法中,增加消息类型:
public int getItemViewType(int position) {
EMMessage message = getItem(position);
if (message == null) {
return -1;
}
if(customRowProvider != null && customRowProvider.getCustomChatRowType(message) > 0){
//13改成15
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;
}else if (message.getBooleanAttribute("records", false)){//自定义消息扩展
return message.direct() == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_EXTENDS : MESSAGE_TYPE_SEND_EXTENDS;
}
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
}
之前我们写一个自定义的ChatRow,我们需要在EaseMessageAdapter中的createChatRow方法中创建方法体
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 (message.getBooleanAttribute("records", false)){//自定义消息扩展
chatRow = new EaseChatRowShare(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;
}
消息刷新
以上基本可以完成发送名片的功能,存在一个小缺陷,消息在发送时,未刷新,所以我们需要手动刷新,在EaseChatFragment可以获取EaseChatMessageList进行刷新。
在EaseChatFragment写一个获取EaseChatMessageList 的方法
public EaseChatMessageList getMessageList(){
return messageList;
}
在发送自定义消息后,进行刷新消息
...
mChatFragment.getMessageList().refresh();//刷新数据,否则发送时,无法接收到
ToastUtil.showShort(getApplicationContext(),"名片发送成功");
以上就完成了集成easeui后进行发送名片