項(xiàng)目總結(jié)之即時(shí)通訊
融云的服務(wù)真是快,有問題提工單,工程師回答速度快,可以很方便解決實(shí)際開發(fā)中的問題。作為總結(jié),我們還是隨便聊聊融云的使用吧!直接進(jìn)入正題————>>>
首先去融云開發(fā)者平臺(tái)注冊(cè)、下載SDK、導(dǎo)包、連接、實(shí)現(xiàn)會(huì)話列表和會(huì)話界面這里就簡(jiǎn)單說明一下。不懂的可以去之前文章看。本片重點(diǎn)說一下自定義消息和plugin擴(kuò)展區(qū)域自定義。
第一步,使用第三方的東西肯定要去他們的官網(wǎng)注冊(cè)一個(gè)開發(fā)者賬號(hào),步驟很簡(jiǎn)單,一步步填寫,下一步就行。注意手機(jī)號(hào)的填寫,還是要填寫真實(shí)的,貌似有條規(guī)定,如果手機(jī)號(hào)為空號(hào)可能會(huì)導(dǎo)致他們不會(huì)給改用戶下的app提供服務(wù),所以為了安全起見還是填寫真實(shí)的手機(jī)號(hào)碼。注冊(cè)鏈接地址。
第二步:下載SDK,建議SDK別使用當(dāng)前太低版本、因?yàn)橛行┕δ芸赡懿恢С?。下載SDK時(shí),我們也要把官方提供的demo下載下來,畢竟這是第一手資料,官網(wǎng)提供的demo功能還是比較豐富的,如果應(yīng)用要求的功能不是很多,demo里面提供的基本功能使應(yīng)該能滿足的。demo可能是托管在github上的,所以還要有個(gè)git賬號(hào)。請(qǐng)自行注冊(cè),畢竟開發(fā)者還是應(yīng)該多去上面看看大牛寫的開源東西來學(xué)習(xí)。
第三步:注冊(cè)應(yīng)用,使用第三方的工具,不注冊(cè)怎么用呢?個(gè)人中心填寫下應(yīng)用的基本情況應(yīng)該沒啥問題了。
簡(jiǎn)要的幾個(gè)步驟,具體使用可以參照官方提供的幫助文檔,官方文檔地址戳這里
功能介紹
前沿簡(jiǎn)介
前奏
使用第三方的庫,首先就需要進(jìn)行第三方要求的初始化操作,這部分代碼,基本直接copy官網(wǎng)提供的就可以,這樣可以保證準(zhǔn)確性。所以這里簡(jiǎn)要介紹需要我們處理的幾個(gè)點(diǎn)。
1、Manifest.xml文件中:
android:host選項(xiàng)的值全部為自己應(yīng)用的包名。例如:android:host=”com.dsw.infor”
meta-data標(biāo)簽中的appkey的值要改為自己應(yīng)用申請(qǐng)的appkey
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mydemoplugin">
<permission
android:name="cn.rongcloud.im.permission.MIPUSH_RECEIVE"
android:protectionLevel="signature" /> <!-- 發(fā)送位置消息,實(shí)時(shí)位置共享,如果您需要定位相關(guān)的功能,可以打開以下注釋 -->
<permission
android:name="cn.rongcloud.im.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<permission
android:name="cn.rongcloud.im.push.permission.MESSAGE"
android:protectionLevel="signature" /> <!-- targetSdkVersion為29時(shí),如果需要后臺(tái)定位權(quán)限,需要添加 ACCESS_BACKGROUND_LOCATION 權(quán)限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- ?米 配置開始 < -->
<uses-permission android:name="MediaStore.Images.Media.INTERNAL_CONTENT_URI" />
<uses-permission android:name="MediaStore.Images.Media.EXTERNAL_CONTENT_URI" /> <!-- ?米 配置結(jié)束 < -->
<!-- GCM 配置開始 < -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- GCM 配置結(jié)束 < -->
<!-- 華為 配置開始 < -->
<!-- HMS-SDK引導(dǎo)升級(jí)HMS功能,訪問OTA服務(wù)器需要網(wǎng)絡(luò)權(quán)限 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- HMS-SDK引導(dǎo)升級(jí)HMS功能,保存下載的升級(jí)包需要SD卡寫權(quán)限 -->
<uses-permission android:name="cn.rongcloud.im.permission.MIPUSH_RECEIVE" /> <!-- 檢測(cè)網(wǎng)絡(luò)狀態(tài) -->
<uses-permission android:name="cn.rongcloud.im.permission.C2D_MESSAGE" /> <!-- 檢測(cè)wifi狀態(tài) -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- 為了獲取用戶手機(jī)的IMEI,用來唯一的標(biāo)識(shí)用戶;發(fā)送位置及實(shí)時(shí)位置時(shí)需要此權(quán)限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 如果是安卓8.0,應(yīng)用編譯配置的targetSdkVersion>=26,請(qǐng)務(wù)必添加以下權(quán)限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 華為 配置結(jié)束 < -->
<!-- MeiZu 配置開始 < -->
<!-- 兼容 flyme5.0 以下版本,魅族內(nèi)部集成 pushSDK 必填,不然無法收到 消息 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- 兼容 flyme3.0 配置權(quán)限 -->
<uses-permission android:name="com.meizu.flyme.push.permission.RECEIVE" /> <!-- MeiZu 配置結(jié)束 < -->
<!-- OPPPO 權(quán)限配置 -->
<uses-permission android:name="cn.rongcloud.im.push.permission.MESSAGE" /> <!-- OPPO 配置結(jié)束 -->
<uses-permission android:name="com.meizu.c2dm.permission.RECEIVE" /> <!-- 為了獲取用戶手機(jī)的IMEI,用來唯一的標(biāo)識(shí)用戶;發(fā)送位置消息,實(shí)時(shí)位置共享需要此權(quán)限 -->
<uses-permission android:name="com.coloros.mcs.permission.SEND_MCS_MESSAGE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyDemoPlugin">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ConversationActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="com.example.mydemoplugin"
android:pathPrefix="/conversation/"
android:scheme="rong" />
</intent-filter>
</activity>
<activity
android:name=".ConversationListActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="com.example.mydemoplugin"
android:pathPrefix="/conversationlist"
android:scheme="rong" />
</intent-filter>
</activity>
</application>
</manifest>
2、在我們的Application中進(jìn)行初始化,執(zhí)行RongIM.init(this);
代碼如下:
/**
* 應(yīng)用啟動(dòng)時(shí),判斷用戶是否已接受隱私協(xié)議,如果已接受,正常初始化;否則跳轉(zhuǎn)到隱私授權(quán)頁面請(qǐng)求用戶授權(quán)。
*/
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
// if (!AppContext.isInitialized()) {
// AppContext.init(getApplicationContext());
// }
//用戶已接受隱私協(xié)議,進(jìn)行初始化
String appKey = "pvxdm17jpws8r";
// 第一個(gè)參數(shù)必須傳應(yīng)用上下文
RongIM.init(this.getApplicationContext(), appKey);
//注冊(cè)Plugin自定義類型
registerExtensionPlugin();
//消息自定義類型
RongIM.registerMessageType(RedPackageMessage.class);
RongIM.registerMessageTemplate(new RedPackageItemProvider());
}
private void registerExtensionPlugin() {
List<IExtensionModule> moduleList = RongExtensionManager.getInstance().getExtensionModules();
IExtensionModule defaultModule = null;
if (moduleList != null) {
for (IExtensionModule module : moduleList) {
if (module instanceof DefaultExtensionModule) {
defaultModule = module;
break;
}
}
if (defaultModule != null) {
//移除已注冊(cè)的默認(rèn)模塊,替換成自定義模塊RongExtensionManager.getInstance().unregisterExtensionModule(defaultModule);
RongExtensionManager.getInstance().unregisterExtensionModule(defaultModule);
RongExtensionManager.getInstance().registerExtensionModule(new SealExtensionModule());
}
}
}
}
3、初始化的工作完成后,然后我們就在我們成功登陸后,進(jìn)行服務(wù)器的鏈接。此時(shí),我們需要調(diào)用我們的后臺(tái)來獲取融云認(rèn)證的token信息。測(cè)試階段,我們可以使用api調(diào)試,手動(dòng)生成來用。
RongIM.connect(token, new ConnectCallback() {
@Override
public void onSuccess(String arg0) {
Log.d("RongClound", "RongClound: Tocken Success");
}
@Override
public void onError(ErrorCode arg0) {
Log.d("RongClound", "RongClound: Tocken Error,ErrorCode:" + arg0);
}
@Override
public void onTokenIncorrect() {
Log.d("RongClound", "RongClound: onTokenIncorrect");
}
});
4.單聊功能
由于IMKit中融云提供的界面都是基于fragment,所以使用也是簡(jiǎn)單,我們只需要簡(jiǎn)單配置下我們的會(huì)話列表界面就可以了,比如:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
布局搞好了,我們要?jiǎng)?chuàng)建一個(gè)ConversationActivity來進(jìn)行顯示。
public class ConversationListActivity extends FragmentActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_conversation_list);
ConversationListFragment conversationListFragment=new ConversationListFragment();
// 此處設(shè)置 Uri. 通過 appendQueryParameter 去設(shè)置所要支持的會(huì)話類型. 例如
// .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(),"false")
// 表示支持單聊會(huì)話, false 表示不聚合顯示, true 則為聚合顯示
Uri uri = Uri.parse("rong://" +
getApplicationContext().getApplicationInfo().packageName).buildUpon()
.appendPath("conversationlist")
.appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false") //設(shè)置私聊會(huì)話是否聚合顯示
.appendQueryParameter(Conversation.ConversationType.GROUP.getName(), "false")//群組
.appendQueryParameter(Conversation.ConversationType.PUBLIC_SERVICE.getName(), "false")//公共服務(wù)號(hào)
.appendQueryParameter(Conversation.ConversationType.APP_PUBLIC_SERVICE.getName(), "false")//訂閱號(hào)
.appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "true")//系統(tǒng)
.build();
conversationListFragment.setUri(uri);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.container, conversationListFragment);
transaction.commit();
Activity創(chuàng)建好自然要在Manifest中進(jìn)行聲明
<activity
android:name=".ConversationListActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="com.example.mydemoplugin"
android:pathPrefix="/conversationlist"
android:scheme="rong" />
</intent-filter>
</activity>
回話界面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
public class ConversationActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_conversation);
ConversationFragment conversationFragment = new ConversationFragment();
Intent intent = getIntent(); // 取得從上一個(gè)Activity當(dāng)中傳遞過來的Intent對(duì)象
String targetId = null;
if (intent != null) {
targetId = intent.getStringExtra("targetId");
}
Uri uri = Uri.parse("rong://" +
getApplicationContext().getApplicationInfo().packageName).buildUpon()
.appendPath("conversation").appendPath("private")
.appendQueryParameter("targetId", targetId)//設(shè)置私聊會(huì)話是否聚合顯示,targetId:單聊人的id
.build();
conversationFragment.setUri(uri);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.container, conversationFragment);
transaction.commit();
}
}
至此單聊功能就實(shí)現(xiàn)了!開始進(jìn)入正題自定義消息和Plugin擴(kuò)展。
自定義消息
參考資料:http://www.rongcloud.cn/docs/android.html#新建消息
- 自定義消息實(shí)體 –RedPackageMessage(一個(gè)自定義的紅包消息)
package com.example.mydemoplugin.PhoneInfoProvider;
/**
* Created by zhangbowen on 6/15/21.
**/
import android.os.Parcel;
import android.util.Log;
import com.alibaba.fastjson.JSON;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import io.rong.common.ParcelUtils;
import io.rong.imlib.MessageTag;
import io.rong.imlib.model.MessageContent;
import io.rong.message.CSSuspendMessage;
/*
* 注解名:MessageTag ;屬性:value ,flag; value 即 ObjectName 是消息的唯一標(biāo)識(shí)不可以重復(fù),
* 開發(fā)者命名時(shí)不能以 RC 開頭,避免和融云內(nèi)置消息沖突;flag 是用來定義消息的可操作狀態(tài)。
*如下面代碼段,自定義消息名稱 CustomizeMessage ,vaule 是 app:custom ,
* flag 是 MessageTag.ISCOUNTED | MessageTag.ISPERSISTED 表示消息計(jì)數(shù)且存庫。
* app:RedPkgMsg: 這是自定義消息類型的名稱,測(cè)試的時(shí)候用"app:RedPkgMsg";
* */
@MessageTag(value = "app:RedPkgMsg", flag = MessageTag.ISCOUNTED | MessageTag.ISPERSISTED)
public class RedPackageMessage extends MessageContent {
//自定義的屬性
private String title;
private String storeName;
private String desc1;
private String desc2;
public RedPackageMessage() {
}
public static RedPackageMessage obtain(String title, String storeName, String desc1, String desc2) {
RedPackageMessage message = new RedPackageMessage();
message.title = title;
message.storeName = storeName;
message.desc1 = desc1;
message.desc2 = desc2;
return message;
}
/*
*
* 實(shí)現(xiàn) encode() 方法,該方法的功能是將消息屬性封裝成 json 串,
* 再將 json 串轉(zhuǎn)成 byte 數(shù)組,該方法會(huì)在發(fā)消息時(shí)調(diào)用,如下面示例代碼:
* */
@Override
public byte[] encode() {
JSONObject jsonObj = new JSONObject();
try {
jsonObj.put("title", this.getTitle());
jsonObj.put("storeName", this.getStoreName());
jsonObj.put("desc1", this.getDesc1());
jsonObj.put("desc2", this.getDesc2());
} catch (JSONException e) {
Log.e("JSONException", e.getMessage());
}
try {
return jsonObj.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/*
* 覆蓋父類的 MessageContent(byte[] data) 構(gòu)造方法,該方法將對(duì)收到的消息進(jìn)行解析,
* 先由 byte 轉(zhuǎn)成 json 字符串,再將 json 中內(nèi)容取出賦值給消息屬性。
* */
public RedPackageMessage(byte[] data) {
String jsonStr = null;
try {
jsonStr = new String(data, "UTF-8");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
try {
JSONObject jsonObj = new JSONObject(jsonStr);
if (jsonObj.has("title"))
setTitle(jsonObj.optString("title"));
if (jsonObj.has("storeName"))
setStoreName(jsonObj.optString("storeName"));
if (jsonObj.has("desc1"))
setDesc1(jsonObj.optString("desc1"));
if (jsonObj.has("desc2"))
setDesc2(jsonObj.optString("desc2"));
} catch (JSONException e) {
Log.d("JSONException", e.getMessage());
}
}
//給消息賦值。
public RedPackageMessage(Parcel in) {
setTitle(ParcelUtils.readFromParcel(in));//該類為工具類,消息屬性
//這里可繼續(xù)增加你消息的屬性
setStoreName(ParcelUtils.readFromParcel(in));//該類為工具類,消息屬性
setDesc1(ParcelUtils.readFromParcel(in));//該類為工具類,消息屬性
setDesc2(ParcelUtils.readFromParcel(in));//該類為工具類,消息屬性
}
/**
* 讀取接口,目的是要從Parcel中構(gòu)造一個(gè)實(shí)現(xiàn)了Parcelable的類的實(shí)例處理。
*/
public static final Creator<RedPackageMessage> CREATOR = new Creator<RedPackageMessage>() {
@Override
public RedPackageMessage createFromParcel(Parcel source) {
return new RedPackageMessage(source);
}
@Override
public RedPackageMessage[] newArray(int size) {
return new RedPackageMessage[size];
}
};
@Override
public int describeContents() {
return 0;
}
/**
* 將類的數(shù)據(jù)寫入外部提供的 Parcel 中。
*
* @param dest 對(duì)象被寫入的 Parcel。
* @param flags 對(duì)象如何被寫入的附加標(biāo)志。
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
ParcelUtils.writeToParcel(dest, getTitle());
ParcelUtils.writeToParcel(dest, getStoreName());
ParcelUtils.writeToParcel(dest, getDesc1());
ParcelUtils.writeToParcel(dest, getDesc2());
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public String getDesc1() {
return desc1;
}
public void setDesc1(String desc1) {
this.desc1 = desc1;
}
public String getDesc2() {
return desc2;
}
public void setDesc2(String desc2) {
this.desc2 = desc2;
}
}
2.自定義消息提供者
package com.example.mydemoplugin.PhoneInfoProvider;
import android.content.ClipboardManager;
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.example.mydemoplugin.R;
import io.rong.imkit.RongIM;
import io.rong.imkit.emoticon.AndroidEmoji;
import io.rong.imkit.model.ProviderTag;
import io.rong.imkit.model.UIMessage;
import io.rong.imkit.utilities.OptionsPopupDialog;
import io.rong.imkit.widget.provider.IContainerItemProvider;
import io.rong.imlib.RongIMClient;
import io.rong.imlib.model.Message;
/**
* Created by zhangbowen on 6/15/21.PhoneInfo的布局
* desc新建一個(gè)消息類繼承 IContainerItemProvider.MessageProvider 類,實(shí)現(xiàn)對(duì)應(yīng)接口方法,
* 1.注意開頭的注解!
* 2.注意泛型!
*/
@ProviderTag(
messageContent = RedPackageMessage.class,
showReadState = true
)
public class RedPackageItemProvider extends IContainerItemProvider.MessageProvider<RedPackageMessage> {
public RedPackageItemProvider() {
}
@Override
public View newView(Context context, ViewGroup viewGroup) {
//這就是展示在會(huì)話界面的自定義的消息的布局
View view = LayoutInflater.from(context).inflate(R.layout.item_redpackage_message, null);
ViewHolder holder = new ViewHolder();
holder.ll_msg = (FrameLayout) view.findViewById(R.id.ll_msg);
holder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
holder.tvStoreName = (TextView) view.findViewById(R.id.tv_store_name);
holder.tvDesc1 = (TextView) view.findViewById(R.id.tv_desc1);
holder.tvDesc2 = (TextView) view.findViewById(R.id.tv_desc2);
view.setTag(holder);
return view;
}
@Override
public void bindView(View view, int i, RedPackageMessage redPackageMessage, UIMessage message) {
//根據(jù)需求,適配數(shù)據(jù)
ViewHolder holder = (ViewHolder) view.getTag();
if (message.getMessageDirection() == Message.MessageDirection.SEND) {//消息方向,自己發(fā)送的
// holder.ll_msg.setBackgroundResource(io.rong.imkit.R.drawable.rc_ic_bubble_right);
} else {
// holder.ll_msg.setBackgroundResource(io.rong.imkit.R.drawable.rc_ic_bubble_left);
}
// AndroidEmoji.ensure((Spannable) holder.message.getText());//顯示消息中的 Emoji 表情。
holder.tvTitle.setText(redPackageMessage.getTitle());
holder.tvStoreName.setText(redPackageMessage.getStoreName());
holder.tvDesc1.setText(redPackageMessage.getDesc1());
holder.tvDesc2.setText(redPackageMessage.getDesc2());
}
@Override
public Spannable getContentSummary(RedPackageMessage redPackageMessage) {
return new SpannableString(redPackageMessage.getDesc1());
}
@Override
public void onItemClick(View view, int i, RedPackageMessage redPackageMessage, UIMessage uiMessage) {
}
@Override
public void onItemLongClick(View view, int i, RedPackageMessage redPackageMessage, UIMessage uiMessage) {
//實(shí)現(xiàn)長(zhǎng)按刪除等功能,咱們直接復(fù)制融云其他provider的實(shí)現(xiàn)
String[] items1;//復(fù)制,刪除
items1 = new String[]{view.getContext().getResources().getString(io.rong.imkit.R.string.rc_dialog_item_message_copy), view.getContext().getResources().getString(io.rong.imkit.R.string.rc_dialog_item_message_delete)};
OptionsPopupDialog.newInstance(view.getContext(), items1).setOptionsPopupDialogListener(new OptionsPopupDialog.OnOptionsItemClickedListener() {
public void onOptionsItemClicked(int which) {
// Log.e("實(shí)現(xiàn)長(zhǎng)按刪除等功能",uiMessage.getMessage().getMessageId()+"---"+redPackageMessage.describeContents()+"-----"+uiMessage.getMessage());
//長(zhǎng)安復(fù)制暫時(shí)沒有實(shí)現(xiàn)
if (which == 0) {
ClipboardManager clipboard = (ClipboardManager) view.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
// clipboard.setText(redPackageMessage.encode());//這里是自定義消息的消息屬性
} else if (which == 1) {
RongIM.getInstance().deleteMessages(new int[]{uiMessage.getMessage().getMessageId()}, (RongIMClient.ResultCallback) null);
}
}
}).show();
}
private static class ViewHolder {
TextView tvTitle, tvStoreName, tvDesc1, tvDesc2;
FrameLayout ll_msg;
}
}
3.注意自定義消息的布局:(整體布局外面再嵌套一層)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="220dp"
android:layout_height="100dp"
android:background="#eca24f">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="23dp"
android:layout_marginTop="8dp"
android:text="標(biāo)題"
android:textColor="#333fff"
android:textSize="12dp" />
<TextView
android:id="@+id/tv_store_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="70dp"
android:layout_marginTop="36dp"
android:singleLine="true"
android:text="店名店名店名店名店名店名店名店名店名"
android:textColor="@color/white"
android:textSize="15dp" />
<TextView
android:id="@+id/tv_desc1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="70dp"
android:layout_marginTop="55dp"
android:singleLine="true"
android:text="送你一個(gè)現(xiàn)金紅包"
android:textColor="@color/white"
android:textSize="12dp" />
<TextView
android:id="@+id/tv_desc2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="70dp"
android:layout_marginTop="73dp"
android:singleLine="true"
android:text="點(diǎn)擊領(lǐng)取紅包"
android:textColor="@color/white"
android:textSize="12dp" />
</RelativeLayout>
</FrameLayout>

4.注冊(cè)消息類型以及消息提供者
RongIM.init(this);
//注意,要在初始化之后注冊(cè)
RongIM.registerMessageType(RedPackageMessage.class);
RongIM.registerMessageTemplate(new RedPackageItemProvider());
5.官網(wǎng)測(cè)試發(fā)消息

6.自定義紅包Plugin
/**
* Created by zhangbowen on 6/15/21.
* 自定義紅包Plugin
**/
public class MyPlugin implements IPluginModule {
@Override
public Drawable obtainDrawable(Context context) {
return ContextCompat.getDrawable(context, io.rong.imkit.R.drawable.rc_cs_evaluate_selector);
}
@Override
public String obtainTitle(Context context) {
return "紅包";
}
@Override
public void onClick(Fragment fragment, RongExtension rongExtension) {
Log.e("發(fā)紅包啦?。?!", rongExtension.getTargetId());
RedPackageMessage redPackageMessage = RedPackageMessage.obtain("測(cè)試" + new Random().nextInt(1000), "商店名稱" + new Random().nextInt(1000), "描述" + new Random().nextInt(1000), "描述" + new Random().nextInt(1000));
RongIM.getInstance().sendMessage(Conversation.ConversationType.PRIVATE, rongExtension.getTargetId(), redPackageMessage, "測(cè)試一下pushContent" + new Random().nextInt(100), "測(cè)試一下pushData" + new Random().nextInt(100)
, new IRongCallback.ISendMediaMessageCallback() {
@Override
public void onAttached(Message message) {
}
@Override
public void onSuccess(Message message) {
}
@Override
public void onError(Message message, RongIMClient.ErrorCode errorCode) {
}
@Override
public void onProgress(Message message, int i) {
}
@Override
public void onCanceled(Message message) {
}
});
}
@Override
public void onActivityResult(int i, int i1, Intent intent) {
Log.e("發(fā)紅包啦?。。?, "onActivityResult");
}
}
/**
* Created by zhangbowen on 6/15/21.
* Plugin自定義類型
**/
public class SealExtensionModule extends DefaultExtensionModule {
@Override
public List<IPluginModule> getPluginModules(Conversation.ConversationType conversationType) {
List<IPluginModule> pluginModuleList = new ArrayList<>();
IPluginModule image = new ImagePlugin();
IPluginModule location = new DefaultLocationPlugin();
// IPluginModule audio = new AudioPlugin();
// IPluginModule video = new VideoPlugin();
IPluginModule file = new FilePlugin();
IPluginModule myPlugin = new MyPlugin();
if (conversationType.equals(Conversation.ConversationType.GROUP) ||
conversationType.equals(Conversation.ConversationType.DISCUSSION) ||
conversationType.equals(Conversation.ConversationType.PRIVATE)) {
pluginModuleList.add(image);
pluginModuleList.add(location);
// pluginModuleList.add(audio);
// pluginModuleList.add(video);
pluginModuleList.add(file);
pluginModuleList.add(myPlugin);
} else {
pluginModuleList.add(image);
}
return pluginModuleList;
}
@Override
public List<IEmoticonTab> getEmoticonTabs() {
return super.getEmoticonTabs();
}
}
7.將自定義好的Plugin重新注冊(cè)(在init初始化之后注冊(cè))
private void registerExtensionPlugin() {
List<IExtensionModule> moduleList = RongExtensionManager.getInstance().getExtensionModules();
IExtensionModule defaultModule = null;
if (moduleList != null) {
for (IExtensionModule module : moduleList) {
if (module instanceof DefaultExtensionModule) {
defaultModule = module;
break;
}
}
if (defaultModule != null) {
//移除已注冊(cè)的默認(rèn)模塊,替換成自定義模塊RongExtensionManager.getInstance().unregisterExtensionModule(defaultModule);
RongExtensionManager.getInstance().unregisterExtensionModule(defaultModule);
RongExtensionManager.getInstance().registerExtensionModule(new SealExtensionModule());
}
}
}
以上就是關(guān)于融云在項(xiàng)目中的使用,融云提供很多功能,希望以后有機(jī)會(huì)再深入研究吧!
簡(jiǎn)單demo下載地址