融云快速集成Demo+自定義消息+連接+會(huì)話界面+會(huì)話列表+發(fā)送消息+用戶信息提供者

項(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#新建消息

  1. 自定義消息實(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>
WeChatd051bc561c017d0e6e02e2410ee9c7aa.png

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


WeChatb0b6e1c0d1863f8cdc08bfcd9bd2ad24.png

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下載地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 偶然發(fā)現(xiàn),用過一些,分享給大家 { "XcodeChaJian": [ { "Dname":"...
    MonkeyHan閱讀 7,012評(píng)論 0 4
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,899評(píng)論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風(fēng)險(xiǎn)厭惡者,不喜歡去冒險(xiǎn),但是人生放棄了冒險(xiǎn),也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 8,171評(píng)論 0 4

友情鏈接更多精彩內(nèi)容