環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-ChatDemoUI3.0
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-LoginActivity
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-主界面的三個(gè)fragment-會(huì)話界面
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-主界面的三個(gè)fragment-通訊錄界面
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-主界面的三個(gè)fragment-設(shè)置界面
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-EaseUI
環(huán)信官方Demo源碼分析及SDK簡(jiǎn)單應(yīng)用-IM集成開發(fā)詳案及具體代碼實(shí)現(xiàn)
前言
手頭工作上,正好需要在已有的兩個(gè)App上集成IM功能。且迭代流程中是有開發(fā)詳案這一項(xiàng)的。就分享給大家,邊寫開發(fā)詳案邊寫代碼。好吧,廢話不多說,我們一起來學(xué)習(xí)如何集成和改造這款簡(jiǎn)單易用而又非常強(qiáng)大的環(huán)信SDK。
具體步驟
迭代點(diǎn)
需要做的功能點(diǎn)及工作
1.集成環(huán)信
2.圍繞UE和UI進(jìn)行編碼
房源詳情增加咨詢按鈕,點(diǎn)擊進(jìn)入咨詢對(duì)話框,并且將房源信息帶入對(duì)話框。
消息中心
主界面TABBAR點(diǎn)擊消息進(jìn)入該界面
包含系統(tǒng)消息入庫(kù)和咨詢用戶列表
從TABBAR點(diǎn)擊“消息”圖標(biāo)進(jìn)入本頁(yè)面后,可以在本頁(yè)面進(jìn)入”系統(tǒng)消息“,并且將咨詢過的用戶會(huì)話顯示在本頁(yè),長(zhǎng)按任意一條會(huì)話,提示刪除當(dāng)前會(huì)話確定。
列表排序:“系統(tǒng)通知“仍然在最上面的位置,不受排序影響
咨詢排序:按最后聊天時(shí)間倒序排列,咨詢列表默認(rèn)顯示20條,多了拖動(dòng)加載分頁(yè)數(shù)據(jù)。
無咨詢用戶時(shí),只顯示”系統(tǒng)通知“入口即可。
咨詢列表:長(zhǎng)按可刪除當(dāng)前聊天對(duì)象,需要有確認(rèn)對(duì)話框(只刪除會(huì)話,不刪除聊天記錄)
根據(jù)UE和UI改造聊天窗口(EaseUI庫(kù))
注意以下幾點(diǎn)
從房源詳情頁(yè)進(jìn)入時(shí),就返回房源詳情頁(yè),從消息中心進(jìn)入時(shí),就返回消息中心。
顯示當(dāng)前咨詢?nèi)说慕?jīng)紀(jì)人姓名,并顯示當(dāng)前咨詢的對(duì)象的在線狀態(tài)(在線/離線)
標(biāo)題頭中的電話按鈕可以直接撥打電話
對(duì)于從房源詳情進(jìn)入時(shí)帶入的房源詳情類型的聊天條目,經(jīng)紀(jì)人可以點(diǎn)擊查看該房源在STM中的詳情。
顯示經(jīng)紀(jì)人照片上傳的照片,如果經(jīng)紀(jì)人沒有上傳照片,就顯示一個(gè)經(jīng)紀(jì)人的占位圖(要區(qū)別于用戶的占位圖)
當(dāng)前用戶頭像默認(rèn)顯示當(dāng)前用戶的頭像,如果沒有頭像,就顯示一個(gè)默認(rèn)的占位圖
聊天內(nèi)容上長(zhǎng)按可復(fù)制
發(fā)送的是手機(jī)號(hào)碼時(shí)可以直接打電話。
思路
先做加法,再做減法
我們來按照原有代碼改造和設(shè)計(jì)環(huán)信SDK部分相關(guān)代碼改造,兩個(gè)部分來做工作。將具體的功能點(diǎn)拆分并給出實(shí)現(xiàn)。
我們?cè)贒emo上修改,修改完成后剔除無關(guān)代碼抽取成獨(dú)立的我們需要的相關(guān)代碼。整個(gè)工作也就結(jié)束了。
通過之前的代碼閱讀,我們知道整個(gè)Demo是一個(gè)相對(duì)完整的App,而我們實(shí)際工作中集成個(gè)im基本出不了這個(gè)范圍。
就好比這次迭代也是。
因?yàn)閷?shí)際整個(gè)涉及的只有會(huì)話列表和聊天界面,我們主要關(guān)注ConversationListFragment與ChatActivity就行了。
實(shí)現(xiàn)
SeeHouse相關(guān)改造
原有代碼改造
房源詳情增加咨詢按鈕,點(diǎn)擊進(jìn)入咨詢對(duì)話框,并且將房源信息帶入對(duì)話框。
主界面TABBAR點(diǎn)擊消息進(jìn)入該界面
涉及環(huán)信SDK部分相關(guān)代碼改造
包含系統(tǒng)消息入庫(kù)和咨詢用戶列表
同列表,不同type類型區(qū)分,并置頂系統(tǒng)消息
從TABBAR點(diǎn)擊“消息”圖標(biāo)進(jìn)入本頁(yè)面后,可以在本頁(yè)面進(jìn)入”系統(tǒng)消息“,并且將咨詢過的用戶會(huì)話顯示在本頁(yè),長(zhǎng)按任意一條會(huì)話,提示刪除當(dāng)前會(huì)話確定。
直接貼過去,Demo已經(jīng)實(shí)現(xiàn)。
列表排序:“系統(tǒng)通知“仍然在最上面的位置,不受排序影響根據(jù)Type來判斷類型,并排序置頂。
咨詢排序:按最后聊天時(shí)間倒序排列,咨詢列表默認(rèn)顯示20條,多了拖動(dòng)加載分頁(yè)數(shù)據(jù)。sort算法改一下,看下本身是否帶分頁(yè)。
無咨詢用戶時(shí),只顯示”系統(tǒng)通知“入口即可。
無需實(shí)現(xiàn)。
咨詢列表:長(zhǎng)按可刪除當(dāng)前聊天對(duì)象,需要有確認(rèn)對(duì)話框(只刪除會(huì)話,不刪除聊天記錄)

