之前寫過一個(gè)即時(shí)通訊的項(xiàng)目麦备,使用的是極光的 JMessage ,但是在使用會(huì)話輸入組件的時(shí)候遇到了鍵盤高度的問題昭娩,解決無果凛篙。
項(xiàng)目也有段時(shí)間沒更新了,但是已經(jīng)實(shí)現(xiàn)了基本的聊天會(huì)話栏渺,好友列表呛梆,在線離線,添加刪除等功能磕诊。
正好極光舉辦這次活動(dòng)填物,抽空又嘗試把會(huì)話組件集成了進(jìn)來。
這次使用已經(jīng)沒有了之前的問題霎终≈突牵可以看效果圖
UI 組件倉庫:https://github.com/jpush/aurora-imui
一、基本使用
在 App 下的 build.gradle 引入即可
compile 'cn.jiguang.imui:chatinput:0.5.7'
然后布局中使用
<cn.jiguang.imui.chatinput.ChatInputView
android:id="@+id/chat_input"
android:visibility="visible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
初始化控件
ChatInputView chatInputView = (ChatInputView) findViewById(R.id.chat_input);
chatInputView.setMenuContainerHeight(softInputHeight);
這里需要給組件設(shè)置高度莱褒,一般使用軟鍵盤彈起時(shí)的高度击困。官方文檔建議是在上個(gè)界面通過 onSizeChanged 方法獲取,這里具體可以自己測試广凸。
獲取軟鍵盤高度的方法
int heightDifference = 0;
/*獲取鍵盤的高度*/
private void editTextHeight() {
mChatInput.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//獲取當(dāng)前界面可視部分
ChatMsgActivity.this.getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
//獲取屏幕的高度
int screenHeight = ChatMsgActivity.this.getWindow().getDecorView().getRootView().getHeight();
//此處就是用來獲取鍵盤的高度的阅茶, 在鍵盤沒有彈出的時(shí)候 此高度為0 鍵盤彈出的時(shí)候?yàn)橐粋€(gè)正數(shù)
if (screenHeight - r.bottom > 0) {
heightDifference = screenHeight - r.bottom;
}
// LogUtils.e(heightDifference + "");
}
});
}
如果上設(shè)置都沒問題之后,那么就可以直接開啟會(huì)話組件的監(jiān)聽了炮障。
chatInput.setMenuClickListener(new OnMenuClickListener() {
@Override
public boolean onSendTextMessage(CharSequence input) {
sendMessage(charSequence.toString());
// 輸入框輸入文字后目派,點(diǎn)擊發(fā)送按鈕事件
}
@Override
public void onSendFiles(List<String> list) {
// 選中文件或者錄制完視頻后,點(diǎn)擊發(fā)送按鈕觸發(fā)此事件
}
@Override
public boolean switchToMicrophoneMode() {
// 點(diǎn)擊語音按鈕觸發(fā)事件胁赢,顯示錄音界面前觸發(fā)此事件
// 返回 true 表示使用默認(rèn)的界面,若返回 false 應(yīng)該自己實(shí)現(xiàn)界面
return true;
}
@Override
public boolean switchToGalleryMode() {
// 點(diǎn)擊圖片按鈕觸發(fā)事件,顯示圖片選擇界面前觸發(fā)此事件
// 返回 true 表示使用默認(rèn)的界面
return true;
}
@Override
public boolean switchToCameraMode() {
// 點(diǎn)擊拍照按鈕觸發(fā)事件智末,顯示拍照界面前觸發(fā)此事件
// 返回 true 表示使用默認(rèn)的界面
return true;
}
});
二谅摄、開始發(fā)送一條消息
發(fā)送消息需要?jiǎng)?chuàng)建消息,并且指定消息類型系馆。
發(fā)送消息包括兩方面送漠,一個(gè)是本地端,另一個(gè)服務(wù)端由蘑。
本地的用于消息的即時(shí)展示闽寡,服務(wù)端用于消息的轉(zhuǎn)發(fā)。
創(chuàng)建一條本地消息:
本地消息需要制定展示的位置尼酿,用于 List 識(shí)別是發(fā)送的消息還是接收的消息爷狈,方便位置的展示。
final MyMessage myMessage = new MyMessage(string, SEND_TEXT);
myMessage.setMessage(message1);
myMessage.setTimeString(TimeUtils.ms2date("MM-dd HH:mm", message1.getCreateTime()));
myMessage.setUserInfo(new DefaultUser(JMessageClient.getMyInfo().getUserName(), "DeadPool", imgSend));
如果僅僅是本地展示不需要經(jīng)過服務(wù)端通訊那么直接調(diào)用 mAdapter.addToStart(myMessage, true);
方法將消息追加到消息列表就完成了裳擎。
但是對(duì)方是接收不到消息的涎永。所以在創(chuàng)建本地消息的同時(shí),也需要把消息推送到服務(wù)端鹿响,當(dāng)服務(wù)端處理并且返回成功的時(shí)候本地再展示羡微。
創(chuàng)建一條消息發(fā)送到服務(wù)端:
TextContent content = new TextContent(string);
Message message1 = conversation.createSendMessage(content);
TextContent 是創(chuàng)建的文本消息,
要?jiǎng)?chuàng)建其它類型的消息比如語音就是 VoiceContent 惶我。
VoiceContent content1 = new VoiceContent(new File("/11111.mp3", 0));
Image 消息 ImageContent content1 = new ImageContent(new File(""));
目前已經(jīng)支持的消息類型:
text,
image,
voice,
location,
video,
eventNotification,
custom,
unknown,
file,
prompt;
創(chuàng)建完成之后需要將消息添加到 Message 妈倔,初始化發(fā)送消息事件。
Message message1 = conversation.createSendMessage(content);
下面是發(fā)送消息的重點(diǎn)
gotResult 是發(fā)送消息的回調(diào)绸贡,只有 = 1 的情況下才是發(fā)送成功的狀態(tài)盯蝴,所以需要在發(fā)送成功的時(shí)候?qū)⑾⒆芳拥较⒘斜怼?br> 注意這里追加的消息就是創(chuàng)建的本地消息。
message1.setOnSendCompleteCallback(new BasicCallback() {
@Override
public void gotResult(int i, String s) {
if (i == 0) {
mAdapter.addToStart(myMessage, true);
mChatEt.setText("");
} else {
}
}
});
JMessageClient.sendMessage(message1);
但是這里有個(gè)小問題就是恃轩,如果在無網(wǎng)絡(luò)的情況下结洼,重復(fù)點(diǎn)擊發(fā)送事件,消息就會(huì)被重復(fù)添加到待發(fā)送隊(duì)列叉跛,當(dāng)連接網(wǎng)絡(luò)后會(huì)一次性把所以的消息都發(fā)送出去松忍。
所以在這里需要要對(duì)發(fā)送失敗的消息做處理。
完整代碼
/*發(fā)送消息筷厘,當(dāng)前版本只能發(fā)送文本*/
private void sendMessage(String msg) {
TextContent content = new TextContent(msg);
Message message1 = conversation.createSendMessage(content);
final MyMessage myMessage = new MyMessage(msg, SEND_TEXT);
myMessage.setMessage(message1);
myMessage.setTimeString(TimeUtils.ms2date("MM-dd HH:mm", message1.getCreateTime()));
myMessage.setUserInfo(new DefaultUser(JMessageClient.getMyInfo().getUserName(), "DeadPool", imgSend));
message1.setOnSendCompleteCallback(new BasicCallback() {
@Override
public void gotResult(int i, String s) {
if (i == 0) {
mAdapter.addToStart(myMessage, true);
mChatEt.setText("");
} else {
}
}
});
JMessageClient.sendMessage(message1);
// EventBus.getDefault().post(new MessageEvent(1,"",message1));
if (mData != null) {
mData.clear();
}
}
三鸣峭、接收消息
在使用 JMessage 接收消息,都需要先在當(dāng)前頁面注冊(cè)消息接收
注冊(cè)接收者
如果用過 EventBus 那么很容易理解即時(shí)通訊的消息傳遞機(jī)制酥艳。
JMessageClient.registerEventReceiver(this);
conversations = JMessageClient.getConversationList();
接收事件解綁
@Override
protected void onDestroy() {
JMessageClient.unRegisterEventReceiver(this);
JMessageClient.exitConversation();
super.onDestroy();
}
消息處理
當(dāng)收到消息后摊溶,所有注冊(cè)過消息接收事件的頁面都會(huì)收到通知,然后我們可以對(duì)收到的消息進(jìn)行處理充石。
常用消息事件
- MessageEvent 消息事件
- MessageRetractEvent 撤回消息事件
- ContactNotifyEvent 好友通知事件
- ConversationRefreshEvent 消息漫游事件
- OfflineMessageEvent 離線消息事件
消息事件
這里需要注意的是同上面發(fā)送消息的方法類似莫换,由于 SDK 和 UI 組件的消息 Bean 不一致,所以需要對(duì) SDK 返回的消息解析到ui組件(MessageList)。
然后就是對(duì)視圖的處理
final Message message = event.getMessage();
runOnUiThread(new Runnable() {
@Override
public void run() {
//創(chuàng)建一個(gè)消息對(duì)象
myMessage = new MyMessage(((TextContent) message.getContent()).getText(), IMessage.MessageType.RECEIVE_TEXT);
myMessage.setMessage(message);
myMessage.setMsgID(message.getServerMessageId());
myMessage.setText(((TextContent) message.getContent()).getText() + "");
myMessage.setTimeString(TimeUtils.ms2date("MM-dd HH:mm", message.getCreateTime()));
myMessage.setUserInfo(new DefaultUser(JMessageClient.getMyInfo().getUserName(), "DeadPool", imgRecrive));
if (message.getContentType() == ContentType.text || message.getContentType().equals("text")) {
mAdapter.addToStart(myMessage, true);
mAdapter.notifyDataSetChanged();
}
//收到消息時(shí)拉岁,添加到集合
list.add(myMessage);
}
});
撤回消息的事件
/*接收到撤回的消息*/
public void onEvent(MessageRetractEvent event) {
final Message message = event.getRetractedMessage();
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e("dataSize", mData.size() + "," + list.size());
for (int i = 0; i <= list.size(); i++) {
Log.e("messageRetract", "message:" + message + "\nMymessage:" + list.get(i));
if (list.get(i).getMsgID() == message.getServerMessageId()) {
mAdapter.delete(list.get(i));
MyMessage message1 = new MyMessage("[對(duì)方撤回了一條消息]", IMessage.MessageType.RECEIVE_TEXT);
mAdapter.addToStart(message1, true);
mAdapter.notifyDataSetChanged();
mAdapter.updateMessage(message1);
}
}
}
});
}
這里只是簡單介紹 imui 結(jié)合 MessageList 的基本使用坷剧,關(guān)于更詳細(xì),更多功能請(qǐng)參考官方文檔說明喊暖。
關(guān)于使用中遇到的問題可以參考 demo 惫企,或者去極光技術(shù)社區(qū)提問。
Demo地址:Android-IM
JMessage 相關(guān)文章
「本文系極光征文 | 寫寫文章就能贏 Filco陵叽,豈不美滋滋的參賽文章」