環(huán)信的哥哥們已經(jīng)幫我們實(shí)現(xiàn)了。但是根據(jù)要求呢,我沒只需要?jiǎng)h除會(huì)話,所以我們把第二項(xiàng)注釋掉。

我們把對(duì)應(yīng)處的判斷代碼和對(duì)應(yīng)的menu文件em_delete_message中的標(biāo)簽給注釋掉??葱Ч?。

從房源詳情頁(yè)進(jìn)入時(shí),就返回房源詳情頁(yè),從消息中心進(jìn)入時(shí),就返回消息中心。?
直接finish();
顯示當(dāng)前咨詢?nèi)说慕?jīng)紀(jì)人姓名,并顯示當(dāng)前咨詢的對(duì)象的在線狀態(tài)(在線/離線)官方的EaseUi是這么說的

我們來找下EaseTitleBar

我們來看下他的布局
http://schemas.android.com/apk/res/android"android:id="@+id/root"? ? android:layout_width="match_parent"? ? android:layout_height="@dimen/height_top_bar"? ? android:background="@color/top_bar_normal_bg"? ? android:gravity="center_vertical" >?? ? ?? ? ? ? ? ? ?? ? ?? ? ?? ? ? ? ? ? ?
其實(shí)有title和rightview的。

我們來對(duì)title加入一個(gè)是否在線的狀態(tài)
1.獲取token
MacBook:~ mli$ curl -X POST "https://a1.easemob.com/1177170 ... ot%3B-d '{"grant_type":"client_credentials","client_id":"YXA6vcNInEeatzGVyK0tA","client_secret":"YXA6YACo7qumFfgYdWher3D3Cs"}'
{"access_token":"YWMtOT73nvcIEeaPCCuTQsCAAAVuOB_MQchxsIsxVJFXsW6lZ8f2l__xn8","expires_in":5168429,"application":"bd09c370-d227-11e6-adcc-65700322b4b4"}
2.拿token獲取用戶狀態(tài)
MacBook:~ mli$ curl -X GET -i -H "Authorization: Bearer YWMtOT73nvcIEeaPCCuTQsC6kwAAAVuOB_MQchxsIsxybVJFXsW6lZ8f2l__xn8" "https://a1.easemob.com/1177170 ... ot%3BHTTP/1.1 200 OKServer: Tengine/2.0.3Date: Mon, 20 Feb 2017 05:24:00 GMTContent-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedConnection: keep-aliveAccess-Control-Allow-Origin: *Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Sun, 19-Feb-2017 05:24:00 GMT{? "action" : "get",? "uri" : "http://a1.easemob.com/11771701 ... ot%3B,? "entities" :[],? "data" : {? ? "2" : "offline"? },? "timestamp" : 1487568240699,? "duration" : 25,? "count" : 0}MacBook:~ mli$ curl -X GET -i -H "Authorization: Bearer YWMtOT73nvcIEeaPCCuCkwAAAVuOB_MQchxsIJFXsW6lZ8f2l__xn8" "https://a1.easemob.com/1177170 ... ot%3BHTTP/1.1 200 OKServer: Tengine/2.0.3Date: Mon, 20 Feb 2017 05:24:08 GMTContent-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedConnection: keep-aliveAccess-Control-Allow-Origin: *Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Sun, 19-Feb-2017 05:24:08 GMT{? "action" : "get",? "uri" : "http://a1.easemob.com/11771701 ... ot%3B,? "entities" :[],? "data" : {? ? "1" : "online"? },? "timestamp" : 1487568248135,? "duration" : 14,? "count" : 0MacBook:~ mli$
我們可以看到2是離線,1是在線的。
注意一點(diǎn)

所以昵稱是在咱自己的體系的。可以從現(xiàn)有的App里提取,如果有的話。
我們知道從列表ConversationListFragment->ChatActivity->ChatFragment
那么如何接受和發(fā)送自己與他人的頭像和昵稱呢?
我們來玩這個(gè)ChatFragment

在OnSetMessageAttributes中,設(shè)置我們要發(fā)送時(shí)的消息擴(kuò)展屬性。
那么接收怎么辦呢,我們來看下DemoHelper中的getUserInfo()方法。

無聊的用鄙人蹩腳的英文寫了一把注釋。英文若是寫的不對(duì)就不對(duì)吧。
標(biāo)題頭中的電話按鈕可以直接撥打電話
修改刪除按鈕為打電話,并改動(dòng)相關(guān)代碼
顯示經(jīng)紀(jì)人照片上傳的照片,如果經(jīng)紀(jì)人沒有上傳照片,就顯示一個(gè)經(jīng)紀(jì)人的占位圖(要區(qū)別于用戶的占位圖)
修改原demo
當(dāng)前用戶頭像默認(rèn)顯示當(dāng)前用戶的頭像,如果沒有頭像,就顯示一個(gè)默認(rèn)的占位圖
修改原demo。
聊天內(nèi)容上長(zhǎng)按可復(fù)制

自帶了,后面我們可能需要去掉轉(zhuǎn)發(fā)。
發(fā)送的是手機(jī)號(hào)碼時(shí)可以直接打電話。
我們?cè)匍L(zhǎng)按后判斷其是否為電話號(hào)碼,如果是添加一項(xiàng)撥打電話。
引用關(guān)系是這樣的
ChatFragment->ContextMenuActivity->em_context_menu_for_location.xml
最后調(diào)回ChatFragment的onActivityResult
我們來改em_context_menu_for_location.xml
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" >?? ? ?? ? ?? ? ?? -->? ? ? ?
再來改ContextMenuActivity
/** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *http://www.apache.org/licenses/LICENSE-2.0* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.hyphenate.chatuidemo.ui;?import android.content.Intent;import android.os.Bundle;import android.text.TextUtils;import android.view.MotionEvent;import android.view.View;import android.widget.TextView;?import com.easemob.redpacketsdk.constant.RPConstant;import com.hyphenate.chat.EMMessage;import com.hyphenate.chatuidemo.Constant;import com.hyphenate.chatuidemo.R;?public class ContextMenuActivity extends BaseActivity {? ? 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 RESUTL_CALL_PHONE = 4;? ? String phoneNumber;? ? @Override? ? protected void onCreate(Bundle savedInstanceState) {? ? ? ? super.onCreate(savedInstanceState);? ? ? ? EMMessage message = getIntent().getParcelableExtra("message");? ? ? ? boolean isChatroom = getIntent().getBooleanExtra("ischatroom", false);? ? ? ? phoneNumber = getIntent().getStringExtra("phone_number");? ? ? ? ? ? ? ? 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 : 屏蔽紅包消息、轉(zhuǎn)賬消息的轉(zhuǎn)發(fā)功能? ? ? ? ? ? ? ? ? ? || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)? ? ? ? ? ? ? ? ? ? || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_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{? ? ? ? ? ? ? ? //for text content? ? ? ? ? ? ? ? setContentView(R.layout.em_context_menu_for_text);? ? ? ? ? ? ? ? //for call phone number? ? ? ? ? ? ? ? TextView callPhone = (TextView) findViewById(R.id.call_phone);? ? ? ? ? ? ? ? if(!TextUtils.isEmpty(phoneNumber)){? ? ? ? ? ? ? ? ? ? callPhone.setVisibility(View.VISIBLE);? ? ? ? ? ? ? ? ? ? callPhone.setText("撥打電話:" + phoneNumber);? ? ? ? ? ? ? ? }else{? ? ? ? ? ? ? ? ? ? callPhone.setVisibility(View.GONE);? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? } 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);? ? ? ? }? ? ? ? if (isChatroom? ? ? ? ? ? ? ? //red packet code : 屏蔽紅包消息、轉(zhuǎn)賬消息的撤回功能? ? ? ? ? ? ? ? || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_RED_PACKET_MESSAGE, false)? ? ? ? ? ? ? ? || message.getBooleanAttribute(RPConstant.MESSAGE_ATTR_IS_TRANSFER_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 call(View view) {? ? ? ? Intent it = new Intent();? ? ? ? it.putExtra("phone_number",phoneNumber);? ? ? ? setResult(RESUTL_CALL_PHONE,it);? ? ? ? finish();? ? }}
再來判斷內(nèi)容是否為電話號(hào)碼
String phoneNumber="";
if(isPhoneNumber(content)){
phoneNumber = content;
}
// no message forward when in chat room
startActivityForResult((new Intent(getActivity(), ContextMenuActivity.class)).putExtra("message",message)
//if message's context is a phone number ,make it can be call it.
.putExtra("ischatroom", chatType == EaseConstant.CHATTYPE_CHATROOM).putExtra("phone_number",phoneNumber),
REQUEST_CODE_CONTEXT_MENU);
onActivityResult部分
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_CONTEXT_MENU) {
//for Context MenuActivity Result
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;
?
case ContextMenuActivity.RESUTL_CALL_PHONE:
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri callData = Uri.parse("tel:" +data.getStringExtra("phone_number"));
intent.setData(callData);
startActivity(intent);
break;
?
default:
break;
}
}
記住先提取字符串中的數(shù)字,再去匹配正則。


STM集成
在本質(zhì)上是相同的。不同的是一個(gè)是用戶端,一個(gè)是經(jīng)紀(jì)人端
標(biāo)注下需要注意的幾個(gè)地方
頭像和昵稱的擴(kuò)展互通,是SeeHouse和STM兩邊都需要做的。
因?yàn)橛幸粭l對(duì)于從房源詳情進(jìn)入時(shí)帶入的房源詳情類型的聊天條目,經(jīng)紀(jì)人可以點(diǎn)擊查看該房源在STM中的詳情。是在STM中單獨(dú)實(shí)現(xiàn)的。SeeHouse負(fù)責(zé)帶入,STM負(fù)責(zé)點(diǎn)擊跳轉(zhuǎn)。
對(duì)于從房源詳情進(jìn)入時(shí)帶入的房源詳情類型的聊天條目,經(jīng)紀(jì)人可以點(diǎn)擊查看該房源在STM中的詳情。
創(chuàng)建圖文chatrow并設(shè)置對(duì)應(yīng)點(diǎn)擊事件代碼。
集成至目標(biāo)App
不需要的代碼,我們只做注釋,不刪除,防止后面增加了,需要了。避免一系列麻煩。
?剔除紅包庫(kù)?
在ChatUIDemo3.0的build.gradle中注釋編譯紅包依賴庫(kù)。
各種編譯,遇到報(bào)錯(cuò)就刪除相關(guān)代碼
剔除不需要的代碼
注意EaseUI下有個(gè)SimpleDemo

目標(biāo)App集成與調(diào)試
因?yàn)槭枪镜纳虡I(yè)項(xiàng)目,這里就不貼出來了。接著完成需調(diào)試才能完成的功能點(diǎn)
總結(jié)
好了,至此,我們開發(fā)詳案寫完了,代碼也寫完了。因?yàn)楸疚膶懙臅r(shí)候UI還未出,所以后面就是根據(jù)UI改改的調(diào)整調(diào)整界面的小事情了。
有任何問題或者其他事宜請(qǐng)聯(lián)系我:5108168@qq.com,歡迎指正和勘誤。