diff --git a/docs/.vuepress/navbar/index.ts b/docs/.vuepress/navbar/index.ts
index c71e9fe9c..f494f0c2f 100644
--- a/docs/.vuepress/navbar/index.ts
+++ b/docs/.vuepress/navbar/index.ts
@@ -72,66 +72,66 @@ export const zhNavbar = navbar([
},
]
},
- // {
- // text: 'UIKit',
- // children: [
- // {
- // text: '平台',
- // children: [
- // {
- // text: 'Android',
- // icon: '/icon-Android.svg',
- // link: '/uikit/android/overview.html'
- // },
- // {
- // text: 'iOS',
- // icon: '/icon-iOS.svg',
- // link: '/uikit/ios/overview.html'
- // },
- // // {
- // // text: 'Web',
- // // icon: '/icon-web.svg',
- // // link: '/uikit/web/overview.html'
- // // },
- // // {
- // // text: 'Windows',
- // // icon: '/icon-windows.svg',
- // // link: '/uikit/windows/overview.html'
- // // }
- // ]
- // },
- // {
- // text: '框架',
- // children: [
- // {
- // text: 'React Native',
- // icon: '/icon-ReactNative.svg',
- // link: '/uikit/react-native/overview.html'
- // },
- // {
- // text: 'Flutter',
- // icon: '/icon-flutter.svg',
- // link: '/uikit/flutter/overview.html'
- // },
- // // {
- // // text: 'Unity',
- // // icon: '/icon-unity.svg',
- // // link: '/uikit/unity/overview.html'
- // // },
- // // {
- // // text: '小程序',
- // // icon: '/icon-mini-program.svg',
- // // link: '/uikit/applet/overview.html'
- // // },
- // // {
- // // text: 'uni-app',
- // // icon: '/icon-uni-app.svg',
- // // link: '/uikit/applet/uniapp.html'
- // // },
- // ]
- // },
- // ]
- // },
+ {
+ text: 'UIKit',
+ children: [
+ {
+ text: '平台',
+ children: [
+ {
+ text: 'Android',
+ icon: '/icon-Android.svg',
+ link: '/uikit/android/overview.html'
+ },
+ {
+ text: 'iOS',
+ icon: '/icon-iOS.svg',
+ link: '/uikit/ios/overview.html'
+ },
+ // {
+ // text: 'Web',
+ // icon: '/icon-web.svg',
+ // link: '/uikit/web/overview.html'
+ // },
+ // {
+ // text: 'Windows',
+ // icon: '/icon-windows.svg',
+ // link: '/uikit/windows/overview.html'
+ // }
+ ]
+ },
+ {
+ text: '框架',
+ children: [
+ {
+ text: 'React Native',
+ icon: '/icon-ReactNative.svg',
+ link: '/uikit/react-native/overview.html'
+ },
+ {
+ text: 'Flutter',
+ icon: '/icon-flutter.svg',
+ link: '/uikit/flutter/overview.html'
+ },
+ // {
+ // text: 'Unity',
+ // icon: '/icon-unity.svg',
+ // link: '/uikit/unity/overview.html'
+ // },
+ // {
+ // text: '小程序',
+ // icon: '/icon-mini-program.svg',
+ // link: '/uikit/applet/overview.html'
+ // },
+ // {
+ // text: 'uni-app',
+ // icon: '/icon-uni-app.svg',
+ // link: '/uikit/applet/uniapp.html'
+ // },
+ ]
+ },
+ ]
+ },
{
text: 'API 参考',
children: [
diff --git a/docs/.vuepress/sidebar/document.ts b/docs/.vuepress/sidebar/document.ts
index 825f9fb3a..b62a0d0ed 100644
--- a/docs/.vuepress/sidebar/document.ts
+++ b/docs/.vuepress/sidebar/document.ts
@@ -103,7 +103,7 @@ const documentSidebar = [
text: '其他',
children: [
{ text: '错误码', link: 'error.html' },
- { text: 'EaseIMKit 使用指南', link: 'easeimkit.html', except: ['web', 'windows', 'react-native', 'flutter', 'unity'] },
+ //{ text: 'EaseIMKit 使用指南', link: 'easeimkit.html', except: ['web', 'windows', 'react-native', 'flutter', 'unity'] },
{ text: 'EaseCallKit 使用指南', link: 'easecallkit.html', except: ['web', 'windows', 'react-native', 'flutter', 'unity'] },
],
except: ['applet', 'server-side']
diff --git a/docs/.vuepress/sidebar/uikit.ts b/docs/.vuepress/sidebar/uikit.ts
index c01824227..80fb32ec4 100644
--- a/docs/.vuepress/sidebar/uikit.ts
+++ b/docs/.vuepress/sidebar/uikit.ts
@@ -18,11 +18,20 @@ const uikitSidebar = [
collapsible: 子菜单是否允许展开/收起,true: 允许; false: 不允许。请参考「子菜单示例」
children: 子菜单。请参考「子菜单示例」
*/
- text: '快速开始',
- children: [
- { text: 'UIKit 集成', link: 'overview.html' },
- ]
+ text: 'UIKit 介绍', link: 'overview.html',
+ except: ['android', 'ios']
},
+ {
+ text: '快速开始', link: 'quickstart.html',
+ },
+ {
+ text: '集成聊天页面', link: 'key_function_chat_page.html',
+ except: ['android', 'ios']
+ },
+ {
+ text: '集成会话列表页面', link: 'key_function_conversation_list.html',
+ except: ['android', 'ios']
+ }
]
function buildDocSidebar() {
diff --git a/docs/document/android/easeimkit.md b/docs/document/android/easeimkit.md
deleted file mode 100644
index 572314c07..000000000
--- a/docs/document/android/easeimkit.md
+++ /dev/null
@@ -1,1261 +0,0 @@
-# EaseIMKit 使用指南
-
-
-
-在您阅读此文档时,我们假定您已经具备了基础的 Android 应用开发经验,并能够理解相关基础概念。此文档是针对导入 EaseIMKit 库的快速集成文档,如果只是导入 SDK 集成使用,请参考 [环信即时通讯 IM Android 快速开始](quickstart.html)。
-
-## 简介
-
-EaseIMKit 是什么?
-
-**EaseIMKit** 是 EaseUI 的升级版,是基于环信 IM SDK 的一款 UI 组件库,它提供了一些通用的 UI 组件,例如‘会话列表’、‘聊天界面’和‘联系人列表’等,开发者可根据实际业务需求通过该组件库快速地搭建自定义 IM 应用。EaseIMKit 中的组件在实现 UI 功能的同时,调用 IM SDK 相应的接口实现 IM 相关逻辑和数据的处理,因而开发者在使用 EaseIMKit 时只需关注自身业务或个性化扩展即可。
-
-EaseIMKit 源码地址
-
-- [EaseIMKit](https://github.com/easemob/easeui/tree/EaseIMKit)
-
-使用 EaseIMKIt 的环信 IM APP 源码地址:
-
-- [环信 IM](https://github.com/easemob/chat-android)
-
-## 导入 EaseIMKit
-
-### 开发环境要求
-
-- Android Studio 3.2 以上
-- Gradle 4.6 以上
-- targetVersion 26 以上
-- Android SDK API 19 以上
-- Java JDK 1.8 以上
-
-### 集成说明
-
-EaseIMKit 支持 Gradle 接入和 Module 源码集成
-
-#### Gradle 接入集成
-
-:::notice 重大变动
-远程仓库统一由 JCenter 迁移到 `MavenCentral`,依赖库的域名由 “com.hyphenate” 修改为 “io.hyphenate”,详见 [环信即时通讯 IM Android 快速开始](quickstart.html)。
-:::
-
-```gradle
-implementation 'io.hyphenate:ease-im-kit:xxx版本'
-implementation 'io.hyphenate:hyphenate-chat:xxx版本'
-```
-
-**EaseIMKit 必须依赖环信 IM SDK,因而在使用 EaseIMKit 时必须同时添加环信 IM SDK 依赖。**
-
-:::notice
-
-1. IM SDK **3.8.0** 版本以后,远程依赖的 `artifactId` 修改为 `hyphenate-chat`,且该版本以后中不再包含音视频相关逻辑。
-2. IM SDK **3.8.0** 以下,远程依赖,包含音视频的 `artifactId` 为 `hyphenate-sdk`,不包含音视频的 `artifactId` 为 `hyphenate-sdk-lite`。如果想使用不包含音视频通话的 SDK,用 `implementation 'io.hyphenate:hyphenate-sdk-lite:xxx版本`'。
-
-版本号参考 [Android SDK 更新日志](releasenote.html)。
-:::
-
-#### Module 源码集成
-
-```gradle
-implementation project(':ease-im-kit')
-```
-
-#### 依赖的第三方库
-
-- Glide: 图片处理库,显示用户头像时用到。
-- BaiduLBS_Android.jar: 百度地图定位库。
-
-#### 关于位置消息说明
-
-EaseIMKit 中位置消息使用的是百度地图定位 jar 包,为了防止出现百度地图类冲突的问题,EaseIMKit 库打包时对百度地图定位 jar 包采用了只编译的方式,这就要求如果开发者想要使用位置消息,需要在自己的项目中添加百度地图定位的 jar 包及其 so 文件。
-
-### 初始化
-
-在 application 的 onCreate 下调用初始化 EaseIMKit 的方法。
-
-:::notice
-EaseIMKit 初始化里已包含 SDK 的初始化,不需要再去调用 SDK 的初始化。
-:::
-
-```java
-public class DemoApplication extends Application {
- @Override
- public void onCreate() {
- super.onCreate();
-
- //EaseIM 初始化
- if(EaseIM.getInstance().init(context, options)){
- //在做打包混淆时,关闭 debug 模式,避免消耗不必要的资源
- EMClient.getInstance().setDebugMode(true);
- //EaseIM 初始化成功之后再调用注册消息监听的代码 ...
- }
- }
-}
-```
-
-## 快速搭建
-
-EaseIMKit 封装了常用 IM 功能,提供了会话,聊天及联系人等基本的 fragment,旨在帮助开发者快速集成环信 SDK。
-
-### 创建会话列表界面
-
-EaseIMKit 提供了 EaseConversationListFragment,需要将其或者其子类添加到 Activity 中。开发者需要对刷新事件(新消息,删除消息,删除会话等)进行处理。
-
-![img](@static/images/android/easeim.jpeg)
-
-:::notice
-要实现自定义头像及昵称,请参考 [设置头像和昵称](userprofile.html#设置当前用户的属性)。
-:::
-
-### 创建聊天界面
-
-EaseIMKit 提供了 EaseChatFragment,添加到 Activity 中并传递相应的参数即可用。
-必须向 EaseChatFragment 传递的参数为:
-
-- `conversationId`——会话 ID,单聊时指对方 ID,群聊和聊天室时指群和聊天室 ID;
-- `chatType`——聊天类型,整型,分别为单聊(1)、群聊(2)和聊天室(3);
-
-可选传递参数为:
-
-- `history_msg_id`——消息 ID,用于查询历史记录时的定位消息 ID;
-- `isRoaming`——是否开启漫游,布尔类型,用于标记是否优先从服务器拉取消息。
-
-```java
-public class ChatActivity extends BaseActivity {
- private EaseChatFragment chatFragment;
-
- @Override
- protected void onCreate(Bundle arg0) {
- super.onCreate(arg0);
- setContentView(R.layout.em_activity_chat);
- //use EaseChatFratFragment
- chatFragment = new EaseChatFragment();
- //pass parameters to chat fragment
- chatFragment.setArguments(getIntent().getExtras());
- getSupportFragmentManager().beginTransaction().add(R.id.container, chatFragment).commit();
- }
-}
-```
-
-![img](@static/images/android/easeim1.jpeg)
-
-### 添加联系人界面
-
-EaseIMKit 提供了 EaseContactListFragment,添加其及其子类到 Activity 中。开发者需要对刷新事件(添加联系人,删除联系人等)进行处理。
-
-![img](@static/images/android/easeim2.jpeg)
-
-## 设置样式
-
-### 设置标题栏
-
-EaseIMKit 提供了自定义的标题栏控件 EaseTitleBar。
-
-![img](@static/images/android/easeim-titlebar.jpeg)
-
-标题栏除了做为 View 所具有的属性功能外,还可以设置标题的位置等。
-
-xml 中设置如下:
-
-```xml
-
-```
-
-其中 `titleBarDisplayHomeAsUpEnabled` 属性为设置返回按钮是否可见,设置标题位置可设置 `titleBarTitlePosition`,可选值为 `center`,`left` 和 `right`。
-
-也可进行代码设置,如下:
-
-```java
-EaseTitleBar titleBarMessage = findViewById(R.id.title_bar_message);
-//设置右侧菜单图标
-titleBarMessage.setRightImageResource(R.drawable.chat_user_info);
-//设置标题
-titleBarMessage.setTitle("标题");
-//设置标题位置
-titleBarMessage.setTitlePosition(EaseTitleBar.TitlePosition.Left);
-//设置右侧菜单图标的点击事件
-titleBarMessage.setOnRightClickListener(this);
-//设置返回按钮的点击事件
-titleBarMessage.setOnBackPressListener(this);
-```
-
-当然设置右侧菜单,您也可以通过 Android 提供的添加 `menu xml` 的形式实现。修改按钮图标,可以调用 `titleBarMenuResource` 属性进行设置。
-
-### 设置会话列表
-
-会话列表可以修改如下样式:
-
-- 头像:头像大小,头像形状(方形,带圆角的方形,圆形),描边
-- 标题、内容、时间等文字:字体大小,字体颜色
-- 未读消息:可设置是否展示,展示位置(左式和右式)
-
-在 `EaseConversationListFragment` 及其子类中可以直接获取到 `EaseConversationListLayout` 这个控件,然后通过这个控件进行设置。
-
-代码如下:
-
-```java
-//设置头像尺寸
-conversationListLayout.setAvatarSize(EaseCommonUtils.dip2px(mContext, 50));
-//设置头像样式:0 为默认,1 为圆形,2 为方形(设置方形时,需要配合设置 avatarRadius,默认的 avatarRadius 为 50dp)
-conversationListLayout.setAvatarShapeType(2);
-//设置圆角半径
-conversationListLayout.setAvatarRadius((int) EaseCommonUtils.dip2px(mContext, 5));
-//设置标题字体的颜色
-conversationListLayout.setTitleTextColor(ContextCompat.getColor(mContext, R.color.red));
-//设置是否隐藏未读消息数,默认为不隐藏
-conversationListLayout.hideUnreadDot(false);
-//设置未读消息数展示位置,默认为左侧
-conversationListLayout.showUnreadDotPosition(EaseConversationSetStyle.UnreadDotPosition.LEFT);
-```
-
-效果如下图:
-
-![img](@static/images/android/easeim3.jpeg)
-更多样式请参考 EaseContactListLayout 控件。
-
-#### 增加长按菜单项
-
-`EaseConversationListLayout` 提供了增加菜单项的 API,开发者可方便的增加更多的菜单功能。
-示例代码如下:
-
-```java
-@Override
-public void initView(Bundle savedInstanceState) {
- super.initView(savedInstanceState);
- ......
- conversationListLayout.addItemMenu(Menu.NONE, R.id.action_con_delete, 4, getString(R.string.ease_conversation_menu_delete));
-}
-
-......
-
-@Override
-public boolean onMenuItemClick(MenuItem item, int position) {
- EaseConversationInfo info = conversationListLayout.getItem(position);
- Object object = info.getInfo();
- if(object instanceof EMConversation) {
- switch (item.getItemId()) {
- case R.id.action_con_make_top :
- // 将会话置顶
- conversationListLayout.makeConversationTop(position, info);
- return true;
- case R.id.action_con_cancel_top :
- // 取消会话置顶
- conversationListLayout.cancelConversationTop(position, info);
- return true;
- case R.id.action_con_delete :
- showDeleteDialog(position, info);
- return true;
- }
- }
- return super.onMenuItemClick(item, position);
-}
-```
-
-### 设置聊天窗口
-
-聊天窗口包括标题栏(不包含在 EaseChatFragment 中),聊天区,输入区及扩展展示区,如下图所示:
-
-![img](@static/images/android/easeim4.png)
-
-标题区 EaseTitleBar 的具体布局及实现不在 EaseIMKit 库的聊天控件及 fragment 中,需要你自己去实现。
-开发者可以在 EaseChatFragment 中获取到 EaseChatLayout 这个控件,然后通过这个控件进一步获取到获取其他控件,代码如下:
-
-```java
-//获取到聊天列表控件
-EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
-//获取到菜单输入父控件
-EaseChatInputMenu chatInputMenu = chatLayout.getChatInputMenu();
-//获取到菜单输入控件
-IChatPrimaryMenu primaryMenu = chatInputMenu.getPrimaryMenu();
-//获取到扩展区域控件
-IChatExtendMenu chatExtendMenu = chatInputMenu.getChatExtendMenu();
-//获取到表情区域控件
-IChatEmojiconMenu emojiconMenu = chatInputMenu.getEmojiconMenu();
-```
-
-#### 修改聊天列表样式
-
-聊天列表区域可以修改背景,文字,气泡,是否展示昵称及聊天展示样式等,更多设置请参考 IChatMessageItemSet。
-
-#### 修改聊天列表背景
-
-```java
-//获取到聊天列表控件
-EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
-//设置聊天列表背景
-messageListLayout.setBackground(new ColorDrawable(Color.parseColor("#DA5A4D")));
-```
-
-效果如下图:
-
-![img](@static/images/android/easeim5.jpeg)
-
-#### 修改头像属性
-
-开发者可以设置默认头像和头像形状。
-
-```java
-//获取到聊天列表控件
-EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
-//设置默认头像
-messageListLayout.setAvatarDefaultSrc(ContextCompat.getDrawable(mContext, R.drawable.ease_default_avatar));
-//设置头像形状:0 为默认,1 为圆形,2 为方形
-messageListLayout.setAvatarShapeType(1);
-```
-
-效果如下图:
-
-![img](@static/images/android/easeim6.jpeg)
-
-#### 修改聊天文本
-
-开发者可以修改聊天文本的字体大小及字体颜色,发送方及接收方需保持一致。
-
-```java
-//获取到聊天列表控件
-EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
-//设置文本字体大小
-messageListLayout.setItemTextSize((int) EaseCommonUtils.sp2px(mContext, 18));
-//设置文本字体颜色
-messageListLayout.setItemTextColor(ContextCompat.getColor(mContext, R.color.red));
-```
-
-效果如下图:
-
-![img](@static/images/android/easeim7.jpeg)
-
-#### 修改时间线样式
-
-开发者可以修改时间线的背景,文字的大小及颜色。
-
-```java
-//获取到聊天列表控件
-EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
-//设置时间线的背景
-messageListLayout.setTimeBackground(ContextCompat.getDrawable(mContext, R.color.gray_normal));
-//设置时间线的文本大小
-messageListLayout.setTimeTextSize((int) EaseCommonUtils.sp2px(mContext, 18));
-//设置时间线的文本颜色
-messageListLayout.setTimeTextColor(ContextCompat.getColor(mContext, R.color.black));
-```
-
-效果如下图:
-
-![img](@static/images/android/easeim8.jpeg)
-
-#### 修改聊天列表展示样式
-
-开发者可以设置聊天列表的样式,发送方和接收方位于两侧还是位于一侧。
-
-```java
-//获取到聊天列表控件
-EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
-//设置聊天列表样式:两侧及均位于左侧
-messageListLayout.setItemShowType(EaseChatMessageListLayout.ShowType.LEFT);
-```
-
-效果如下图:
-
-![img](@static/images/android/easeim9.jpeg)
-
-#### 修改输入区样式
-
-输入区控件为 EaseChatInputMenu,它由输入控件 EaseChatPrimaryMenu,扩展控件 EaseChatExtendMenu 和表情控件 EaseEmojiconMenu 组成。
-
-```java
-//获取到菜单输入父控件
-EaseChatInputMenu chatInputMenu = chatLayout.getChatInputMenu();
-//获取到菜单输入控件
-IChatPrimaryMenu primaryMenu = chatInputMenu.getPrimaryMenu();
-//获取到扩展区域控件
-IChatExtendMenu chatExtendMenu = chatInputMenu.getChatExtendMenu();
-//获取到表情区域控件
-IChatEmojiconMenu emojiconMenu = chatInputMenu.getEmojiconMenu();
-```
-
-开发者可以修改菜单输入控件的样式,其有 5 种模式,即 `完整模式`,`不可用语音模式`,`不可用表情模式`,`不可用语音和表情模式` 和 `只有文本输入模式`。
-
-```java
-//获取到菜单输入父控件
-EaseChatInputMenu chatInputMenu = chatLayout.getChatInputMenu();
-//获取到菜单输入控件
-IChatPrimaryMenu primaryMenu = chatInputMenu.getPrimaryMenu();
-if(primaryMenu != null) {
- //设置菜单样式为不可用语音模式
- primaryMenu.setMenuShowType(EaseInputMenuStyle.DISABLE_VOICE);
-}
-```
-
-效果(EaseInputMenuStyle.DISABLE_VOICE)如下图:
-
-![img](@static/images/android/easeim10.jpeg)
-
-其他样式为:
-
-完整模式(EaseInputMenuStyle.All):
-
-![img](@static/images/android/easeim11.jpeg)
-
-不可用表情模式(EaseInputMenuStyle.DISABLE_EMOJICON):
-
-![img](@static/images/android/easeim12.jpeg)
-
-不可用语音和表情模式(EaseInputMenuStyle.DISABLE_VOICE_EMOJICON):
-
-![img](@static/images/android/easeim13.jpeg)
-
-只有文本输入模式(EaseInputMenuStyle.ONLY_TEXT):
-
-![img](@static/images/android/easeim14.jpeg)
-
-#### 增加自定义消息类型及其布局
-
-EaseIMKit 中已经为八种基本消息类型文本,表情,图片,视频,语音,文件,定位及 Custom 提供了基本的布局,ViewHolder 及 Delegate,开发者可以直接使用。但是这些类型很可能不能满足开发者的需求,那么就需要添加新的消息类型及其布局和逻辑。
-使用 EaseIMKit 只需按照以下 5 步即可快速添加自定义消息类型。
-下面我们以自定一个新的文本消息为例:
-
-1、新建 ChatTxtNewAdapterDelegate 继承 EaseMessageAdapterDelegate。
-
-```java
-public class ChatTxtNewAdapterDelegate extends EaseMessageAdapterDelegate {
- @Override
- protected EaseChatRow getEaseChatRow(ViewGroup parent, boolean isSender) {
- return null;
- }
-
- @Override
- protected EaseChatRowViewHolder createViewHolder(View view, MessageListItemClickListener itemClickListener) {
- return null;
- }
-}
-```
-
-2、新建 ChatRowTxtNew 继承 EaseChatRow 并实现相关方法
-
-```java
-public class ChatRowTxtNew extends EaseChatRow {
- private TextView contentView;
-
- public ChatRowTxtNew(Context context, boolean isSender) {
- super(context, isSender);
- }
-
- @Override
- protected void onInflateView() {
- inflater.inflate(isSender ? R.layout.ease_row_sent_txt_new : R.layout.ease_row_received_txt_new, this);
- }
-
- @Override
- protected void onFindViewById() {
- contentView = (TextView) findViewById(com.hyphenate.easeui.R.id.tv_chatcontent);
- }
-
- @Override
- protected void onSetUpView() {
- EMTextMessageBody txtBody = (EMTextMessageBody) message.getBody();
- contentView.setText(txtBody.getMessage());
- }
-}
-```
-
-布局文件以 R.layout.ease_row_sent_txt_new 为例,如下:
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-其中 ID 为 bubble 控件外的控件为发送端布局的基本组成,需要开发者拷贝进去。一般而言,开发者需要添加的控件,放在 bubble 控件内即可。如上所示,开发者只需在 `onFindViewById()` 方法中找到自己添加的控件,并在 `onSetUpView()` 方法内处理展示逻辑即可。
-
-3、新建 ChatTxtNewViewHolder 继承 EaseChatRowViewHolder 并实现相关方法
-
-```java
-public class ChatTxtNewViewHolder extends EaseChatRowViewHolder {
- public ChatTxtNewViewHolder(@NonNull View itemView, MessageListItemClickListener itemClickListener) {
- super(itemView, itemClickListener);
- }
-
- @Override
- public void onBubbleClick(EMMessage message) {
- super.onBubbleClick(message);
- // 实现相关点击方法
- }
-}
-```
-
-自定义的 ChatTxtNewViewHolder 可以复写实现 onBubbleClick(EMMessage message) 及 onResendClick(EMMessage message) 方法。需要注意的是,如果在其他地方设置了 MessageListItemClickListener 监听,并将相应的方法实现并返回 true 以后,ViewHolder 中的这两个方法会被拦截。 自定义的 ViewHolder 可以复写 onDetachedFromWindow() 方法,可以做一些释放资源的处理。 自定义的 ViewHolder 需要根据消息的方向(发送发或者接收方)对消息进行处理,可以分别复写 handleSendMessage(final EMMessage message) 或者 handleReceiveMessage(EMMessage message)。
-
-4、补全 ChatTxtNewAdapterDelegate 并重写 isForViewType 方法
-
-```java
-public class ChatTxtNewAdapterDelegate extends EaseMessageAdapterDelegate {
- @Override
- public boolean isForViewType(EMMessage item, int position) {
- return item.getType() == EMMessage.Type.TXT && item.getBooleanAttribute(EaseConstant.MESSAGE_ATTR_IS_TXT_NEW, false);
- }
-
- @Override
- protected EaseChatRow getEaseChatRow(ViewGroup parent, boolean isSender) {
- return new ChatRowTxtNew(parent.getContext(), isSender);
- }
-
- @Override
- protected EaseChatRowViewHolder createViewHolder(View view, MessageListItemClickListener itemClickListener) {
- return new ChatTxtNewViewHolder(view, itemClickListener);
- }
-}
-```
-
-:::notice
-(1)相同的消息类型(比如例子中消息类型是 EMMessage.Type.TXT)且通过标记判断类型时,在第 5 步注册对话类型时,应将该对话类型注册于基类的对话类型之前(即 ChatTxtNewAdapterDelegate 注册应在 EaseTextAdapterDelegate 之前)。
-
-(2)对于 `item.getBooleanAttribute(EaseConstant.MESSAGE_ATTR_IS_TXT_NEW, false)` 可以理解为一种标记,在发送消息时设置,如下;
-:::
-
-```java
-/**
- * 发送新文本消息
- * @param content
- */
-public void sendTxtNewMessage(String content) {
- EMMessage message = EMMessage.createTxtSendMessage(content, toChatUsername);
- message.setAttribute(DemoConstant.MESSAGE_ATTR_IS_TXT_NEW, true);
- EMClient.getInstance().chatManager().sendMessage(message);
-}
-```
-
-其中 `DemoConstant.MESSAGE_ATTR_IS_TXT_NEW` 为定义的一个常量,通常为字符串类型。
-
-5、注册 ChatTxtNewAdapterDelegate 对话类型(通过 EaseMessageTypeSetManager 进行注册)
-
-```java
-/**
- * 注册对话类型
- */
-private void registerConversationType() {
- EaseMessageTypeSetManager.getInstance()
- .addConversationType(EaseExpressionAdapterDelegate.class) //自定义表情
- .addConversationType(EaseFileAdapterDelegate.class) //文件
- ......
- .addConversationType(ChatTxtNewAdapterDelegate.class) //新文本消息
- .setDefaultConversionType(EaseTextAdapterDelegate.class); //文本
-}
-```
-
-需要开发者注意的是,自定义的消息类型需要注册到 EaseMessageTypeSetManager 中,具体用法可以参考环信 App 中的 [DemoHelper](https://github.com/easemob/chat-android/blob/master/app/src/main/java/com/hyphenate/easeim/DemoHelper.java) 类中的 `registerConversationType()` 方法,并在初始化时调用 `registerConversationType()` 方法。
-
-开发者在注册消息类型时,一定要在最后设置默认项(即调用 `setDefaultConversionType()`),并建议将 `EaseTextAdapterDelegate` 设置默认项。如果没有符合的消息类型,EaseIMKit 会选择默认的消息类型进行展示(注:需要展示的消息类型也需要符合默认消息的消息类型,否则会造成 EMMessageBody 强转时报错)。
-
-#### 增加长按菜单项
-
-EaseChatLayout 提供了增加菜单项的 API,开发者可方便的增加更多的菜单功能。
-
-示例代码如下:
-
-```java
-@Override
-public void initView(Bundle savedInstanceState) {
- super.initView(savedInstanceState);
- ......
- // 构建菜单项 model 并通过 EaseChatLayout 添加
- MenuItemBean itemMenu = new MenuItemBean(0, R.id.action_chat_forward, 11, getString(R.string.action_forward));
- itemMenu.setResourceId(R.drawable.ease_chat_item_menu_forward);
- chatLayout.addItemMenu(itemMenu );
-}
-
-......
-
-@Override
-public boolean onMenuItemClick(MenuItemBean item, EMMessage message) {
- switch (item.getItemId()) {
- case R.id.action_chat_forward :
- // 增加实现逻辑,并返回 true
- return true;
- }
- return false;
-}
-```
-
-#### 增加更多扩展功能
-
-EaseIMKit 提供了常用的一些扩展功能,比如发送图片,发送文件,发送定位等,但是实际开发中可能满足不了开发者的需求,EaseIMKit 提供了增加扩展功能的接口,以实现发送短视频消息、音视频通话等。
-
-以添加视频通话和音视频会议为例,示例代码如下:
-
-```java
-private void resetChatExtendMenu() {
- // 获取到扩展功能控件
- IChatExtendMenu chatExtendMenu = chatLayout.getChatInputMenu().getChatExtendMenu();
- if(chatExtendMenu == null) {
- return;
- }
- // 清除所有的扩展项
- chatExtendMenu.clear();
- // 添加自己需要的扩展功能
- chatExtendMenu.registerMenuItem(R.string.attach_picture, R.drawable.ease_chat_image_selector, EaseChatExtendMenu.ITEM_PICTURE);
- chatExtendMenu.registerMenuItem(R.string.attach_take_pic, R.drawable.ease_chat_takepic_selector, EaseChatExtendMenu.ITEM_TAKE_PICTURE);
- // 根据消息类型添加不同的扩展功能
- if(chatType == EaseConstant.CHATTYPE_SINGLE){
- chatExtendMenu.registerMenuItem(R.string.attach_media_call, R.drawable.em_chat_video_call_selector, ITEM_VIDEO_CALL);
- }
- if (chatType == EaseConstant.CHATTYPE_GROUP) { // 音视频会议
- chatExtendMenu.registerMenuItem(R.string.voice_and_video_conference, R.drawable.em_chat_video_call_selector, ITEM_CONFERENCE_CALL);
- }
-}
-
-......
-
-@Override
-public void onChatExtendMenuItemClick(View view, int itemId) {
- super.onChatExtendMenuItemClick(view, itemId);
- // 只需实现不满足需求或者开发者自己添加的扩展功能
- switch (itemId) {
- case ITEM_VIDEO_CALL:
- // 实现条目点击事件
- break;
- case ITEM_CONFERENCE_CALL:
- // 实现条目点击事件
- break;
- }
-}
-```
-
-#### 增加自定义表情
-
-EaseIMKit 也提供了增加自定义表情的接口,开发者可仿照 EmojiconExampleGroupData 进行定义类型的表情类,然后调用相应接口加入即可。
-
-代码如下:
-
-```java
-// 添加扩展表情
-chatLayout.getChatInputMenu().getEmojiconMenu().addEmojiconGroup(EmojiconExampleGroupData.getData());
-```
-
-### 设置联系人列表
-
-通讯录列表界面可以设置如下样式:
-
-- 展示模式:分为简洁模式和常规模式
-- 条目:设置条目背景,条目高度及 header 背景
-- 头像:设置默认头像,头像样式及头像大小等
-
-在 EaseContactListFragment 及其子类中可以直接获取到 EaseContactLayout 这个控件,然后通过这个控件进行设置。
-
-代码如下:
-
-```java
-// 获取列表控件
-EaseContactListLayout contactList = contactLayout.getContactList();
-// 设置条目高度
-contactList.setItemHeight((int) EaseCommonUtils.dip2px(mContext, 80));
-// 设置条目背景
-contactList.setItemBackGround(ContextCompat.getDrawable(mContext, R.color.gray));
-// 设置头像样式:0 为默认,1 为圆形,2 为方形(设置方形时,需要配合设置 avatarRadius,默认的 avatarRadius 为 50dp)
-contactList.setAvatarShapeType(2);
-// 设置头像圆角
-contactList.setAvatarRadius((int) EaseCommonUtils.dip2px(mContext, 5));
-// 设置 header 背景
-contactList.setHeaderBackGround(ContextCompat.getDrawable(mContext, R.color.white));
-```
-
-效果如图:
-
-![img](@static/images/android/easeim15.jpeg)
-
-设置简洁模式
-
-```java
-//设置为简洁模式
-contactLayout.showSimple();
-```
-
-效果如图:
-
-![img](@static/images/android/easeim16.jpeg)
-
-#### 增加长按菜单项
-
-EaseContactListLayout 提供了增加菜单项的 API,开发者可方便的增加更多的菜单功能。
-
-示例代码如下:
-
-```java
-// 通过增加 `OnPopupMenuPreShowListener` 监听,并在 `onMenuPreShow` 中增加菜单项更简单
-@Override
-public void onMenuPreShow(EasePopupMenuHelper menuHelper, int position) {
- super.onMenuPreShow(menuHelper, position);
- // 增加需要的菜单项,其中传入的第三项 order 决定了菜单项的位置
- menuHelper.addItemMenu(1, R.id.action_friend_block, 2, getString(R.string.em_friends_move_into_the_blacklist_new));
- menuHelper.addItemMenu(1, R.id.action_friend_delete, 1, getString(R.string.ease_friends_delete_the_contact));
-}
-
-......
-
-@Override
-public boolean onMenuItemClick(MenuItem item, int position) {
- EaseUser user = contactLayout.getContactList().getItem(position);
- switch (item.getItemId()) {
- case R.id.action_friend_block :
- // 增加处理逻辑并返回 `true`
- return true;
- case R.id.action_friend_delete:
- // 增加处理逻辑并返回 `true`
- return true;
- }
- return super.onMenuItemClick(item, position);
-}
-```
-
-#### 增加头布局
-
-EaseIMKit 默认是不再通讯录列表之前增加头布局的,但是内部预设了添加头布局的逻辑,开发者可通过 EaseContactListLayout 提供的 API 快速的增加一个或者多个头布局。
-
-示例代码如下:
-
-```java
-/**
- * 添加头布局
- */
-public void addHeader() {
- // 增加多个头布局
- contactLayout.getContactList().addCustomItem(CUSTOM_NEW_CHAT, R.drawable.em_friends_new_chat, getString(R.string.em_friends_new_chat));
- contactLayout.getContactList().addCustomItem(CUSTOM_GROUP_LIST, R.drawable.em_friends_group_chat, getString(R.string.em_friends_group_chat));
- contactLayout.getContactList().addCustomItem(CUSTOM_CHAT_ROOM_LIST, R.drawable.em_friends_chat_room, getString(R.string.em_friends_chat_room));
-}
-
-......
-
-@Override
-public void initListener() {
- super.initListener();
- ......
- contactLayout.getContactList().setOnCustomItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(View view, int position) {
- EaseContactCustomBean item = contactLayout.getContactList().getCustomAdapter().getItem(position);
- switch (item.getId()) {
- case CUSTOM_NEW_CHAT :
- // 增加实现逻辑
- break;
- case CUSTOM_GROUP_LIST :
- // 增加实现逻辑
- break;
- case CUSTOM_CHAT_ROOM_LIST :
- // 增加实现逻辑
- break;
- }
- }
- });
-}
-```
-
-### 设置用户头像和昵称属性
-
-#### 设置头像和昵称
-
-环信 IM SDK 不做用户信息存储,如果用户想要展示自定义的头像及昵称,可以通过 EaseUserProfileProvider 进行提供。
-
-首先需要在合适的时机去设置 EaseUserProfileProvider,例如:
-
-```java
-EaseIM.getInstance().setUserProvider(new EaseUserProfileProvider() {
- @Override
- public EaseUser getUser(String username) {
- //根据 username,从数据库中或者内存中取出之前保存的用户信息,如从数据库中取出的用户对象为 DemoUserBean
- DemoUserBean bean = getUserFromDbOrMemery(username);
- EaseUser user = new EaseUser(username);
- ......
- //设置用户昵称
- user.setNickname(bean.getNickname());
- //设置头像地址
- user.setAvatar(bean.getAvatar());
- //最后返回构建的 EaseUser 对象
- return user;
- }
-});
-```
-
-EaseIMKit 中会话列表,聊天列表及联系人列表,内部已经添加 EaseUserProfileProvider 的判断,当展示数据时优先从 EaseUserProfileProvider 获取头像和昵称数据,如果有则展示,如果没有头像采用默认头像,昵称展示为环信 ID。
-
-:::notice 建议方案
-开发者先将相关用户信息从服务器中获取并存储到数据库中,在 getUser(String username) 方法调用时,从数据库中根据 username(环信 ID)取出相应的用户数据,生成 EaseUser 对象 user,并给 user 赋值 nickname 及 avatar 属性,最后返回这个 user 即可。
-:::
-
-#### 统一设置头像样式
-
-EaseIMKit 提供了 `EaseAvatarOptions` 这个类用于全局配置头像的样式,包括形状,圆角半径,描边宽度及描边颜色。会话,聊天及联系人中已经增加了对于 EaseAvatarOptions 的支持。
-
-示例代码如下:
-
-```java
-//设置头像配置属性
-EaseIM.getInstance().setAvatarOptions(getAvatarOptions());
-......
-/**
- * 统一配置头像
- * @return
- */
-private EaseAvatarOptions getAvatarOptions() {
- EaseAvatarOptions avatarOptions = new EaseAvatarOptions();
- //设置头像形状为圆形,1 代表圆形,2 代表方形
- avatarOptions.setAvatarShape(1);
- return avatarOptions;
-}
-```
-
-使用时可以直接调用 EaseUserUtils 中的 `setUserAvatarStyle(EaseImageView imageView)` 方法即可设置。
-
-## 事件处理
-
-EaseIMKit 还帮助开发者实现了一系列的事件监听接口,比如条目的点击事件,条目的长按事件,菜单项的点击事件等。
-
-### 会话列表
-
-#### 条目点击事件
-
-开发者如果使用 `EaseConversationListFragment` 及其子类,可以重写 `onItemClick(View view, int position)` 方法即可。
-
-代码如下:
-
-```java
-@Override
-public void onItemClick(View view, int position) {
- super.onItemClick(view, position);
- //添加点击事件实现逻辑
-}
-```
-
-开发者如果直接使用的 `EaseConversationListLayout` 控件,则可通过该控件设置条目的点击事件。
-
-代码如下:
-
-```java
-conversationListLayout.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(View view, int position) {
- //添加点击事件实现逻辑
- }
-});
-```
-
-#### 长按事件及弹出菜单点击事件
-
-`EaseConversationListLayout` 中已经实现了一套默认的长按弹出菜单逻辑,开发者只需实现弹出菜单的点击事件即可。
-
-如果开发者使用的是 `EaseConversationListFragment` 及其子类,则直接重写 `onMenuItemClick(MenuItem item, int position)` 即可。
-
-代码如下:
-
-```java
-@Override
-public boolean onMenuItemClick(MenuItem item, int position) {
- //添加具体的点击事件实现逻辑,并返回 true
- return super.onMenuItemClick(item, position);
-}
-```
-
-开发者如果直接使用的 EaseConversationListLayout 控件,则可通过该控件设置弹出菜单的点击事件。
-
-代码如下:
-
-```java
-conversationListLayout.setOnPopupMenuItemClickListener(new OnPopupMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item, int position) {
- //添加具体的点击事件实现逻辑,并返回 true
- return false;
- }
-});
-```
-
-如果开发者需要自己实现弹出菜单,通过 EaseConversationListLayout 控件添加条目的长按监听并实现即可。
-
-代码如下:
-
-```java
-conversationListLayout.setOnItemLongClickListener(new OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(View view, int position) {
- //添加弹出菜单的逻辑,并返回 true
- return false;
- }
-});
-```
-
-### 聊天区域
-
-#### 聊天列表事件
-
-开发者如果使用的是 EaseChatFragment 及其子类,则可以根据需要重写相关的事件方法即可。聊天列表中的常用监听事件均封装到了 OnChatLayoutListener 接口中,EaseChatFragment 已经设置了该监听。
-
-`OnChatLayoutListener` 中有如下事件监听:
-
-```java
-public interface OnChatLayoutListener {
- /**
- * 点击消息 bubble 区域
- * @param message
- * @return
- */
- boolean onBubbleClick(EMMessage message);
-
- /**
- * 长按消息 bubble 区域
- * @param v
- * @param message
- * @return
- */
- boolean onBubbleLongClick(View v, EMMessage message);
-
- /**
- * 点击头像
- * @param username
- */
- void onUserAvatarClick(String username);
-
- /**
- * 长按头像
- * @param username
- */
- void onUserAvatarLongClick(String username);
-
- /**
- * 条目点击
- * @param view
- * @param itemId
- */
- void onChatExtendMenuItemClick(View view, int itemId);
-
- /**
- * EditText 文本变化监听
- * @param s
- * @param start
- * @param before
- * @param count
- */
- void onTextChanged(CharSequence s, int start, int before, int count);
-
- /**
- * 聊天中错误信息
- * @param code
- * @param errorMsg
- */
- void onChatError(int code, String errorMsg);
-
- /**
- * 用于监听其他人正在数据事件
- * @param action 输入事件 TypingBegin为开始 TypingEnd 为结束
- */
- default void onOtherTyping(String action){}
-
-}
-```
-
-如果开发者使用的是 EaseChatLayout 控件,则通过该控件实现 OnChatLayoutListener 接口即可。
-
-#### 长按事件及弹出菜单点击事件
-
-EaseChatLayout 中已经实现了一套默认的长按弹出菜单逻辑,并对默认的菜单项进行了处理,如果开发者对默认菜单项有其他实现需求,只需实现弹出菜单的点击事件即可。
-
-如果开发者使用的是 EaseChatFragment 及其子类,则直接重写 onMenuItemClick(MenuItemBean item, EMMessage message) 即可。
-
-代码如下:
-
-```java
-@Override
-public boolean onMenuItemClick(MenuItemBean item, EMMessage message) {
- //添加菜单条目点击事件实现逻辑,并返回 true
- //返回 true 表示不采用默认的实现逻辑
- return false;
-}
-```
-
-如果开发者需要在弹出菜单展示前对菜单项进行处理,重写 `onPreMenu(EasePopupWindowHelper helper, EMMessage message)` 并处理即可。
-
-示例代码如下:
-
-```java
-@Override
-public void onPreMenu(EasePopupWindowHelper helper, EMMessage message) {
- // 默认两分钟后,即不可撤回
- if(System.currentTimeMillis() - message.getMsgTime() > 2 * 60 * 1000) {
- helper.findItemVisible(R.id.action_chat_recall, false);
- }
- EMMessage.Type type = message.getType();
- helper.findItemVisible(R.id.action_chat_forward, false);
- switch (type) {
- case TXT:
- if(!message.getBooleanAttribute(DemoConstant.MESSAGE_ATTR_IS_VIDEO_CALL, false)
- && !message.getBooleanAttribute(DemoConstant.MESSAGE_ATTR_IS_VOICE_CALL, false)) {
- helper.findItemVisible(R.id.action_chat_forward, true);
- }
- break;
- case IMAGE:
- helper.findItemVisible(R.id.action_chat_forward, true);
- break;
- }
-
- if(chatType == DemoConstant.CHATTYPE_CHATROOM) {
- helper.findItemVisible(R.id.action_chat_forward, true);
- }
-}
-```
-
-开发者如果直接使用的 EaseChatLayout 控件,则可通过该控件设置弹出菜单的点击事件。
-
-代码如下:
-
-```java
-chatLayout.setOnPopupWindowItemClickListener(new OnMenuChangeListener() {
- @Override
- public void onPreMenu(EasePopupWindowHelper helper, EMMessage message) {
- // 菜单预处理逻辑
- }
- @Override
- public boolean onMenuItemClick(MenuItemBean item, EMMessage message) {
- // 菜单项点击事件,如果默认实现不满足,则自己实现并返回 true。
- return false;
- }
-});
-```
-
-如果开发者需要自己实现弹出菜单,通过 EaseChatLayout 控件找到 EaseChatMessageListLayout 并添加条目的长按监听并实现即可。
-
-代码如下:
-
-```java
-chatLayout.getChatMessageListLayout().setOnItemLongClickListener(new OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(View view, int position) {
- //添加弹出菜单的逻辑,并返回 true
- return false;
- }
-});
-```
-
-### 通讯录列表
-
-#### 条目点击事件
-
-开发者如果使用 EaseContactListFragment 及其子类,可以重写 `onItemClick(View view, int position)` 方法即可。
-
-代码如下:
-
-```java
-@Override
-public void onItemClick(View view, int position) {
- super.onItemClick(view, position);
- //添加点击事件实现逻辑
-}
-```
-
-开发者如果直接使用的 EaseContactLayout 控件,则可通过该控件找到 EaseContactListLayout 并设置条目的点击事件。
-
-代码如下:
-
-```java
-contactLayout.getContactList().setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(View view, int position) {
- //添加点击事件实现逻辑
- }
-});
-```
-
-#### 长按事件及弹出菜单点击事件
-
-EaseContactListLayout 中已经实现了一套默认的长按弹出菜单逻辑,开发者只需实现弹出菜单的点击事件即可。
-
-如果开发者使用的是 EaseContactListFragment 及其子类,则直接重写 `onMenuItemClick(MenuItem item, int position)` 即可。
-
-代码如下:
-
-```java
-@Override
-public boolean onMenuItemClick(MenuItem item, int position) {
- // 添加具体的点击事件实现逻辑,并返回 'true'
- return super.onMenuItemClick(item, position);
-}
-```
-
-如果开发者需要在弹出菜单展示前对菜单项进行处理,重写 `onMenuPreShow(EasePopupWindowHelper helper, EMMessage message)` 并处理即可。
-
-开发者如果直接使用的 EaseContactLayout 控件,则可通过该控件找到 EaseContactListLayout 并设置弹出菜单的点击事件。
-
-代码如下:
-
-```java
-contactLayout.getContactList().setOnPopupMenuItemClickListener(new OnPopupMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item, int position) {
- //添加具体的点击事件实现逻辑,并返回 true
- return false;
- }
-});
-```
-
-相应的菜单项预处理,需要通过 EaseContactListLayout 设置菜单预处理监听事件。 代码如下:
-
-```java
-contactLayout.getContactList().setOnPopupMenuPreShowListener(new OnPopupMenuPreShowListener() {
- @Override
- public void onMenuPreShow(EasePopupMenuHelper menuHelper, int position) {
- //菜单预处理逻辑
- }
-});
-```
-
-如果开发者需要自己实现弹出菜单,通过 EaseContactListLayout 控件添加条目的长按监听并实现即可。
-
-代码如下:
-
-```java
-contactLayout.getContactList().setOnItemLongClickListener(new OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(View view, int position) {
- //添加弹出菜单的逻辑,并返回 true
- return false;
- }
-});
-```
-
-## 功能扩展
-
-### 系统消息
-
-EaseIMKit 中 EaseConversationListLayout 已经封装了 IM 通知的展示逻辑,但是需要开发者将 IM 的通知封装成系统消息并保存到本地数据库。为了方便开发者封装成符合 EaseIMKit 能够使用的系统消息,EaseIMKit 中提供了 EaseSystemMsgManager 管理类,开发者可通过该管理类,方便的封装及更新系统消息。
-
-EaseIMKit 可处理的系统消息有如下要求:
-
-```java
-// 设置为文本消息
-EMMessage emMessage = EMMessage.createReceiveMessage(EMMessage.Type.TXT);
-// 设置 from 为固定的 "em_system"
-emMessage.setFrom(EaseConstant.DEFAULT_SYSTEM_MESSAGE_ID);
-emMessage.setMsgId(UUID.randomUUID().toString());
-emMessage.setStatus(EMMessage.Status.SUCCESS);
-```
-
-当然 EaseSystemMsgManager 管理类已经做了默认处理,开发者只需传入文本内容(会话列表中展示的内容)及扩展内容 ext(Map)即可。 示例如下:
-
-```java
-@Override
-public void onFriendRequestDeclined(String username) {
- EMLog.i("ChatContactListener", "onFriendRequestDeclined");
- Map ext = EaseSystemMsgManager.getInstance().createMsgExt();
- ext.put(DemoConstant.SYSTEM_MESSAGE_FROM, username);
- ext.put(DemoConstant.SYSTEM_MESSAGE_STATUS, InviteMessageStatus.BEREFUSED.name());
- EMMessage message = EaseSystemMsgManager.getInstance().createMessage(PushAndMessageHelper.getSystemMessage(ext), ext);
- ......
-}
-```
-
-同时 EaseConversationListLayout 提供了是否展示系统消息的 API,showSystemMessage(boolean show) 用于控制是否展示系统消息。
diff --git a/docs/document/ios/easeimkit.md b/docs/document/ios/easeimkit.md
deleted file mode 100644
index b3e5e2317..000000000
--- a/docs/document/ios/easeimkit.md
+++ /dev/null
@@ -1,1058 +0,0 @@
-# EaseIMKit 使用指南
-
-
-
-仍在使用旧版 EaseUI 的用户可参考旧版 EaseUI 的文档,旧版已不再维护。 旧版文档地址:[EaseUI 集成](https://docs-im.easemob.com/im/ios/other/easeui)
-
-## 简介
-
-EaseIMKit 是什么?
-
-EaseIMKit 是基于环信 IM SDK 的一款 UI 组件库,它提供了一些通用的 UI 组件,例如 ‘会话列表’、‘聊天界面’ 和 ‘联系人列表’ 等,开发者可根据实际业务需求通过该组件库快速地搭建自定义 IM 应用。EaseIMKit 中的组件在实现 UI 功能的同时,调用 IM SDK 相应的接口实现 IM 相关逻辑和数据的处理,因而开发者在使用 EaseIMKit 时只需关注自身业务或个性化扩展即可。
-
-EaseIMKit 源码地址
-
-- [EaseIMKit 工程](https://github.com/easemob/easeui_ios/tree/EaseIMKit)
-
-使用 EaseIMKit 环信 IM App 地址:
-
-- [环信 IM](https://github.com/easemob/chat-ios)
-
-## 导入
-
-### 支持系统版本要求
-
-- EaseIMKit 支持 iOS 10.0 及以上系统版本
-- EaseIM 支持 iOS 11.0 及以上系统版本
-
-## 快速集成
-
-### pod 方式集成
-
-```
-pod 'EaseIMKit'
-```
-
-需要在 `Podfile` 文件加上 `use_frameworks!`
-
-:::notice
-EaseIMKit: 对应 HyphenateChat SDK(HyphenateChat 不包含实时音视频,EaseIMKit 不包含音视频,EaseIM 依赖音视频库 EaseCallKit 后实现了音视频功能)
-
-EaseIMKit 中包含了拍照,发语音,发图片,发视频,发位置,发文件的功能,需要使用录音,摄像头,相册,地理位置的权限。需要在您项目的 info.plist 中添加对应权限。
-:::
-
-### 源码集成
-
-- [Github 下载源码](https://github.com/easemob/easeui_ios.git)
-
-执行命令:`git clone https://github.com/easemob/easeui_ios.git`
-
-- 创建 `Podfile` 文件并添加 EaseIMKit 源码依赖
-
- 1. 项目 `Podfile` 文件 和 `ProjectName.xcodeproj` 文件应在同一目录,如下图所示:
-
- ![img](@static/images/ios/easeimkit1.png)
-
- Podfile 文件示例:
-
- ```
- platform :ios, '11.0'
-
- source 'https://github.com/CocoaPods/Specs.git'
-
- target 'ProjectName' do
- pod 'EaseIMKit', :path => "../EaseUI/EaseIMKit"
- pod 'HyphenateChat', '3.8.4'
- end
- ```
-
- 2. EaseIMKit path 路径(如:pod 'EaseIMKit', :path ⇒ “../EaseUI/EaseIMKit”)需指向 EaseIMKit.podspec 文件所在目录,如下图所示:
-
- ![img](@static/images/ios/easeimkit2.png)
-
-- 项目集成本地 EaseIMKit 源码
-
- 1. 终端 cd 到 Podfile 文件所在目录,执行 pod install 命令在项目中安装 EaseIMKit 本地源码
- 2. 执行完成后,则在 Xcode 项目目录 Pods/Development Pods/ 可找到 EaseIMKit 源码,如下图所示:
-
- ![img](@static/images/ios/easeimkit3.png)
-
- 3. 可对源码进行符合自己项目目标的自定义修改
-
-- 成为社区贡献者
-
-如果在源码自定义过程中有任何通用自定义都可以给我们 [Github 仓库](https://github.com/easemob/easeui_ios.git) 提交代码成为社区贡献者!
-
-### 初始化
-
-第 1 步:引入相关头文件
-
-```objectivec
-#import
-```
-
-第 2 步:在在工程的 AppDelegate 中的以下方法中调用 EaseIMKitManager 的初始化方法一并初始化环信 SDK。(注: 此方法不需要重复调用)
-
-```objectivec
-- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- // Override point for customization after application launch.
- EMOptions *options = [EMOptions optionsWithAppkey:@"您的APPKEY"];
- [EaseIMKitManager initWithEMOptions:options];
- //再做登录操作,可准确接收到系统通知
- return YES;
-}
-```
-
-EaseIMKitManager 主要包含系统通知(好友申请,群邀请/申请)回调,未读总数回调等方法。 用户需要注册自己的类到 EaseIMKitManagerDelegate 才可收到未读总数变化回调。 用户需要添加 EaseIMKitSystemNotiDelegate 代理才可收到系统通知相关回调。
-
-系统通知相关回调接口,系统通知构造成了一个本地会话,每个新通知构造为一条本地消息。 系统通知所构造的会话的 conversationId 为 @“emsystemnotificationid”。
-
-#### 是否需要系统通知
-
-```objectivec
-/*!
- @method
- @brief 是否需要系统通知:好友/群 申请等
- @discussion 默认需要系统通知
- @result 返回: YES:需要; NO:不需要;
- */
-- (BOOL)isNeedsSystemNotification;
-```
-
-#### 收到系统通知所展示信息回调接口
-
-```objectivec
-/*!
- @method
- @brief 收到请求返回展示信息
- @param conversationId 会话 ID。
- * 对于单聊类型,会话 ID 同时也是对方用户的名称。
- * 对于群聊类型,会话 ID 同时也是对方群组的 ID,并不同于群组的名称。
- * 对于聊天室类型,会话 ID 同时也是聊天室的 ID,并不同于聊天室的名称。
-
- @param requestUser 请求方。
- @param reason 请求原因。
- @result 返回系统通知所展示信息。
- */
-- (NSString *)requestDidReceiveShowMessage:(NSString *)conversationId requestUser:(NSString *)requestUser reason:(EaseIMKitCallBackReason)reason;
-```
-
-对于返回值处理:空字符串不产生新 message / nil:使用默认实现 / 非空字符串且长度大于 0,使用该字符串产生新 message
-
-#### 收到系统通知扩展信息回调接口
-
-```objectivec
-/*!
- @method
- @brief 收到请求返回扩展信息
- @param conversationId 会话 ID。
- * 对于单聊类型,会话 ID 同时也是对方用户的名称。
- * 对于群聊类型,会话 ID 同时也是对方群组的 ID,并不同于群组的名称。
- * 对于聊天室类型,会话 ID 同时也是聊天室的 ID,并不同于聊天室的名称。
-
- @param requestUser 请求方。
- @param reason 请求原因。
- @result 返回系统通知携带扩展信息字典。
- */
-- (NSDictionary *)requestDidReceiveConversationExt:(NSString *)conversationId requestUser:(NSString *)requestUser reason:(EaseIMKitCallBackReason)reason;
-```
-
-#### 未读总数变化回调接口
-
-```objectivec
-/*!
- @method
- @brief 会话未读总数变化。
- @param unreadCount 当前会话列表的总未读数。
- */
-- (void)conversationsUnreadCountUpdate:(NSInteger)unreadCount;
-```
-
-## 快速搭建
-
-### 聊天会话快速搭建
-
-导入 EaseIMKit 头文件
-
-```objectivec
-#import
-```
-
-EaseIMKit 提供现成的聊天会话 ViewController,可以通过创建 EaseChatViewController 对象实例,嵌入进自己的聊天控制器方式(参考 EaseIM 中 EMChatViewController)实现对 EaseIMKit 聊天会话的集成。 创建聊天会话页实例,需传递用户‘环信 ID’或‘群 ID’ ,会话类型(EMConversationType)以及必须传入聊天视图配置数据模型 EaseChatViewModel 实例。
-
-```objectivec
-EaseChatViewModel *viewModel = [[EaseChatViewModel alloc]init];
-EaseChatViewController *chatController = [EaseChatViewController initWithConversationId:@"custom"
- conversationType:EMConversationTypeChat
- chatViewModel:viewModel];
-[self addChildViewController:chatController];
-[self.view addSubview:chatController.view];
-chatController.view.frame = self.view.bounds;
-```
-
-聊天控制器嵌入自己的聊天页后还需传入消息列表 messageList 以供 EaseChatViewController 展示使用
-
-```objectivec
-//isScrollBottom 是否滑动到页面底部
-- (void)loadData:(BOOL)isScrollBottom
-{
- __weak typeof(self) weakself = self;
- void (^block)(NSArray *aMessages, EMError *aError) = ^(NSArray *aMessages, EMError *aError) {
- dispatch_async(dispatch_get_main_queue(), ^{
- //给 EaseChatViewController 传入消息列表messageList
- //isScrollBottom 是否滑动到页面底部 isInsertBottom 消息数据集是否插入消息列表底部
- //在传入消息列表之前不需要对列表做任何处理,只需传入列表数据即可,否则会刷新 UI 失败
- [weakself.chatController refreshTableViewWithData:aMessages isInsertBottom:NO isScrollBottom:isScrollBottom];
- });
- };
- [self.conversation loadMessagesStartFromId:nil count:50 searchDirection:EMMessageSearchDirectionUp completion:block];
-}
-```
-
-### 会话列表快速搭建
-
-导入 EaseIMKit 头文件
-
-```objectivec
-#import
-```
-
-在自己聊天控制器内可嵌入 EaseIMKit 的会话列表页,创建会话列表实例,实例化会话列表必须传入会话列表视图数据配置模型 EaseConversationViewModel 实例。
-
-```objectivec
-EaseConversationViewModel *viewModel = [[EaseConversationViewModel alloc] init];
-
-EaseConversationsViewController *easeConvsVC = [[EaseConversationsViewController alloc] initWithModel:viewModel];
-easeConvsVC.delegate = self;
-[self addChildViewController:easeConvsVC];
-[self.view addSubview:easeConvsVC.view];
-[easeConvsVC.view mas_makeConstraints:^(MASConstraintMaker *make) {
- make.size.equalTo(self.view);
-}];
-```
-
-### 通讯录快速搭建
-
-导入 EaseIMKit 头文件
-
-```objectivec
-#import
-```
-
-在自己聊天控制器内可嵌入 EaseIMKit 的会话列表页,创建通讯录实例,必须传入通讯录视图数据模型 EaseContactsViewModel 实例以构建通讯录 UI 界面。
-
-```objectivec
-EaseContactsViewModel *model = [[EaseContactsViewModel alloc] init];
-EaseContactsViewController *contactsVC = [[EaseContactsViewController alloc] initWithModel:model];
-//通讯录头部功能区(加好友/群聊/聊天室 入口)
-contactsVC.customHeaderItems = [self items];
-contactsVC.delegate = self;
-[self addChildViewController:contactsVC];
-[self.view addSubview:contactsVC.view];
-[contactsVC.view mas_makeConstraints:^(MASConstraintMaker *make) {
- make.size.equalTo(self.view);
-}];
-//设置通讯录头部功能区实例(EaseIM APP 有效):
-- (NSArray *)items {
- EMContactModel *newFriends = [[EMContactModel alloc] init];
- newFriends.huanXinId = @"newFriend";
- newFriends.nickname = @"新的好友";
- newFriends.avatar = [UIImage imageNamed:@"newFriend"];
- EMContactModel *groups = [[EMContactModel alloc] init];
- groups.huanXinId = @"groupList";
- groups.nickname = @"群聊";
- groups.avatar = [UIImage imageNamed:@"groupchat"];
- EMContactModel *chatooms = [[EMContactModel alloc] init];
- chatooms.huanXinId = @"chatroomList";
- chatooms.nickname = @"聊天室";
- chatooms.avatar = [UIImage imageNamed:@"chatroom"];
- return (NSArray *)@[newFriends, groups, chatooms];
-}
-```
-
-## 设置样式
-
-### 聊天会话样式配置
-
-聊天会话可配置参数如下:
-
-```objectivec
-@property (nonatomic, strong) UIColor *chatViewBgColor; //聊天页背景色
-@property (nonatomic, strong) UIColor *chatBarBgColor; //输入区背景色
-@property (nonatomic, strong) EaseExtFuncModel *extFuncModel; //输入区扩展功能数据模型
-@property (nonatomic, strong) UIColor *msgTimeItemBgColor; //时间线背景色
-@property (nonatomic, strong) UIColor *msgTimeItemFontColor; //时间线字体颜色
-@property (nonatomic, strong) UIImage *receiveBubbleBgPicture; //所接收信息的气泡
-@property (nonatomic, strong) UIImage *sendBubbleBgPicture; //所发送信息的气泡
-@property (nonatomic, strong) UIColor *contentFontColor; //文本消息字体颜色
-@property (nonatomic) CGFloat contentFontSize; //文本消息字体大小
-@property (nonatomic) UIEdgeInsets bubbleBgEdgeInset; //消息气泡背景图保护区域
-@property (nonatomic) EaseInputBarStyle inputBarStyle; //输入区类型:(全部功能,无语音,无表情,无表情和语音,纯文本)
-@property (nonatomic) EaseAvatarStyle avatarStyle; //头像风格
-@property (nonatomic) CGFloat avatarCornerRadius; //头像圆角大小 默认:0 (只有头像类型是圆角生效)
-//仅群聊可设置
-@property (nonatomic) EaseAlignmentStyle msgAlignmentStyle; //聊天区域消息排列方式
-```
-
-其中参数:EaseExtFuncModel 输入区扩展功能数据配置模型(聊天会话页相机,相册,音视频等区域)内含可配参数:
-
-```objectivec
-@property (nonatomic, strong) UIColor *iconBgColor;//图标所在 view 背景色
-@property (nonatomic, strong) UIColor *viewBgColor;//视图背景色
-@property (nonatomic, strong) UIColor *fontColor;//字体颜色
-@property (nonatomic, assign) CGFloat fontSize;//字体大小
-@property (nonatomic, assign) CGSize collectionViewSize;//视图尺寸
-```
-
-其中参数:inputBarStyle(输入区)包含五种类型:
-
-```objectivec
-typedef NS_ENUM(NSInteger, EaseInputBarStyle) {
- EaseInputBarStyleAll = 1, //全部功能
- EaseInputBarStyleNoAudio, //无语音
- EaseInputBarStyleNoEmoji, //无表情
- EaseInputBarStyleNoAudioAndEmoji, //无表情和语音
- EaseInputBarStyleOnlyText, //纯文本
-};
-```
-
-其中参数:EaseAlignmentStyle (消息排列方式,仅群聊可生效)包含两种类型
-
-```objectivec
-typedef enum {
- EaseAlignmentNormal = 1, //左右排列
- EaseAlignmentlLeft, //居左排列
-} EaseAlignmentStyle;
-```
-
-实例化的聊天控制器可通过重置视图 UI 配置模型刷新页面
-
-```objectivec
-//重置聊天控制器
-- (void)resetChatVCWithViewModel:(EaseChatViewModel *)viewModel;
-```
-
-聊天页背景色,输入区颜色配置示例:
-
-![背景色,输入区颜色](@static/images/ios/easeimkit4.png)
-
-聊天会话输入区类型参数配置示例:
-
-![全部功能,语音不可用,表情不可用,语音和表情不可用,纯文本](@static/images/ios/easeimkit5.png)
-
-输入区扩展功能参数配置示例:
-
-![输入区扩展](@static/images/ios/easeimkit6.jpeg)
-
-聊天会话群聊消息同左排列,时间线背景色,时间字体颜色配置示例:
-
-![群聊消息同左排列,时间线背景色,时间字体颜色](@static/images/ios/easeimkit7.jpeg)
-
-### 会话列表样式配置
-
-会话列表可配置参数如下:
-
-```objectivec
-@property (nonatomic) EaseAvatarStyle avatarType; // 头像样式
-@property (nonatomic, strong) UIImage *defaultAvatarImage; // 默认头像
-@property (nonatomic) CGSize avatarSize; // 头像尺寸
-@property (nonatomic) UIEdgeInsets avatarEdgeInsets; // 头像位置
-@property (nonatomic, strong) UIFont *nameLabelFont; // 昵称字体
-@property (nonatomic, strong) UIColor *nameLabelColor; // 昵称颜色
-@property (nonatomic) UIEdgeInsets nameLabelEdgeInsets; // 昵称位置
-@property (nonatomic, strong) UIFont *detailLabelFont; // 详情字体
-@property (nonatomic, strong) UIColor *detailLabelColor; // 详情字色
-@property (nonatomic) UIEdgeInsets detailLabelEdgeInsets; // 详情位置
-@property (nonatomic, strong) UIFont *timeLabelFont; // 时间字体
-@property (nonatomic, strong) UIColor *timeLabelColor; // 时间字色
-@property (nonatomic) UIEdgeInsets timeLabelEdgeInsets; // 时间位置
-@property (nonatomic) EMUnReadCountViewPosition badgeLabelPosition; // 未读数显示风格
-@property (nonatomic, strong) UIFont *badgeLabelFont; // 未读数字体
-@property (nonatomic, strong) UIColor *badgeLabelTitleColor; // 未读数字色
-@property (nonatomic, strong) UIColor *badgeLabelBgColor; // 未读数背景色
-@property (nonatomic) CGFloat badgeLabelHeight; // 未读数角标高度
-@property (nonatomic) CGVector badgeLabelCenterVector; // 未读数中心位置偏移
-@property (nonatomic) int badgeMaxNum; // 未读数显示上限, 超过上限后会显示 xx+
-```
-
-会话列表以及联系人列表共用其父类可配置参数如下:
-
-```objectivec
-@property (nonatomic) BOOL canRefresh; // 是否可下拉刷新
-@property (nonatomic, strong) UIView *bgView; // tableView 背景图
-@property (nonatomic, strong) UIColor *cellBgColor; // UITableViewCell 背景色
-@property (nonatomic) UIEdgeInsets cellSeparatorInset; // UITableViewCell 下划线位置
-@property (nonatomic, strong) UIColor *cellSeparatorColor; // UITableViewCell 下划线颜色
-```
-
-### 通讯录样式配置
-
-通讯录可配置参数如下:
-
-```objectivec
-@property (nonatomic) EaseAvatarStyle avatarType; // 头像样式
-@property (nonatomic, strong) UIImage *defaultAvatarImage; // 默认头像
-@property (nonatomic) CGSize avatarSize; // 头像尺寸
-@property (nonatomic) UIEdgeInsets avatarEdgeInsets; // 头像位置
-@property (nonatomic, strong) UIFont *nameLabelFont; // 昵称字体
-@property (nonatomic, strong) UIColor *nameLabelColor; // 昵称颜色
-@property (nonatomic) UIEdgeInsets nameLabelEdgeInsets; // 昵称位置
-@property (nonatomic, strong) UIFont *sectionTitleFont; // section title 字体
-@property (nonatomic, strong) UIColor *sectionTitleColor; // section title 颜色
-@property (nonatomic, strong) UIColor *sectionTitleBgColor; // section title 背景
-@property (nonatomic) UIEdgeInsets sectionTitleEdgeInsets; // section title 位置
-// section title label 高度 (section title view = sectionTitleLabelHeight + sectionTitleEdgeInsets.top + sectionTitleEdgeInsets.bottom)
-@property (nonatomic) CGFloat sectionTitleLabelHeight;
-```
-
-以及通讯录和会话列表共用的参数配置
-
-```objectivec
-@property (nonatomic) BOOL canRefresh; // 是否可下拉刷新
-@property (nonatomic, strong) UIView *bgView; // tableView 背景图
-@property (nonatomic, strong) UIColor *cellBgColor; // UITableViewCell 背景色
-@property (nonatomic) UIEdgeInsets cellSeparatorInset; // UITableViewCell 下划线位置
-@property (nonatomic, strong) UIColor *cellSeparatorColor; // UITableViewCell 下划线颜色
-```
-
-通讯录添加头部功能区:新的好友,群聊,聊天室示意图:
-
-![头部功能区:新的好友,群聊,聊天室以及联系人列表](@static/images/ios/easeimkit8.png)
-
-## 自定义功能扩展
-
-### 聊天会话自定义功能扩展
-
-实例化 EaseChatViewController 之后,可选择实现 EaseChatViewControllerDelegate 协议(聊天控制器回调代理),接收 EaseChatViewController 的回调并做进一步的自定义实现。
-
-EaseChatViewControllerDelegate
-
-#### 下拉加载更多消息回调
-
-下拉加载更多消息回调(可得到当前第一条消息 ID 作为下次加载更多消息的参考 ID;当前消息列表)
-
-```objectivec
-/**
- * 下拉加载更多消息回调
- *
- * @param firstMessageId 第一条消息 ID
- * @param messageList 当前消息列表
- */
-- (void)loadMoreMessageData:(NSString *)firstMessageId currentMessageList:(NSArray *)messageList;
-```
-
-#### 自定义 cell
-
-通过实现聊天控制回调获取自定义消息 cell,根据 messageModel,用户自己判断是否显示自定义消息 cell。如果返回 nil 会显示默认;如果返回 cell 会显示用户自定义消息 cell。
-
-```objectivec
-/*!
- @method
- @brief 获取消息自定义 cell
- @discussion 用户根据 messageModel 判断是否显示自定义 cell。返回 nil 显示默认 cell,否则显示用户自定义 cell
- @param tableView 当前消息视图的 tableView
- @param messageModel 消息的数据模型
- @result 返回用户自定义 cell
- */
-- (UITableViewCell *)cellForItem:(UITableView *)tableView messageModel:(EaseMessageModel *)messageModel;
-```
-
-具体创建自定义 Cell 的示例:
-
-```objectivec
-//自定义通话记录cell
-- (UITableViewCell *)cellForItem:(UITableView *)tableView messageModel:(EaseMessageModel *)messageModel
-{
- //当消息是单聊音视频通话消息时,EaseIM 自定义通话记录 cell
- if (messageModel.type == EMMessageTypePictMixText) {
- EMMessageCell *cell = [[EMMessageCell alloc]initWithDirection:messageModel.direction type:messageModel.type];
- cell.model = messageModel;
- cell.delegate = self;
- return cell;
- }
- return nil;
-}
-```
-
-通过自定义 cell 展示单聊音视频通话记录的效果图:
-
-![自定义 cell 展示单聊音视频通话记录](@static/images/ios/easeimkit9.png)
-
-#### 选中消息的回调
-
-选中消息的回调(EaseIMKit 没有对于自定义 cell 的选中事件回调,需用户自定义实现选中响应)
-
-```objectivec
-/*!
- @method
- @brief 消息点击事件
- @discussion 用户根据 messageModel 判断,是否自定义处理消息选中时间。返回 NO 为自定义处理,返回 YES 为默认处理
- @param message 当前点击的消息
- @param userData 当前点击的消息携带的用户资料
- @result 是否采用默认事件处理
-*/
-- (BOOL)didSelectMessageItem:(EMMessage*)message userData:(id)userData;
-```
-
-EaseIMKit 选中是消息气泡,自定义 cell 的点击事件需自定义实现,例:EaseIM 单聊通话记录 cell 点击事件再次发起通话
-
-```objectivec
-- (void)messageCellDidSelected:(EMMessageCell *)aCell
-{
- //使用‘通知’的方式发起通话,其中所定义的宏仅在 EaseIM APP 中生效
- NSString *callType = nil;
- NSDictionary *dic = aCell.model.message.ext;
- if ([[dic objectForKey:EMCOMMUNICATE_TYPE] isEqualToString:EMCOMMUNICATE_TYPE_VOICE])
- callType = EMCOMMUNICATE_TYPE_VOICE;
- if ([[dic objectForKey:EMCOMMUNICATE_TYPE] isEqualToString:EMCOMMUNICATE_TYPE_VIDEO])
- callType = EMCOMMUNICATE_TYPE_VIDEO;
- if ([callType isEqualToString:EMCOMMUNICATE_TYPE_VOICE])
- [[NSNotificationCenter defaultCenter] postNotificationName:CALL_MAKE1V1 object:@{CALL_CHATTER:aCell.model.message.conversationId, CALL_TYPE:@(EMCallTypeVoice)}];
- if ([callType isEqualToString:EMCOMMUNICATE_TYPE_VIDEO])
- [[NSNotificationCenter defaultCenter] postNotificationName:CALL_MAKE1V1 object:@{CALL_CHATTER:aCell.model.message.conversationId, CALL_TYPE:@(EMCallTypeVideo)}];
-}
-```
-
-#### 用户资料回调
-
-用户资料回调(头像昵称等)
-
-```objectivec
-/*!
- @method
- @brief 返回用户资料
- @discussion 用户根据 huanxinID 在自己的用户体系中匹配对应的用户资料,并返回相应的信息,否则默认实现
- @param huanxinID 环信 ID
- @result 返回与当前环信 ID 关联的用户资料
- */
-- (id)userData:(NSString*)huanxinID;
-```
-
-#### 用户选中头像的回调
-
-```objectivec
-/*!
- @method
- @brief 点击消息头像
- @discussion 获取用户点击头像回调
- @param userData 当前点击的头像所指向的用户资料
- */
-
-- (void)avatarDidSelected:(id)userData;
-```
-
-获取用户选中头像回调的样例:
-
-```objectivec
-//头像点击
-- (void)avatarDidSelected:(id)userData
-{
- //EMPersonalDataViewController 用户自定义的个人信息视图
- //样例逻辑是选中消息头像后,进入该消息发送者的个人信息页
- if (userData && userData.easeId) {
- EMPersonalDataViewController *controller = [[EMPersonalDataViewController alloc]initWithNickName:userData.easeId];
- [self.navigationController pushViewController:controller animated:YES];
- }
-}
-```
-
-#### 用户长按头像的回调
-
-```objectivec
-/*!
- @method
- @brief 点击消息头像
- @discussion 获取用户长按头像回调
- @param userData 当前长按的头像所指向的用户资料
- */
-
-- (void)avatarDidLongPress:(id)userData;
-```
-
-#### 群通知回执详情
-
-```objectivec
-/*!
- @method
- @brief 群通知回执详情
- @discussion 获取用户点击群通知的回调(仅在群聊中并且是点击用户群主有效)
- @param message 当前群通知消息
- @param groupId 当前消息所属群 ID
- */
-
-- (void)groupMessageReadReceiptDetail:(EMMessage*)message groupId:(NSString*)groupId;
-```
-
-获取用户点击群通知回执详情的样例:
-
-```objectivec
-//群通知阅读回执详情
-- (void)groupMessageReadReceiptDetail:(EMMessage *)message groupId:(NSString *)groupId
-{
- //EMReadReceiptMsgViewController用户自定义群通知阅读回执详情页(在 EaseIM APP 中有效)
- EMReadReceiptMsgViewController *readReceiptControl = [[EMReadReceiptMsgViewController alloc] initWithMessage:message groupId:groupId];
- readReceiptControl.modalPresentationStyle = 0;
- [self presentViewController:readReceiptControl animated:YES completion:nil];
-}
-```
-
-#### 输入区回调
-
-当前会话输入扩展区数据模型组(UI 配置可在聊天视图配置数据模型中设置)
-
-```objectivec
-/*!
- @method
- @brief 当前会话输入扩展区数据模型组
- @param defaultInputBarItems 默认功能数据模型组 (默认有序:相册,相机,位置,文件)
- @param conversationType 当前会话类型:单聊,群聊,聊天室
- @result 返回一组输入区扩展功能
- */
-
-- (NSMutableArray*)inputBarExtMenuItemArray:
- (NSMutableArray*)defaultInputBarItems
- conversationType:(EMConversationType)conversationType;
-```
-
-当前会话输入扩展区数据模型组回调示例(EaseIM APP 有效):
-
-```objectivec
-- (NSMutableArray *)inputBarExtMenuItemArray:(NSMutableArray *)defaultInputBarItems conversationType:(EMConversationType)conversationType
-{
-NSMutableArray *menuArray = [[NSMutableArray alloc]init];
-//相册
-[menuArray addObject:[defaultInputBarItems objectAtIndex:0]];
-//相机
-[menuArray addObject:[defaultInputBarItems objectAtIndex:1]];
-//音视频
-__weak typeof(self) weakself = self;
-if (conversationType != EMConversationTypeChatRoom) {
- EaseExtMenuModel *rtcMenu = [[EaseExtMenuModel alloc]initWithData:[UIImage imageNamed:@"video_conf"] funcDesc:@"音视频" handle:^(NSString * _Nonnull itemDesc, BOOL isExecuted) {
- if (isExecuted) {
- [weakself chatSealRtcAction];
- }
- }];
- [menuArray addObject:rtcMenu];
-}
-//位置
-[menuArray addObject:[defaultInputBarItems objectAtIndex:2]];
-//文件
-[menuArray addObject:[defaultInputBarItems objectAtIndex:3]];
-//群组回执
-if (conversationType == EMConversationTypeGroupChat) {
- if ([[EMClient.sharedClient.groupManager getGroupSpecificationFromServerWithId:_conversation.conversationId error:nil].owner isEqualToString:EMClient.sharedClient.currentUsername]) {
- EaseExtMenuModel *groupReadReceiptExtModel = [[EaseExtMenuModel alloc]initWithData:[UIImage imageNamed:@"pin_readReceipt"] funcDesc:@"群组回执" handle:^(NSString * _Nonnull itemDesc, BOOL isExecuted) {
- //群组回执发送消息页
- [weakself groupReadReceiptAction];
- }];
- [menuArray addObject:groupReadReceiptExtModel];
- }
-}
-return menuArray;
-}
-```
-
-#### 键盘输入变化回调
-
-```objectivec
-/*!
- @method
- @brief 输入区键盘输入变化回调 例:@群成员
- @result 返回键入内容是否有效
- */
-- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
-```
-
-输入区键盘回调示例(EaseIM APP 有效):
-
-```objectivec
-//@群成员
-- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
-{
- if (self.conversation.type == EMConversationTypeGroupChat) {
- if ([text isEqualToString:@"@"]) {
- [self _willInputAt:textView];
- } else if ([text isEqualToString:@""]) {
- __block BOOL isAt = NO;
- [textView.attributedText enumerateAttributesInRange:NSMakeRange(0, textView.text.length) options:0 usingBlock:^(NSDictionary * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
- NSString *atUser = attrs[@"AtInfo"];
- if (atUser) {
- if (textView.selectedRange.location == range.location + range.length) {
- isAt = YES;
- NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithAttributedString:textView.attributedText];
- [result deleteCharactersInRange:range];
- textView.attributedText = result;
- if ([atUser isEqualToString:@"All"]) {
- [self.chatController removeAtAll];
- } else {
- [self.chatController removeAtUser:atUser];
- }
- *stop = YES;
- }
- }
- }];
- return !isAt;
- }
- }
- return YES;
-}
-```
-
-#### 输入框选中回调
-
-```objectivec
-/**
- * 输入区选中范围变化回调 例:@群成员
- */
-- (void)textViewDidChangeSelection:(UITextView *)textView;
-```
-
-输入区选中范围变化回调示例(EaseIM APP 有效):
-
-```objectivec
-- (void)textViewDidChangeSelection:(UITextView *)textView
-{
- [textView.attributedText enumerateAttributesInRange:NSMakeRange(0, textView.text.length) options:0 usingBlock:^(NSDictionary * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
- if (attrs[@"AtInfo"]) {
- NSUInteger min = textView.selectedRange.location;
- NSUInteger max = textView.selectedRange.location + textView.selectedRange.length;
- if (min > range.location && min <= range.location + range.length) {
- NSUInteger location = range.location + range.length;
- NSUInteger length = 0;
- if (textView.selectedRange.location + textView.selectedRange.length > location) {
- length = textView.selectedRange.location + textView.selectedRange.length - location;
- }
- textView.selectedRange = NSMakeRange(location, length);
- *stop = YES;
- } else if (max > range.location && max <= range.location + range.length) {
- NSUInteger location = min;
- NSUInteger length = textView.selectedRange.length - (max - range.location - range.length);
- textView.selectedRange = NSMakeRange(location, length);
- *stop = YES;
- }
- }
- }];
-}
-```
-
-#### 对方正在输入状态回调
-
-对方正在输入状态回调(单聊有效)
-
-```objectivec
-/**
- 对方正在输入
-*/
-- (void)beginTyping;
-```
-
-对方结束输入回调(单聊有效)
-
-```objectivec
-/**
- 对方结束输入
-*/
-- (void)endTyping;
-```
-
-#### 消息长按事件回调
-
-默认消息 cell 长按回调
-
-```objectivec
-/*!
- @method
- @brief 默认消息 cell 长按回调
- @param defaultLongPressItems 默认长按扩展区功能数据模型组(默认共有:复制,删除,撤回发送消息时 间距当前时间小于 2 分钟)
- @param message 当前长按的消息
- @result 返回默认消息长按扩展功能组
- */
-- (NSMutableArray*)messageLongPressExtMenuItemArray:(NSMutableArray*)defaultLongPressItems message:(EMMessage*)message;
-```
-
-默认消息 cell 长按回调示例(EaseIM APP 有效):
-
-```objectivec
-//添加转发消息
-- (NSMutableArray *)messageLongPressExtMenuItemArray:(NSMutableArray *)defaultLongPressItems message:(EMMessage *)message
-{
- NSMutableArray *menuArray = [[NSMutableArray alloc]initWithArray:defaultLongPressItems];
- //转发
- __weak typeof(self) weakself = self;
- if (message.body.type == EMMessageBodyTypeText || message.body.type == EMMessageBodyTypeImage || message.body.type == EMMessageBodyTypeLocation || message.body.type == EMMessageBodyTypeVideo) {
- EaseExtMenuModel *forwardMenu = [[EaseExtMenuModel alloc]initWithData:[UIImage imageNamed:@"forward"] funcDesc:@"转发" handle:^(NSString * _Nonnull itemDesc, BOOL isExecuted) {
- //用户可自定义转发 CallBack
- if (isExecuted) {
- [weakself forwardMenuItemAction:message];
- }
- }];
- [menuArray addObject:forwardMenu];
- }
- return menuArray;
-}
-```
-
-#### 自定义 cell 长按回调
-
-用户自定义消息 cell 长按事件回调
-
-```objectivec
-/*!
- @method
- @brief 当前所长按的 自定义 cell 的扩展区数据模型组
- @param defaultLongPressItems 默认长按扩展区功能数据模型组。默认共有:复制,删除,撤回(发送消息时 间距当前时间小于 2 分钟)
- @param customCell 当前长按的自定义 cell
- @result 返回默认消息长按扩展功能组
- */
-/**
- *
- *
- * @param defaultLongPressItems 默认长按扩展区功能数据模型组 默认共有:复制,删除,撤回(发送消息时 间距当前时间小于 2 分钟))
- * @param customCell 当前长按的自定义 cell
- */
-- (NSMutableArray*)customCellLongPressExtMenuItemArray:(NSMutableArray*)defaultLongPressItems customCell:(UITableViewCell*)customCell;
-```
-
-### 会话列表自定义功能扩展
-
-实例化 `EaseConversationsViewController` 之后,可选择实现 `EaseConversationsViewControllerDelegate` 协议(会话列表回调代理),接收 `EaseConversationsViewController` 的回调并做进一步的自定义实现。
-
-`EaseConversationsViewControllerDelegate`
-
-#### 自定义 cell
-
-通过实现会话列表回调获取自定义消息 cell 如果返回 nil 会显示默认;如果返回 cell 则会显示用户自定义 cell。
-
-```objectivec
-/*!
- @method
- @brief 获取消息自定义 cell
- @discussion 返回 nil 显示默认 cell,否则显示用户自定义 cell
- @param tableView 当前消息视图的 tableView
- @param indexPath 当前所要展示 cell 的 indexPath
- @result 返回用户自定义cell
- */
-- (UITableViewCell *)easeTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
-```
-
-#### 会话列表 cell 选中回调
-
-```objectivec
-/*!
- @method
- @brief 会话列表 cell 选中回调
- @param tableView 当前消息视图的 tableView
- @param indexPath 当前所要展示 cell 的 indexPath
- */
-- (void)easeTableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
-```
-
-会话列表 cell 选中回调示例(EaseIM APP 有效):
-
-```objectivec
-- (void)easeTableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
-{
- EaseConversationCell *cell = (EaseConversationCell*)[tableView cellForRowAtIndexPath:indexPath];
- //系统通知
- if ([cell.model.easeId isEqualToString:@"emsystemnotificationid"]) {
- //此实例仅为 EaseIM APP 展示系统通知
- EMNotificationViewController *controller = [[EMNotificationViewController alloc] initWithStyle:UITableViewStylePlain];
- [self.navigationController pushViewController:controller animated:YES];
- return;
- }
- //跳转至聊天页
- [[NSNotificationCenter defaultCenter] postNotificationName:CHAT_PUSHVIEWCONTROLLER object:cell.model];
-}
-```
-
-#### 会话列表用户资料回调
-
-```objectivec
-/*!
- @method
- @brief 会话列表用户资料回调
- @discussion 可根据 conversationId 或 type 返回对应的用户资料数据集
- @param conversationId 当前会话列表 cell 所拥有的会话 ID
- @param type 当前会话列表 cell 所拥有的会话类型
- */
-- (id)easeUserDelegateAtConversationId:(NSString *)conversationId
- conversationType:(EMConversationType)type;
-```
-
-会话列表用户资料回调实例(EaseIM APP 有效)
-
-```objectivec
-- (id)easeUserDelegateAtConversationId:(NSString *)conversationId conversationType:(EMConversationType)type
-{
- //EMConversationUserDataModel 为自定义用户资料数据模型,实现 EaseUserDelegate 接口返回参数
- //@property (nonatomic, copy, readonly) NSString *easeId; // 环信 ID
- //@property (nonatomic, copy, readonly) UIImage *defaultAvatar; // 默认头像显示
- EMConversationUserDataModel *userData = [[EMConversationUserDataModel alloc]initWithEaseId:conversationId conversationType:type];
- return userData;
-}
-```
-
-#### 会话列表 cell 侧滑项回调
-
-```objectivec
-/*!
- @method
- @brief 会话列表 cell 侧滑项回调
- @param tableView 当前消息视图的 tableView
- @param indexPath 当前所要侧滑 cell 的 indexPath
- @param actions 返回侧滑项集合
- */
-- (NSArray *)easeTableView:(UITableView *)tableView
- trailingSwipeActionsForRowAtIndexPath:(NSIndexPath *)indexPath
- actions:(NSArray *)actions;
-```
-
-会话列表 cell 侧滑项回调示例(EaseIM APP 有效)
-
-```objectivec
-- (NSArray *)easeTableView:(UITableView *)tableView trailingSwipeActionsForRowAtIndexPath:(NSIndexPath *)indexPath actions:(NSArray *)actions
-{
- NSMutableArray *array = [[NSMutableArray alloc]init];
- __weak typeof(self) weakself = self;
- UIContextualAction *deleteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal
- title:@"删除"
- handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL))
- {
- //删除操作
- }];
- deleteAction.backgroundColor = [UIColor redColor];
- [array addObject:deleteAction];
- //返回的 actions 有序:删除,置顶
- [array addObject:actions[1]];
- return [array copy];
-}
-```
-
-### 通讯录自定义功能扩展
-
-#### 获取用户联系人资料
-
-获取用户自己的联系人列表填充到 EaseIMKit 通讯录中
-
-```objectivec
-- (void)setContacts:(NSArray * _Nonnull)contacts;
-```
-
-实例化 EaseContactsViewController 之后,可选择实现 EaseContactsViewControllerDelegate 协议(通讯录代理),接收 EaseContactsViewController 的回调并做进一步的自定义实现。
-
-EaseConversationsViewControllerDelegate
-
-#### 即将刷新通讯录填充数据
-
-```objectivec
-- (void)willBeginRefresh;
-```
-
-即将刷新通讯录填充数据示例(EaseIM APP 有效):
-
-```objectivec
-- (void)willBeginRefresh {
- //从服务器获取当前登录账户的联系人列表
- [EMClient.sharedClient.contactManager getContactsFromServerWithCompletion:^(NSArray *aList, EMError *aError) {
- if (!aError) {
- self->_contacts = [aList mutableCopy];
- NSMutableArray *contacts = [NSMutableArray array];
- for (NSString *username in aList) {
- EMContactModel *model = [[EMContactModel alloc] init];
- model.huanXinId = username;
- [contacts addObject:model];
- }
- //填充联系人列表集合到 EaseIMKit 通讯录实例中
- [self->_contactsVC setContacts:contacts];
- }
- [self->_contactsVC endRefresh];
- }];
-}
-```
-
-#### 通讯录自定义 cell
-
-```objectivec
-/*!
-@method
-@brief 获取通讯录自定义 cell
-@discussion 返回 nil 显示默认 cell,否则显示用户自定义 cell
-@param tableView 当前消息视图的 tableView
-@param contact 当前所要展示 cell 所拥有的数据模型
-@result 返回用户自定义 cell
-*/
-- (UITableViewCell *)easeTableView:(UITableView *)tableView cellForRowAtContactModel:(EaseContactModel *) contact;
-```
-
-#### 通讯录 cell 条目选中回调
-
-```objectivec
-/*!
-@method
-@brief 通讯录 cell 条目选中回调
-@param tableView 当前消息视图的 tableView
-@param contact 当前所选中 cell 所拥有的数据模型
-@result 返回用户自定义 cell
-*/
-- (void)easeTableView:(UITableView *)tableView didSelectRowAtContactModel:(EaseContactModel *) contact;
-```
-
-通讯录 cell 条目选中回调示例:
-
-```objectivec
-- (void)easeTableView:(UITableView *)tableView didSelectRowAtContactModel:(EaseContactModel *)contact {
- //跳转加好友页
- if ([contact.easeId isEqualToString:@"newFriend"]) {
- EMInviteFriendViewController *controller = [[EMInviteFriendViewController alloc] init];
- [self.navigationController pushViewController:controller animated:YES];
- return;
- }
- //跳转群组列表页
- if ([contact.easeId isEqualToString:@"groupList"]) {
- [[NSNotificationCenter defaultCenter] postNotificationName:GROUP_LIST_PUSHVIEWCONTROLLER object:@{NOTIF_NAVICONTROLLER:self.navigationController}];
- return;
- }
- //跳转聊天室列表页
- if ([contact.easeId isEqualToString:@"chatroomList"]) {
- [[NSNotificationCenter defaultCenter] postNotificationName:CHATROOM_LIST_PUSHVIEWCONTROLLER object:@{NOTIF_NAVICONTROLLER:self.navigationController}];
- return;
- }
- //跳转好友个人资料页
- [self personData:contact.easeId];
-}
-```
-
-#### 通讯录 cell 侧滑回调
-
-```objectivec
-/*!
- @method
- @brief 会话列表 cell 侧滑项回调
- @param tableView 当前消息视图的 tableView
- @param contact 当前所侧滑 cell 拥有的联系人数据
- @param actions 返回侧滑项集合
- */
-- (NSArray *)easeTableView:(UITableView *)tableView
- trailingSwipeActionsForRowAtContactModel:(EaseContactModel *) contact
- actions:(NSArray * __nullable)actions;
-```
-
-通讯录 cell 侧滑回调示例:
-
-```objectivec
-- (NSArray *)easeTableView:(UITableView *)tableView trailingSwipeActionsForRowAtContactModel:(EaseContactModel *)contact actions:(NSArray *)actions
-{
- //通讯录头部非联系人列表禁止侧滑
- if ([contact.easeId isEqualToString:@"newFriend"] || [contact.easeId isEqualToString:@"groupList"] || [contact.easeId isEqualToString:@"chatroomList"]) {
- return nil;
- }
- __weak typeof(self) weakself = self;
- UIContextualAction *deleteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
- title:@"删除"
- handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL))
- {
- //删除联系人操作
- }];
- return @[deleteAction];
-}
-```
diff --git a/docs/uikit/android/overview.md b/docs/uikit/android/overview.md
index 8013c93ef..572314c07 100644
--- a/docs/uikit/android/overview.md
+++ b/docs/uikit/android/overview.md
@@ -1 +1,1261 @@
-## UIKit 集成文档
+# EaseIMKit 使用指南
+
+
+
+在您阅读此文档时,我们假定您已经具备了基础的 Android 应用开发经验,并能够理解相关基础概念。此文档是针对导入 EaseIMKit 库的快速集成文档,如果只是导入 SDK 集成使用,请参考 [环信即时通讯 IM Android 快速开始](quickstart.html)。
+
+## 简介
+
+EaseIMKit 是什么?
+
+**EaseIMKit** 是 EaseUI 的升级版,是基于环信 IM SDK 的一款 UI 组件库,它提供了一些通用的 UI 组件,例如‘会话列表’、‘聊天界面’和‘联系人列表’等,开发者可根据实际业务需求通过该组件库快速地搭建自定义 IM 应用。EaseIMKit 中的组件在实现 UI 功能的同时,调用 IM SDK 相应的接口实现 IM 相关逻辑和数据的处理,因而开发者在使用 EaseIMKit 时只需关注自身业务或个性化扩展即可。
+
+EaseIMKit 源码地址
+
+- [EaseIMKit](https://github.com/easemob/easeui/tree/EaseIMKit)
+
+使用 EaseIMKIt 的环信 IM APP 源码地址:
+
+- [环信 IM](https://github.com/easemob/chat-android)
+
+## 导入 EaseIMKit
+
+### 开发环境要求
+
+- Android Studio 3.2 以上
+- Gradle 4.6 以上
+- targetVersion 26 以上
+- Android SDK API 19 以上
+- Java JDK 1.8 以上
+
+### 集成说明
+
+EaseIMKit 支持 Gradle 接入和 Module 源码集成
+
+#### Gradle 接入集成
+
+:::notice 重大变动
+远程仓库统一由 JCenter 迁移到 `MavenCentral`,依赖库的域名由 “com.hyphenate” 修改为 “io.hyphenate”,详见 [环信即时通讯 IM Android 快速开始](quickstart.html)。
+:::
+
+```gradle
+implementation 'io.hyphenate:ease-im-kit:xxx版本'
+implementation 'io.hyphenate:hyphenate-chat:xxx版本'
+```
+
+**EaseIMKit 必须依赖环信 IM SDK,因而在使用 EaseIMKit 时必须同时添加环信 IM SDK 依赖。**
+
+:::notice
+
+1. IM SDK **3.8.0** 版本以后,远程依赖的 `artifactId` 修改为 `hyphenate-chat`,且该版本以后中不再包含音视频相关逻辑。
+2. IM SDK **3.8.0** 以下,远程依赖,包含音视频的 `artifactId` 为 `hyphenate-sdk`,不包含音视频的 `artifactId` 为 `hyphenate-sdk-lite`。如果想使用不包含音视频通话的 SDK,用 `implementation 'io.hyphenate:hyphenate-sdk-lite:xxx版本`'。
+
+版本号参考 [Android SDK 更新日志](releasenote.html)。
+:::
+
+#### Module 源码集成
+
+```gradle
+implementation project(':ease-im-kit')
+```
+
+#### 依赖的第三方库
+
+- Glide: 图片处理库,显示用户头像时用到。
+- BaiduLBS_Android.jar: 百度地图定位库。
+
+#### 关于位置消息说明
+
+EaseIMKit 中位置消息使用的是百度地图定位 jar 包,为了防止出现百度地图类冲突的问题,EaseIMKit 库打包时对百度地图定位 jar 包采用了只编译的方式,这就要求如果开发者想要使用位置消息,需要在自己的项目中添加百度地图定位的 jar 包及其 so 文件。
+
+### 初始化
+
+在 application 的 onCreate 下调用初始化 EaseIMKit 的方法。
+
+:::notice
+EaseIMKit 初始化里已包含 SDK 的初始化,不需要再去调用 SDK 的初始化。
+:::
+
+```java
+public class DemoApplication extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ //EaseIM 初始化
+ if(EaseIM.getInstance().init(context, options)){
+ //在做打包混淆时,关闭 debug 模式,避免消耗不必要的资源
+ EMClient.getInstance().setDebugMode(true);
+ //EaseIM 初始化成功之后再调用注册消息监听的代码 ...
+ }
+ }
+}
+```
+
+## 快速搭建
+
+EaseIMKit 封装了常用 IM 功能,提供了会话,聊天及联系人等基本的 fragment,旨在帮助开发者快速集成环信 SDK。
+
+### 创建会话列表界面
+
+EaseIMKit 提供了 EaseConversationListFragment,需要将其或者其子类添加到 Activity 中。开发者需要对刷新事件(新消息,删除消息,删除会话等)进行处理。
+
+![img](@static/images/android/easeim.jpeg)
+
+:::notice
+要实现自定义头像及昵称,请参考 [设置头像和昵称](userprofile.html#设置当前用户的属性)。
+:::
+
+### 创建聊天界面
+
+EaseIMKit 提供了 EaseChatFragment,添加到 Activity 中并传递相应的参数即可用。
+必须向 EaseChatFragment 传递的参数为:
+
+- `conversationId`——会话 ID,单聊时指对方 ID,群聊和聊天室时指群和聊天室 ID;
+- `chatType`——聊天类型,整型,分别为单聊(1)、群聊(2)和聊天室(3);
+
+可选传递参数为:
+
+- `history_msg_id`——消息 ID,用于查询历史记录时的定位消息 ID;
+- `isRoaming`——是否开启漫游,布尔类型,用于标记是否优先从服务器拉取消息。
+
+```java
+public class ChatActivity extends BaseActivity {
+ private EaseChatFragment chatFragment;
+
+ @Override
+ protected void onCreate(Bundle arg0) {
+ super.onCreate(arg0);
+ setContentView(R.layout.em_activity_chat);
+ //use EaseChatFratFragment
+ chatFragment = new EaseChatFragment();
+ //pass parameters to chat fragment
+ chatFragment.setArguments(getIntent().getExtras());
+ getSupportFragmentManager().beginTransaction().add(R.id.container, chatFragment).commit();
+ }
+}
+```
+
+![img](@static/images/android/easeim1.jpeg)
+
+### 添加联系人界面
+
+EaseIMKit 提供了 EaseContactListFragment,添加其及其子类到 Activity 中。开发者需要对刷新事件(添加联系人,删除联系人等)进行处理。
+
+![img](@static/images/android/easeim2.jpeg)
+
+## 设置样式
+
+### 设置标题栏
+
+EaseIMKit 提供了自定义的标题栏控件 EaseTitleBar。
+
+![img](@static/images/android/easeim-titlebar.jpeg)
+
+标题栏除了做为 View 所具有的属性功能外,还可以设置标题的位置等。
+
+xml 中设置如下:
+
+```xml
+
+```
+
+其中 `titleBarDisplayHomeAsUpEnabled` 属性为设置返回按钮是否可见,设置标题位置可设置 `titleBarTitlePosition`,可选值为 `center`,`left` 和 `right`。
+
+也可进行代码设置,如下:
+
+```java
+EaseTitleBar titleBarMessage = findViewById(R.id.title_bar_message);
+//设置右侧菜单图标
+titleBarMessage.setRightImageResource(R.drawable.chat_user_info);
+//设置标题
+titleBarMessage.setTitle("标题");
+//设置标题位置
+titleBarMessage.setTitlePosition(EaseTitleBar.TitlePosition.Left);
+//设置右侧菜单图标的点击事件
+titleBarMessage.setOnRightClickListener(this);
+//设置返回按钮的点击事件
+titleBarMessage.setOnBackPressListener(this);
+```
+
+当然设置右侧菜单,您也可以通过 Android 提供的添加 `menu xml` 的形式实现。修改按钮图标,可以调用 `titleBarMenuResource` 属性进行设置。
+
+### 设置会话列表
+
+会话列表可以修改如下样式:
+
+- 头像:头像大小,头像形状(方形,带圆角的方形,圆形),描边
+- 标题、内容、时间等文字:字体大小,字体颜色
+- 未读消息:可设置是否展示,展示位置(左式和右式)
+
+在 `EaseConversationListFragment` 及其子类中可以直接获取到 `EaseConversationListLayout` 这个控件,然后通过这个控件进行设置。
+
+代码如下:
+
+```java
+//设置头像尺寸
+conversationListLayout.setAvatarSize(EaseCommonUtils.dip2px(mContext, 50));
+//设置头像样式:0 为默认,1 为圆形,2 为方形(设置方形时,需要配合设置 avatarRadius,默认的 avatarRadius 为 50dp)
+conversationListLayout.setAvatarShapeType(2);
+//设置圆角半径
+conversationListLayout.setAvatarRadius((int) EaseCommonUtils.dip2px(mContext, 5));
+//设置标题字体的颜色
+conversationListLayout.setTitleTextColor(ContextCompat.getColor(mContext, R.color.red));
+//设置是否隐藏未读消息数,默认为不隐藏
+conversationListLayout.hideUnreadDot(false);
+//设置未读消息数展示位置,默认为左侧
+conversationListLayout.showUnreadDotPosition(EaseConversationSetStyle.UnreadDotPosition.LEFT);
+```
+
+效果如下图:
+
+![img](@static/images/android/easeim3.jpeg)
+更多样式请参考 EaseContactListLayout 控件。
+
+#### 增加长按菜单项
+
+`EaseConversationListLayout` 提供了增加菜单项的 API,开发者可方便的增加更多的菜单功能。
+示例代码如下:
+
+```java
+@Override
+public void initView(Bundle savedInstanceState) {
+ super.initView(savedInstanceState);
+ ......
+ conversationListLayout.addItemMenu(Menu.NONE, R.id.action_con_delete, 4, getString(R.string.ease_conversation_menu_delete));
+}
+
+......
+
+@Override
+public boolean onMenuItemClick(MenuItem item, int position) {
+ EaseConversationInfo info = conversationListLayout.getItem(position);
+ Object object = info.getInfo();
+ if(object instanceof EMConversation) {
+ switch (item.getItemId()) {
+ case R.id.action_con_make_top :
+ // 将会话置顶
+ conversationListLayout.makeConversationTop(position, info);
+ return true;
+ case R.id.action_con_cancel_top :
+ // 取消会话置顶
+ conversationListLayout.cancelConversationTop(position, info);
+ return true;
+ case R.id.action_con_delete :
+ showDeleteDialog(position, info);
+ return true;
+ }
+ }
+ return super.onMenuItemClick(item, position);
+}
+```
+
+### 设置聊天窗口
+
+聊天窗口包括标题栏(不包含在 EaseChatFragment 中),聊天区,输入区及扩展展示区,如下图所示:
+
+![img](@static/images/android/easeim4.png)
+
+标题区 EaseTitleBar 的具体布局及实现不在 EaseIMKit 库的聊天控件及 fragment 中,需要你自己去实现。
+开发者可以在 EaseChatFragment 中获取到 EaseChatLayout 这个控件,然后通过这个控件进一步获取到获取其他控件,代码如下:
+
+```java
+//获取到聊天列表控件
+EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
+//获取到菜单输入父控件
+EaseChatInputMenu chatInputMenu = chatLayout.getChatInputMenu();
+//获取到菜单输入控件
+IChatPrimaryMenu primaryMenu = chatInputMenu.getPrimaryMenu();
+//获取到扩展区域控件
+IChatExtendMenu chatExtendMenu = chatInputMenu.getChatExtendMenu();
+//获取到表情区域控件
+IChatEmojiconMenu emojiconMenu = chatInputMenu.getEmojiconMenu();
+```
+
+#### 修改聊天列表样式
+
+聊天列表区域可以修改背景,文字,气泡,是否展示昵称及聊天展示样式等,更多设置请参考 IChatMessageItemSet。
+
+#### 修改聊天列表背景
+
+```java
+//获取到聊天列表控件
+EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
+//设置聊天列表背景
+messageListLayout.setBackground(new ColorDrawable(Color.parseColor("#DA5A4D")));
+```
+
+效果如下图:
+
+![img](@static/images/android/easeim5.jpeg)
+
+#### 修改头像属性
+
+开发者可以设置默认头像和头像形状。
+
+```java
+//获取到聊天列表控件
+EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
+//设置默认头像
+messageListLayout.setAvatarDefaultSrc(ContextCompat.getDrawable(mContext, R.drawable.ease_default_avatar));
+//设置头像形状:0 为默认,1 为圆形,2 为方形
+messageListLayout.setAvatarShapeType(1);
+```
+
+效果如下图:
+
+![img](@static/images/android/easeim6.jpeg)
+
+#### 修改聊天文本
+
+开发者可以修改聊天文本的字体大小及字体颜色,发送方及接收方需保持一致。
+
+```java
+//获取到聊天列表控件
+EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
+//设置文本字体大小
+messageListLayout.setItemTextSize((int) EaseCommonUtils.sp2px(mContext, 18));
+//设置文本字体颜色
+messageListLayout.setItemTextColor(ContextCompat.getColor(mContext, R.color.red));
+```
+
+效果如下图:
+
+![img](@static/images/android/easeim7.jpeg)
+
+#### 修改时间线样式
+
+开发者可以修改时间线的背景,文字的大小及颜色。
+
+```java
+//获取到聊天列表控件
+EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
+//设置时间线的背景
+messageListLayout.setTimeBackground(ContextCompat.getDrawable(mContext, R.color.gray_normal));
+//设置时间线的文本大小
+messageListLayout.setTimeTextSize((int) EaseCommonUtils.sp2px(mContext, 18));
+//设置时间线的文本颜色
+messageListLayout.setTimeTextColor(ContextCompat.getColor(mContext, R.color.black));
+```
+
+效果如下图:
+
+![img](@static/images/android/easeim8.jpeg)
+
+#### 修改聊天列表展示样式
+
+开发者可以设置聊天列表的样式,发送方和接收方位于两侧还是位于一侧。
+
+```java
+//获取到聊天列表控件
+EaseChatMessageListLayout messageListLayout = chatLayout.getChatMessageListLayout();
+//设置聊天列表样式:两侧及均位于左侧
+messageListLayout.setItemShowType(EaseChatMessageListLayout.ShowType.LEFT);
+```
+
+效果如下图:
+
+![img](@static/images/android/easeim9.jpeg)
+
+#### 修改输入区样式
+
+输入区控件为 EaseChatInputMenu,它由输入控件 EaseChatPrimaryMenu,扩展控件 EaseChatExtendMenu 和表情控件 EaseEmojiconMenu 组成。
+
+```java
+//获取到菜单输入父控件
+EaseChatInputMenu chatInputMenu = chatLayout.getChatInputMenu();
+//获取到菜单输入控件
+IChatPrimaryMenu primaryMenu = chatInputMenu.getPrimaryMenu();
+//获取到扩展区域控件
+IChatExtendMenu chatExtendMenu = chatInputMenu.getChatExtendMenu();
+//获取到表情区域控件
+IChatEmojiconMenu emojiconMenu = chatInputMenu.getEmojiconMenu();
+```
+
+开发者可以修改菜单输入控件的样式,其有 5 种模式,即 `完整模式`,`不可用语音模式`,`不可用表情模式`,`不可用语音和表情模式` 和 `只有文本输入模式`。
+
+```java
+//获取到菜单输入父控件
+EaseChatInputMenu chatInputMenu = chatLayout.getChatInputMenu();
+//获取到菜单输入控件
+IChatPrimaryMenu primaryMenu = chatInputMenu.getPrimaryMenu();
+if(primaryMenu != null) {
+ //设置菜单样式为不可用语音模式
+ primaryMenu.setMenuShowType(EaseInputMenuStyle.DISABLE_VOICE);
+}
+```
+
+效果(EaseInputMenuStyle.DISABLE_VOICE)如下图:
+
+![img](@static/images/android/easeim10.jpeg)
+
+其他样式为:
+
+完整模式(EaseInputMenuStyle.All):
+
+![img](@static/images/android/easeim11.jpeg)
+
+不可用表情模式(EaseInputMenuStyle.DISABLE_EMOJICON):
+
+![img](@static/images/android/easeim12.jpeg)
+
+不可用语音和表情模式(EaseInputMenuStyle.DISABLE_VOICE_EMOJICON):
+
+![img](@static/images/android/easeim13.jpeg)
+
+只有文本输入模式(EaseInputMenuStyle.ONLY_TEXT):
+
+![img](@static/images/android/easeim14.jpeg)
+
+#### 增加自定义消息类型及其布局
+
+EaseIMKit 中已经为八种基本消息类型文本,表情,图片,视频,语音,文件,定位及 Custom 提供了基本的布局,ViewHolder 及 Delegate,开发者可以直接使用。但是这些类型很可能不能满足开发者的需求,那么就需要添加新的消息类型及其布局和逻辑。
+使用 EaseIMKit 只需按照以下 5 步即可快速添加自定义消息类型。
+下面我们以自定一个新的文本消息为例:
+
+1、新建 ChatTxtNewAdapterDelegate 继承 EaseMessageAdapterDelegate。
+
+```java
+public class ChatTxtNewAdapterDelegate extends EaseMessageAdapterDelegate {
+ @Override
+ protected EaseChatRow getEaseChatRow(ViewGroup parent, boolean isSender) {
+ return null;
+ }
+
+ @Override
+ protected EaseChatRowViewHolder createViewHolder(View view, MessageListItemClickListener itemClickListener) {
+ return null;
+ }
+}
+```
+
+2、新建 ChatRowTxtNew 继承 EaseChatRow 并实现相关方法
+
+```java
+public class ChatRowTxtNew extends EaseChatRow {
+ private TextView contentView;
+
+ public ChatRowTxtNew(Context context, boolean isSender) {
+ super(context, isSender);
+ }
+
+ @Override
+ protected void onInflateView() {
+ inflater.inflate(isSender ? R.layout.ease_row_sent_txt_new : R.layout.ease_row_received_txt_new, this);
+ }
+
+ @Override
+ protected void onFindViewById() {
+ contentView = (TextView) findViewById(com.hyphenate.easeui.R.id.tv_chatcontent);
+ }
+
+ @Override
+ protected void onSetUpView() {
+ EMTextMessageBody txtBody = (EMTextMessageBody) message.getBody();
+ contentView.setText(txtBody.getMessage());
+ }
+}
+```
+
+布局文件以 R.layout.ease_row_sent_txt_new 为例,如下:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+其中 ID 为 bubble 控件外的控件为发送端布局的基本组成,需要开发者拷贝进去。一般而言,开发者需要添加的控件,放在 bubble 控件内即可。如上所示,开发者只需在 `onFindViewById()` 方法中找到自己添加的控件,并在 `onSetUpView()` 方法内处理展示逻辑即可。
+
+3、新建 ChatTxtNewViewHolder 继承 EaseChatRowViewHolder 并实现相关方法
+
+```java
+public class ChatTxtNewViewHolder extends EaseChatRowViewHolder {
+ public ChatTxtNewViewHolder(@NonNull View itemView, MessageListItemClickListener itemClickListener) {
+ super(itemView, itemClickListener);
+ }
+
+ @Override
+ public void onBubbleClick(EMMessage message) {
+ super.onBubbleClick(message);
+ // 实现相关点击方法
+ }
+}
+```
+
+自定义的 ChatTxtNewViewHolder 可以复写实现 onBubbleClick(EMMessage message) 及 onResendClick(EMMessage message) 方法。需要注意的是,如果在其他地方设置了 MessageListItemClickListener 监听,并将相应的方法实现并返回 true 以后,ViewHolder 中的这两个方法会被拦截。 自定义的 ViewHolder 可以复写 onDetachedFromWindow() 方法,可以做一些释放资源的处理。 自定义的 ViewHolder 需要根据消息的方向(发送发或者接收方)对消息进行处理,可以分别复写 handleSendMessage(final EMMessage message) 或者 handleReceiveMessage(EMMessage message)。
+
+4、补全 ChatTxtNewAdapterDelegate 并重写 isForViewType 方法
+
+```java
+public class ChatTxtNewAdapterDelegate extends EaseMessageAdapterDelegate {
+ @Override
+ public boolean isForViewType(EMMessage item, int position) {
+ return item.getType() == EMMessage.Type.TXT && item.getBooleanAttribute(EaseConstant.MESSAGE_ATTR_IS_TXT_NEW, false);
+ }
+
+ @Override
+ protected EaseChatRow getEaseChatRow(ViewGroup parent, boolean isSender) {
+ return new ChatRowTxtNew(parent.getContext(), isSender);
+ }
+
+ @Override
+ protected EaseChatRowViewHolder createViewHolder(View view, MessageListItemClickListener itemClickListener) {
+ return new ChatTxtNewViewHolder(view, itemClickListener);
+ }
+}
+```
+
+:::notice
+(1)相同的消息类型(比如例子中消息类型是 EMMessage.Type.TXT)且通过标记判断类型时,在第 5 步注册对话类型时,应将该对话类型注册于基类的对话类型之前(即 ChatTxtNewAdapterDelegate 注册应在 EaseTextAdapterDelegate 之前)。
+
+(2)对于 `item.getBooleanAttribute(EaseConstant.MESSAGE_ATTR_IS_TXT_NEW, false)` 可以理解为一种标记,在发送消息时设置,如下;
+:::
+
+```java
+/**
+ * 发送新文本消息
+ * @param content
+ */
+public void sendTxtNewMessage(String content) {
+ EMMessage message = EMMessage.createTxtSendMessage(content, toChatUsername);
+ message.setAttribute(DemoConstant.MESSAGE_ATTR_IS_TXT_NEW, true);
+ EMClient.getInstance().chatManager().sendMessage(message);
+}
+```
+
+其中 `DemoConstant.MESSAGE_ATTR_IS_TXT_NEW` 为定义的一个常量,通常为字符串类型。
+
+5、注册 ChatTxtNewAdapterDelegate 对话类型(通过 EaseMessageTypeSetManager 进行注册)
+
+```java
+/**
+ * 注册对话类型
+ */
+private void registerConversationType() {
+ EaseMessageTypeSetManager.getInstance()
+ .addConversationType(EaseExpressionAdapterDelegate.class) //自定义表情
+ .addConversationType(EaseFileAdapterDelegate.class) //文件
+ ......
+ .addConversationType(ChatTxtNewAdapterDelegate.class) //新文本消息
+ .setDefaultConversionType(EaseTextAdapterDelegate.class); //文本
+}
+```
+
+需要开发者注意的是,自定义的消息类型需要注册到 EaseMessageTypeSetManager 中,具体用法可以参考环信 App 中的 [DemoHelper](https://github.com/easemob/chat-android/blob/master/app/src/main/java/com/hyphenate/easeim/DemoHelper.java) 类中的 `registerConversationType()` 方法,并在初始化时调用 `registerConversationType()` 方法。
+
+开发者在注册消息类型时,一定要在最后设置默认项(即调用 `setDefaultConversionType()`),并建议将 `EaseTextAdapterDelegate` 设置默认项。如果没有符合的消息类型,EaseIMKit 会选择默认的消息类型进行展示(注:需要展示的消息类型也需要符合默认消息的消息类型,否则会造成 EMMessageBody 强转时报错)。
+
+#### 增加长按菜单项
+
+EaseChatLayout 提供了增加菜单项的 API,开发者可方便的增加更多的菜单功能。
+
+示例代码如下:
+
+```java
+@Override
+public void initView(Bundle savedInstanceState) {
+ super.initView(savedInstanceState);
+ ......
+ // 构建菜单项 model 并通过 EaseChatLayout 添加
+ MenuItemBean itemMenu = new MenuItemBean(0, R.id.action_chat_forward, 11, getString(R.string.action_forward));
+ itemMenu.setResourceId(R.drawable.ease_chat_item_menu_forward);
+ chatLayout.addItemMenu(itemMenu );
+}
+
+......
+
+@Override
+public boolean onMenuItemClick(MenuItemBean item, EMMessage message) {
+ switch (item.getItemId()) {
+ case R.id.action_chat_forward :
+ // 增加实现逻辑,并返回 true
+ return true;
+ }
+ return false;
+}
+```
+
+#### 增加更多扩展功能
+
+EaseIMKit 提供了常用的一些扩展功能,比如发送图片,发送文件,发送定位等,但是实际开发中可能满足不了开发者的需求,EaseIMKit 提供了增加扩展功能的接口,以实现发送短视频消息、音视频通话等。
+
+以添加视频通话和音视频会议为例,示例代码如下:
+
+```java
+private void resetChatExtendMenu() {
+ // 获取到扩展功能控件
+ IChatExtendMenu chatExtendMenu = chatLayout.getChatInputMenu().getChatExtendMenu();
+ if(chatExtendMenu == null) {
+ return;
+ }
+ // 清除所有的扩展项
+ chatExtendMenu.clear();
+ // 添加自己需要的扩展功能
+ chatExtendMenu.registerMenuItem(R.string.attach_picture, R.drawable.ease_chat_image_selector, EaseChatExtendMenu.ITEM_PICTURE);
+ chatExtendMenu.registerMenuItem(R.string.attach_take_pic, R.drawable.ease_chat_takepic_selector, EaseChatExtendMenu.ITEM_TAKE_PICTURE);
+ // 根据消息类型添加不同的扩展功能
+ if(chatType == EaseConstant.CHATTYPE_SINGLE){
+ chatExtendMenu.registerMenuItem(R.string.attach_media_call, R.drawable.em_chat_video_call_selector, ITEM_VIDEO_CALL);
+ }
+ if (chatType == EaseConstant.CHATTYPE_GROUP) { // 音视频会议
+ chatExtendMenu.registerMenuItem(R.string.voice_and_video_conference, R.drawable.em_chat_video_call_selector, ITEM_CONFERENCE_CALL);
+ }
+}
+
+......
+
+@Override
+public void onChatExtendMenuItemClick(View view, int itemId) {
+ super.onChatExtendMenuItemClick(view, itemId);
+ // 只需实现不满足需求或者开发者自己添加的扩展功能
+ switch (itemId) {
+ case ITEM_VIDEO_CALL:
+ // 实现条目点击事件
+ break;
+ case ITEM_CONFERENCE_CALL:
+ // 实现条目点击事件
+ break;
+ }
+}
+```
+
+#### 增加自定义表情
+
+EaseIMKit 也提供了增加自定义表情的接口,开发者可仿照 EmojiconExampleGroupData 进行定义类型的表情类,然后调用相应接口加入即可。
+
+代码如下:
+
+```java
+// 添加扩展表情
+chatLayout.getChatInputMenu().getEmojiconMenu().addEmojiconGroup(EmojiconExampleGroupData.getData());
+```
+
+### 设置联系人列表
+
+通讯录列表界面可以设置如下样式:
+
+- 展示模式:分为简洁模式和常规模式
+- 条目:设置条目背景,条目高度及 header 背景
+- 头像:设置默认头像,头像样式及头像大小等
+
+在 EaseContactListFragment 及其子类中可以直接获取到 EaseContactLayout 这个控件,然后通过这个控件进行设置。
+
+代码如下:
+
+```java
+// 获取列表控件
+EaseContactListLayout contactList = contactLayout.getContactList();
+// 设置条目高度
+contactList.setItemHeight((int) EaseCommonUtils.dip2px(mContext, 80));
+// 设置条目背景
+contactList.setItemBackGround(ContextCompat.getDrawable(mContext, R.color.gray));
+// 设置头像样式:0 为默认,1 为圆形,2 为方形(设置方形时,需要配合设置 avatarRadius,默认的 avatarRadius 为 50dp)
+contactList.setAvatarShapeType(2);
+// 设置头像圆角
+contactList.setAvatarRadius((int) EaseCommonUtils.dip2px(mContext, 5));
+// 设置 header 背景
+contactList.setHeaderBackGround(ContextCompat.getDrawable(mContext, R.color.white));
+```
+
+效果如图:
+
+![img](@static/images/android/easeim15.jpeg)
+
+设置简洁模式
+
+```java
+//设置为简洁模式
+contactLayout.showSimple();
+```
+
+效果如图:
+
+![img](@static/images/android/easeim16.jpeg)
+
+#### 增加长按菜单项
+
+EaseContactListLayout 提供了增加菜单项的 API,开发者可方便的增加更多的菜单功能。
+
+示例代码如下:
+
+```java
+// 通过增加 `OnPopupMenuPreShowListener` 监听,并在 `onMenuPreShow` 中增加菜单项更简单
+@Override
+public void onMenuPreShow(EasePopupMenuHelper menuHelper, int position) {
+ super.onMenuPreShow(menuHelper, position);
+ // 增加需要的菜单项,其中传入的第三项 order 决定了菜单项的位置
+ menuHelper.addItemMenu(1, R.id.action_friend_block, 2, getString(R.string.em_friends_move_into_the_blacklist_new));
+ menuHelper.addItemMenu(1, R.id.action_friend_delete, 1, getString(R.string.ease_friends_delete_the_contact));
+}
+
+......
+
+@Override
+public boolean onMenuItemClick(MenuItem item, int position) {
+ EaseUser user = contactLayout.getContactList().getItem(position);
+ switch (item.getItemId()) {
+ case R.id.action_friend_block :
+ // 增加处理逻辑并返回 `true`
+ return true;
+ case R.id.action_friend_delete:
+ // 增加处理逻辑并返回 `true`
+ return true;
+ }
+ return super.onMenuItemClick(item, position);
+}
+```
+
+#### 增加头布局
+
+EaseIMKit 默认是不再通讯录列表之前增加头布局的,但是内部预设了添加头布局的逻辑,开发者可通过 EaseContactListLayout 提供的 API 快速的增加一个或者多个头布局。
+
+示例代码如下:
+
+```java
+/**
+ * 添加头布局
+ */
+public void addHeader() {
+ // 增加多个头布局
+ contactLayout.getContactList().addCustomItem(CUSTOM_NEW_CHAT, R.drawable.em_friends_new_chat, getString(R.string.em_friends_new_chat));
+ contactLayout.getContactList().addCustomItem(CUSTOM_GROUP_LIST, R.drawable.em_friends_group_chat, getString(R.string.em_friends_group_chat));
+ contactLayout.getContactList().addCustomItem(CUSTOM_CHAT_ROOM_LIST, R.drawable.em_friends_chat_room, getString(R.string.em_friends_chat_room));
+}
+
+......
+
+@Override
+public void initListener() {
+ super.initListener();
+ ......
+ contactLayout.getContactList().setOnCustomItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(View view, int position) {
+ EaseContactCustomBean item = contactLayout.getContactList().getCustomAdapter().getItem(position);
+ switch (item.getId()) {
+ case CUSTOM_NEW_CHAT :
+ // 增加实现逻辑
+ break;
+ case CUSTOM_GROUP_LIST :
+ // 增加实现逻辑
+ break;
+ case CUSTOM_CHAT_ROOM_LIST :
+ // 增加实现逻辑
+ break;
+ }
+ }
+ });
+}
+```
+
+### 设置用户头像和昵称属性
+
+#### 设置头像和昵称
+
+环信 IM SDK 不做用户信息存储,如果用户想要展示自定义的头像及昵称,可以通过 EaseUserProfileProvider 进行提供。
+
+首先需要在合适的时机去设置 EaseUserProfileProvider,例如:
+
+```java
+EaseIM.getInstance().setUserProvider(new EaseUserProfileProvider() {
+ @Override
+ public EaseUser getUser(String username) {
+ //根据 username,从数据库中或者内存中取出之前保存的用户信息,如从数据库中取出的用户对象为 DemoUserBean
+ DemoUserBean bean = getUserFromDbOrMemery(username);
+ EaseUser user = new EaseUser(username);
+ ......
+ //设置用户昵称
+ user.setNickname(bean.getNickname());
+ //设置头像地址
+ user.setAvatar(bean.getAvatar());
+ //最后返回构建的 EaseUser 对象
+ return user;
+ }
+});
+```
+
+EaseIMKit 中会话列表,聊天列表及联系人列表,内部已经添加 EaseUserProfileProvider 的判断,当展示数据时优先从 EaseUserProfileProvider 获取头像和昵称数据,如果有则展示,如果没有头像采用默认头像,昵称展示为环信 ID。
+
+:::notice 建议方案
+开发者先将相关用户信息从服务器中获取并存储到数据库中,在 getUser(String username) 方法调用时,从数据库中根据 username(环信 ID)取出相应的用户数据,生成 EaseUser 对象 user,并给 user 赋值 nickname 及 avatar 属性,最后返回这个 user 即可。
+:::
+
+#### 统一设置头像样式
+
+EaseIMKit 提供了 `EaseAvatarOptions` 这个类用于全局配置头像的样式,包括形状,圆角半径,描边宽度及描边颜色。会话,聊天及联系人中已经增加了对于 EaseAvatarOptions 的支持。
+
+示例代码如下:
+
+```java
+//设置头像配置属性
+EaseIM.getInstance().setAvatarOptions(getAvatarOptions());
+......
+/**
+ * 统一配置头像
+ * @return
+ */
+private EaseAvatarOptions getAvatarOptions() {
+ EaseAvatarOptions avatarOptions = new EaseAvatarOptions();
+ //设置头像形状为圆形,1 代表圆形,2 代表方形
+ avatarOptions.setAvatarShape(1);
+ return avatarOptions;
+}
+```
+
+使用时可以直接调用 EaseUserUtils 中的 `setUserAvatarStyle(EaseImageView imageView)` 方法即可设置。
+
+## 事件处理
+
+EaseIMKit 还帮助开发者实现了一系列的事件监听接口,比如条目的点击事件,条目的长按事件,菜单项的点击事件等。
+
+### 会话列表
+
+#### 条目点击事件
+
+开发者如果使用 `EaseConversationListFragment` 及其子类,可以重写 `onItemClick(View view, int position)` 方法即可。
+
+代码如下:
+
+```java
+@Override
+public void onItemClick(View view, int position) {
+ super.onItemClick(view, position);
+ //添加点击事件实现逻辑
+}
+```
+
+开发者如果直接使用的 `EaseConversationListLayout` 控件,则可通过该控件设置条目的点击事件。
+
+代码如下:
+
+```java
+conversationListLayout.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(View view, int position) {
+ //添加点击事件实现逻辑
+ }
+});
+```
+
+#### 长按事件及弹出菜单点击事件
+
+`EaseConversationListLayout` 中已经实现了一套默认的长按弹出菜单逻辑,开发者只需实现弹出菜单的点击事件即可。
+
+如果开发者使用的是 `EaseConversationListFragment` 及其子类,则直接重写 `onMenuItemClick(MenuItem item, int position)` 即可。
+
+代码如下:
+
+```java
+@Override
+public boolean onMenuItemClick(MenuItem item, int position) {
+ //添加具体的点击事件实现逻辑,并返回 true
+ return super.onMenuItemClick(item, position);
+}
+```
+
+开发者如果直接使用的 EaseConversationListLayout 控件,则可通过该控件设置弹出菜单的点击事件。
+
+代码如下:
+
+```java
+conversationListLayout.setOnPopupMenuItemClickListener(new OnPopupMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item, int position) {
+ //添加具体的点击事件实现逻辑,并返回 true
+ return false;
+ }
+});
+```
+
+如果开发者需要自己实现弹出菜单,通过 EaseConversationListLayout 控件添加条目的长按监听并实现即可。
+
+代码如下:
+
+```java
+conversationListLayout.setOnItemLongClickListener(new OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(View view, int position) {
+ //添加弹出菜单的逻辑,并返回 true
+ return false;
+ }
+});
+```
+
+### 聊天区域
+
+#### 聊天列表事件
+
+开发者如果使用的是 EaseChatFragment 及其子类,则可以根据需要重写相关的事件方法即可。聊天列表中的常用监听事件均封装到了 OnChatLayoutListener 接口中,EaseChatFragment 已经设置了该监听。
+
+`OnChatLayoutListener` 中有如下事件监听:
+
+```java
+public interface OnChatLayoutListener {
+ /**
+ * 点击消息 bubble 区域
+ * @param message
+ * @return
+ */
+ boolean onBubbleClick(EMMessage message);
+
+ /**
+ * 长按消息 bubble 区域
+ * @param v
+ * @param message
+ * @return
+ */
+ boolean onBubbleLongClick(View v, EMMessage message);
+
+ /**
+ * 点击头像
+ * @param username
+ */
+ void onUserAvatarClick(String username);
+
+ /**
+ * 长按头像
+ * @param username
+ */
+ void onUserAvatarLongClick(String username);
+
+ /**
+ * 条目点击
+ * @param view
+ * @param itemId
+ */
+ void onChatExtendMenuItemClick(View view, int itemId);
+
+ /**
+ * EditText 文本变化监听
+ * @param s
+ * @param start
+ * @param before
+ * @param count
+ */
+ void onTextChanged(CharSequence s, int start, int before, int count);
+
+ /**
+ * 聊天中错误信息
+ * @param code
+ * @param errorMsg
+ */
+ void onChatError(int code, String errorMsg);
+
+ /**
+ * 用于监听其他人正在数据事件
+ * @param action 输入事件 TypingBegin为开始 TypingEnd 为结束
+ */
+ default void onOtherTyping(String action){}
+
+}
+```
+
+如果开发者使用的是 EaseChatLayout 控件,则通过该控件实现 OnChatLayoutListener 接口即可。
+
+#### 长按事件及弹出菜单点击事件
+
+EaseChatLayout 中已经实现了一套默认的长按弹出菜单逻辑,并对默认的菜单项进行了处理,如果开发者对默认菜单项有其他实现需求,只需实现弹出菜单的点击事件即可。
+
+如果开发者使用的是 EaseChatFragment 及其子类,则直接重写 onMenuItemClick(MenuItemBean item, EMMessage message) 即可。
+
+代码如下:
+
+```java
+@Override
+public boolean onMenuItemClick(MenuItemBean item, EMMessage message) {
+ //添加菜单条目点击事件实现逻辑,并返回 true
+ //返回 true 表示不采用默认的实现逻辑
+ return false;
+}
+```
+
+如果开发者需要在弹出菜单展示前对菜单项进行处理,重写 `onPreMenu(EasePopupWindowHelper helper, EMMessage message)` 并处理即可。
+
+示例代码如下:
+
+```java
+@Override
+public void onPreMenu(EasePopupWindowHelper helper, EMMessage message) {
+ // 默认两分钟后,即不可撤回
+ if(System.currentTimeMillis() - message.getMsgTime() > 2 * 60 * 1000) {
+ helper.findItemVisible(R.id.action_chat_recall, false);
+ }
+ EMMessage.Type type = message.getType();
+ helper.findItemVisible(R.id.action_chat_forward, false);
+ switch (type) {
+ case TXT:
+ if(!message.getBooleanAttribute(DemoConstant.MESSAGE_ATTR_IS_VIDEO_CALL, false)
+ && !message.getBooleanAttribute(DemoConstant.MESSAGE_ATTR_IS_VOICE_CALL, false)) {
+ helper.findItemVisible(R.id.action_chat_forward, true);
+ }
+ break;
+ case IMAGE:
+ helper.findItemVisible(R.id.action_chat_forward, true);
+ break;
+ }
+
+ if(chatType == DemoConstant.CHATTYPE_CHATROOM) {
+ helper.findItemVisible(R.id.action_chat_forward, true);
+ }
+}
+```
+
+开发者如果直接使用的 EaseChatLayout 控件,则可通过该控件设置弹出菜单的点击事件。
+
+代码如下:
+
+```java
+chatLayout.setOnPopupWindowItemClickListener(new OnMenuChangeListener() {
+ @Override
+ public void onPreMenu(EasePopupWindowHelper helper, EMMessage message) {
+ // 菜单预处理逻辑
+ }
+ @Override
+ public boolean onMenuItemClick(MenuItemBean item, EMMessage message) {
+ // 菜单项点击事件,如果默认实现不满足,则自己实现并返回 true。
+ return false;
+ }
+});
+```
+
+如果开发者需要自己实现弹出菜单,通过 EaseChatLayout 控件找到 EaseChatMessageListLayout 并添加条目的长按监听并实现即可。
+
+代码如下:
+
+```java
+chatLayout.getChatMessageListLayout().setOnItemLongClickListener(new OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(View view, int position) {
+ //添加弹出菜单的逻辑,并返回 true
+ return false;
+ }
+});
+```
+
+### 通讯录列表
+
+#### 条目点击事件
+
+开发者如果使用 EaseContactListFragment 及其子类,可以重写 `onItemClick(View view, int position)` 方法即可。
+
+代码如下:
+
+```java
+@Override
+public void onItemClick(View view, int position) {
+ super.onItemClick(view, position);
+ //添加点击事件实现逻辑
+}
+```
+
+开发者如果直接使用的 EaseContactLayout 控件,则可通过该控件找到 EaseContactListLayout 并设置条目的点击事件。
+
+代码如下:
+
+```java
+contactLayout.getContactList().setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(View view, int position) {
+ //添加点击事件实现逻辑
+ }
+});
+```
+
+#### 长按事件及弹出菜单点击事件
+
+EaseContactListLayout 中已经实现了一套默认的长按弹出菜单逻辑,开发者只需实现弹出菜单的点击事件即可。
+
+如果开发者使用的是 EaseContactListFragment 及其子类,则直接重写 `onMenuItemClick(MenuItem item, int position)` 即可。
+
+代码如下:
+
+```java
+@Override
+public boolean onMenuItemClick(MenuItem item, int position) {
+ // 添加具体的点击事件实现逻辑,并返回 'true'
+ return super.onMenuItemClick(item, position);
+}
+```
+
+如果开发者需要在弹出菜单展示前对菜单项进行处理,重写 `onMenuPreShow(EasePopupWindowHelper helper, EMMessage message)` 并处理即可。
+
+开发者如果直接使用的 EaseContactLayout 控件,则可通过该控件找到 EaseContactListLayout 并设置弹出菜单的点击事件。
+
+代码如下:
+
+```java
+contactLayout.getContactList().setOnPopupMenuItemClickListener(new OnPopupMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item, int position) {
+ //添加具体的点击事件实现逻辑,并返回 true
+ return false;
+ }
+});
+```
+
+相应的菜单项预处理,需要通过 EaseContactListLayout 设置菜单预处理监听事件。 代码如下:
+
+```java
+contactLayout.getContactList().setOnPopupMenuPreShowListener(new OnPopupMenuPreShowListener() {
+ @Override
+ public void onMenuPreShow(EasePopupMenuHelper menuHelper, int position) {
+ //菜单预处理逻辑
+ }
+});
+```
+
+如果开发者需要自己实现弹出菜单,通过 EaseContactListLayout 控件添加条目的长按监听并实现即可。
+
+代码如下:
+
+```java
+contactLayout.getContactList().setOnItemLongClickListener(new OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(View view, int position) {
+ //添加弹出菜单的逻辑,并返回 true
+ return false;
+ }
+});
+```
+
+## 功能扩展
+
+### 系统消息
+
+EaseIMKit 中 EaseConversationListLayout 已经封装了 IM 通知的展示逻辑,但是需要开发者将 IM 的通知封装成系统消息并保存到本地数据库。为了方便开发者封装成符合 EaseIMKit 能够使用的系统消息,EaseIMKit 中提供了 EaseSystemMsgManager 管理类,开发者可通过该管理类,方便的封装及更新系统消息。
+
+EaseIMKit 可处理的系统消息有如下要求:
+
+```java
+// 设置为文本消息
+EMMessage emMessage = EMMessage.createReceiveMessage(EMMessage.Type.TXT);
+// 设置 from 为固定的 "em_system"
+emMessage.setFrom(EaseConstant.DEFAULT_SYSTEM_MESSAGE_ID);
+emMessage.setMsgId(UUID.randomUUID().toString());
+emMessage.setStatus(EMMessage.Status.SUCCESS);
+```
+
+当然 EaseSystemMsgManager 管理类已经做了默认处理,开发者只需传入文本内容(会话列表中展示的内容)及扩展内容 ext(Map)即可。 示例如下:
+
+```java
+@Override
+public void onFriendRequestDeclined(String username) {
+ EMLog.i("ChatContactListener", "onFriendRequestDeclined");
+ Map ext = EaseSystemMsgManager.getInstance().createMsgExt();
+ ext.put(DemoConstant.SYSTEM_MESSAGE_FROM, username);
+ ext.put(DemoConstant.SYSTEM_MESSAGE_STATUS, InviteMessageStatus.BEREFUSED.name());
+ EMMessage message = EaseSystemMsgManager.getInstance().createMessage(PushAndMessageHelper.getSystemMessage(ext), ext);
+ ......
+}
+```
+
+同时 EaseConversationListLayout 提供了是否展示系统消息的 API,showSystemMessage(boolean show) 用于控制是否展示系统消息。
diff --git a/docs/uikit/flutter/key_function_chat_page.md b/docs/uikit/flutter/key_function_chat_page.md
new file mode 100644
index 000000000..a2be054a7
--- /dev/null
+++ b/docs/uikit/flutter/key_function_chat_page.md
@@ -0,0 +1,334 @@
+# 聊天页面
+
+用户可以在聊天页面中进行单聊、群聊或聊天室聊天。该页面分为消息列表和消息输入区域。
+
+## 创建聊天界面
+
+flutter_chat_uikit 提供了 `ChatMessagesView`,添加到 `build` 中并传入相应的参数即可用。
+
+| 参数 | 类型 | 是否必需 | 描述 |
+| :------------- | :-----| :----- | :-------- |
+| `conversation` | `EMConversation` | 是 | `ChatMessagesView` 对应的会话对象。 |
+| `inputBarTextEditingController` | | 否 | 输入框中的 `TextField` 对应的 Controller。|
+| `background` | | 否 | `ChatMessagesView` 的背景图。|
+| `inputBar` | | 否 | 输入框组件。 如不设置,默认使用 `ChatInputBar`。|
+| `onTap`| | 否 | 消息气泡点击事件。 |
+| `onBubbleLongPress` | | 否 | 消息气泡长按事件。|
+| `onBubbleDoubleTap`| | 否 | 消息气泡双击事件。|
+| `avatarBuilder` | | 否 | 头像 widget builder。|
+| `nicknameBuilder` | | 否 | 昵称 Widget builder。|
+| `itemBuilder`| | 否 | 消息气泡 Widget builder。|
+| `moreItems` | | 否 | 长按消息气泡后显示的操作项。如果在 `onBubbleLongPress` 中返回 `null`,将使用 `moreItems`。默认显示三个操作:复制、删除和消息撤回。 |
+| `messageListViewController` | | 否 | 消息列表 Controller, 详见 `ChatMessageListController`。|
+| `willSendMessage` | | 否 | 消息将要发送的事件,需要返回一个 `EMMessage` 对象。 |
+| `onError` | |否|错误回调,如权限错误等。|
+| `enableScrollBar` | | 否 | 是否启用滚动条。默认启用。 |
+| `needDismissInputWidget` | | 否 |用于取消输入 Widget 的回调。如果使用自定义输入 Widget,则在接收回调时取消输入 Widget,例如,通过调用 `FocusNode.unfocus`。详见 `ChatInputBar`。 |
+| `inputBarMoreActionsOnTap` | | 否 | 单击输入框旁边的 `+` 的回调。需要返回 `ChatBottomSheetItems` 列表。 |
+
+```dart
+class _MessagesPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: Text(widget.conversation.id)),
+ body: SafeArea(
+ // UIKit 中的聊天页面。
+ child: ChatMessagesView(
+ conversation: widget.conversation,
+ ),
+ ),
+ );
+ }
+}
+```
+
+
+
+## 自定义实现
+
+### 设置主题颜色
+
+可以通过修改主题中的属性来改变消息页面中的颜色和字体。
+
+```dart
+class MyApp extends StatelessWidget {
+ const MyApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Flutter Demo',
+ theme: ThemeData(
+ primarySwatch: Colors.blue,
+ ),
+ builder: (context, child) {
+ // ChatUIKit 需要在你使用 `flutter_chat_uikit` widget 的根节点上。
+ return ChatUIKit(
+ // ChatUIKitTheme 主题。
+ theme: ChatUIKitTheme(),
+ child: child!,
+ );
+ },
+ home: const MyHomePage(title: 'Flutter Demo Home Page'),
+ );
+ }
+}
+```
+
+参数详情如下表所示:
+
+| 参数 | 类型 | 是否必需 | 描述 |
+| :------------- | :-----| :----- | :-------- |
+| `badgeColor` | `Color` | 否 | 未读数角标颜色。 |
+| `badgeBorderColor` | `Color` | 否 | 未读数 `border` 颜色。|
+| `badgeTextStyle` | `TextStyle` | 否 | 未读数角标的字体。|
+| `sendVoiceItemIconColor` | `Color` | 否 | 发送的语音消息 `bubble` 中图标的颜色。|
+| `receiveVoiceItemIconColor` | `Color` | 否 | 接收的语音消息 `bubble` 中图标的颜色。|
+| `sendBubbleColor` | `Color` | 否 | 发送消息的气泡颜色。|
+| `receiveBubbleColor` | `Color` | 否 | 收到消息的气泡颜色。|
+| `sendTextStyle` | `TextStyle` | 否 | 发送文字消息的字体。|
+| `receiveTextStyle` | `TextStyle` | 否 | 接收文字消息的字体。|
+| `conversationListItemTitleStyle` | `TextStyle` | 否 | 会话列表条目标题的字体。|
+| `conversationListItemSubTitleStyle` | `TextStyle` | 否 | 会话列表条目副标题的字体。|
+| `conversationListItemTsStyle` | `TextStyle` | 否 | 会话列表条目时间的字体。|
+| `messagesListItemTsStyle` | `TextStyle` | 否 | 消息列表条目中时间的字体。|
+| `inputWidgetSendBtnColor` | `Color` | 否 | 表情键盘中发送按钮的颜色。|
+| `inputWidgetSendBtnStyle` | `TextStyle` | 否 | 表情键盘中发送按钮的字体。|
+
+### 添加头像
+
+通过设置 `ChatMessagesView` 中的 `avatarBuilder` 实现自定义头像的功能。
+
+```dart
+class _MessagesPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: Text(widget.conversation.id)),
+ body: SafeArea(
+ // UIKit 中的聊天页面。
+ child: ChatMessagesView(
+ conversation: widget.conversation,
+ avatarBuilder: (context, userId) {
+ // 返回你要显示的头像 Widget。
+ return Container(
+ width: 30,
+ height: 30,
+ color: Colors.red,
+ );
+ },
+ ),
+ ),
+ );
+ }
+}
+```
+
+效果如下图所示:
+
+
+
+### 添加昵称
+
+通过设置 `ChatMessagesView` 中的 `nicknameBuilder` 实现自定义昵称的功能。
+
+```dart
+class _MessagesPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: Text(widget.conversation.id)),
+ body: SafeArea(
+ // UIKit 中的聊天页面。
+ child: ChatMessagesView(
+ conversation: widget.conversation,
+ // 返回你要显示的昵称 widget。
+ nicknameBuilder: (context, userId) {
+ return Text(userId);
+ },
+ ),
+ ),
+ );
+ }
+}
+```
+
+效果如下图所示:
+
+
+
+### 添加气泡点击事件
+
+通过设置 `ChatMessagesView` 中的 `onTap` 实现自定义点击功能。
+
+```dart
+class _MessagesPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: Text(widget.conversation.id)),
+ body: SafeArea(
+ // UIKit 中的聊天页面。
+ child: ChatMessagesView(
+ conversation: widget.conversation,
+ // 条目点击事件。
+ onTap: (context, message) {
+ bubbleClicked(message);
+ return true;
+ },
+ ),
+ ),
+ );
+ }
+
+ void bubbleClicked(EMMessage message) {
+ SnackBar bar = const SnackBar(
+ content: Text('气泡被点击'),
+ duration: Duration(milliseconds: 1000),
+ );
+ ScaffoldMessenger.of(context).showSnackBar(bar);
+ }
+}
+```
+
+效果如下图所示:
+
+
+
+### 自定义消息气泡样式
+
+通过设置 `ChatMessagesView` 中的 `itemBuilder` 实现自定义气泡样式。
+
+```dart
+class _MessagesPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: Text(widget.conversation.id)),
+ body: SafeArea(
+ // UIKit 中的聊天页面。
+ child: ChatMessagesView(
+ conversation: widget.conversation,
+ itemBuilder: (context, model) {
+ if (model.message.body.type == MessageType.TXT) {
+ // 自定义消息气泡。
+ return CustomTextItemWidget(
+ model: model,
+ onTap: (context, message) {
+ bubbleClicked(message);
+ return true;
+ },
+ );
+ }
+ },
+ ),
+ ),
+ );
+ }
+
+ void bubbleClicked(EMMessage message) {
+ SnackBar bar = const SnackBar(
+ content: Text('气泡被点击'),
+ duration: Duration(milliseconds: 1000),
+ );
+ ScaffoldMessenger.of(context).showSnackBar(bar);
+ }
+}
+
+
+//自定义消息气泡。
+class CustomTextItemWidget extends ChatMessageListItem {
+ const CustomTextItemWidget({super.key, required super.model, super.onTap});
+
+ @override
+ Widget build(BuildContext context) {
+ EMTextMessageBody body = model.message.body as EMTextMessageBody;
+
+ Widget content = Text(
+ body.content,
+ style: const TextStyle(
+ color: Colors.black,
+ fontSize: 50,
+ fontWeight: FontWeight.w400,
+ ),
+ );
+ return getBubbleWidget(content);
+ }
+}
+```
+
+效果如下图所示:
+
+
+
+### 自定义输入框样式
+
+通过设置 `ChatMessagesView` 中的 `inputBar` 实现自定义输入框,同时用过实现 `ChatMessageListController` 发送信息。
+
+```dart
+class _MessagesPageState extends State {
+ late ChatMessageListController _msgController;
+ final TextEditingController _textController = TextEditingController();
+ final FocusNode _focusNode = FocusNode();
+ @override
+ void initState() {
+ super.initState();
+ _msgController = ChatMessageListController(widget.conversation);
+ }
+
+ @override
+ void dispose() {
+ _focusNode.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: Text(widget.conversation.id)),
+ body: SafeArea(
+ // UIKit 中的聊天页面。
+ child: ChatMessagesView(
+ conversation: widget.conversation,
+ messageListViewController: _msgController,
+ inputBar: customInputWidget(),
+ needDismissInputWidget: () {
+ _focusNode.unfocus();
+ },
+ ),
+ ),
+ );
+ }
+
+ // 自定义输入 Widget。
+ Widget customInputWidget() {
+ return SizedBox(
+ height: 50,
+ child: Row(
+ children: [
+ Expanded(
+ child: TextField(
+ focusNode: _focusNode,
+ controller: _textController,
+ ),
+ ),
+ ElevatedButton(
+ onPressed: () {
+ final msg = EMMessage.createTxtSendMessage(
+ targetId: widget.conversation.id,
+ content: _textController.text);
+ _textController.text = '';
+ _msgController.sendMessage(msg);
+ },
+ child: const Text('Send'))
+ ],
+ ),
+ );
+ }
+}
+```
+
+效果如下图所示:
+
+
diff --git a/docs/uikit/flutter/key_function_conversation_list.md b/docs/uikit/flutter/key_function_conversation_list.md
new file mode 100644
index 000000000..e57cc194a
--- /dev/null
+++ b/docs/uikit/flutter/key_function_conversation_list.md
@@ -0,0 +1,157 @@
+# 会话列表页面
+
+会话列表页面展示了用户当前所有的单聊、群聊和聊天室会话。你可以创建会话列表界面,并设置自定义头像、昵称和界面的展示样式,添加自定义点击事件。
+
+## 创建会话列表界面
+
+flutter_chat_uikit 提供了 `ChatConversationsView`,添加到 `build` 中并传入相应的参数。
+
+```dart
+import 'package:flutter/material.dart';
+import 'package:flutter_chat_uikit/flutter_chat_uikit.dart';
+
+class ConversationsPage extends StatefulWidget {
+ const ConversationsPage({super.key});
+
+ @override
+ State createState() => _ConversationsPageState();
+}
+
+class _ConversationsPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('ConversationPage'),
+ ),
+ body: ChatConversationsView(),
+ );
+ }
+}
+```
+
+效果图如下所示:
+
+
+
+参数详情如下表所示:
+
+通过 `ChatConversationsView`,你可以快速显示和管理当前会话。
+
+| 属性 | 描述 |
+| :-------------- | :----- |
+| `controller` | `ChatConversationsView` Controller。 |
+| `itemBuilder` | 会话列表条目 builder。若需进行自定义,会返回 widget。 |
+| `avatarBuilder` | 头像 builder。若该属性未实现或你返回了 `null`,会使用默认头像。|
+| `nicknameBuilder` | 昵称 builder。若你未设置该属性或返回了 `null`,会显示会话 ID。 |
+| `onItemTap` | 会话列表条目的点击事件的回调。 |
+| `backgroundWidgetWhenListEmpty` | 列表为空时为背景 widget。 |
+
+## 自定义实现
+
+### 设置会话头像
+
+通过设置 `ChatConversationsView` 中的 `avatarBuilder` 实现自定义头像的功能。
+
+```dart
+class _ConversationsPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('ConversationPage'),
+ ),
+ body: ChatConversationsView(
+ avatarBuilder: (context, conversation) {
+ return const CircleAvatar(
+ child: Text('Avatar'),
+ );
+ },
+ ),
+ );
+ }
+}
+```
+
+效果如下图所示:
+
+
+
+### 设置会话名称
+
+通过设置 `ChatConversationsView` 中的 `nicknameBuilder` 实现显示昵称功能。
+
+```dart
+class _ConversationsPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('ConversationPage'),
+ ),
+ body: ChatConversationsView(
+ nicknameBuilder: (context, conversation) {
+ return Text('nickname');
+ },
+ ),
+ );
+ }
+}
+```
+
+效果如下图所示:
+
+
+
+### 自定义显示样式
+
+通过设置 `ChatConversationsView` 中的 `itemBuilder` 实现自定义显示会话。
+
+```dart
+class _ConversationsPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('ConversationPage'),
+ ),
+ body: ChatConversationsView(
+ itemBuilder: (context, index, conversation) {
+ return ListTile(
+ title: Text(conversation.id),
+ subtitle: Text('subtitle'),
+ );
+ },
+ ),
+ );
+ }
+}
+```
+效果如下图所示:
+
+
+
+### 自定义点击事件
+
+通过设置 `ChatConversationsView` 中的 `onItemTap` 实现会话列表点击。
+
+```dart
+class _ConversationsPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('ConversationPage'),
+ ),
+ body: ChatConversationsView(
+ onItemTap: (conversation) {
+ print(conversation.id);
+ },
+ ),
+ );
+ }
+}
+```
+效果如下图所示:
+
+
diff --git a/docs/uikit/flutter/overview.md b/docs/uikit/flutter/overview.md
index 8013c93ef..9c901181e 100644
--- a/docs/uikit/flutter/overview.md
+++ b/docs/uikit/flutter/overview.md
@@ -1 +1,30 @@
-## UIKit 集成文档
+# 概述
+
+flutter_chat_uikit 是基于环信 IM SDK 的一款 UI 组件库,提供通用的 UI 组件,例如会话列表、聊天界面。利用该组件库,开发者可根据实际业务需求快速地搭建自定义 IM 应用。flutter_chat_uikit 中的组件在实现 UI 功能的同时,调用 IM SDK 相关接口实现 IM 相关逻辑和数据的处理,因而开发者在使用 flutter_chat_uikit 时只需关注自身业务或个性化扩展即可。
+
+查看 flutter_chat_uikit 源码,请点击[这里](https://github.com/easemob/flutter_chat_uikit)。
+
+## 功能
+
+目前,flutter_chat_uikit 提供以下功能:
+
+- 快速搭建聊天页面。
+- 快速搭建会话列表页面。
+
+flutter_chat_uikit 在单聊、群聊和聊天室会话中提供下列特性,提升聊天体验。
+
+| 特性 | 描述 |
+| :-------------- | :----------------------------- |
+| 文本消息 | 发送和接收文本消息。 |
+| 图片消息 | 发送和接收图片消息。 |
+| 语音消息 | 发送和接收语音消息。 |
+| 文件消息 | 发送和接收文件消息。 |
+| 已读回执 | 提示接收方的已读状态。该功能仅适用于单聊会话。 |
+| 未读消息数 | 显示收到消息的未读数。 |
+| 消息表情回复 | 回复消息表情。 |
+| 消息删除 | 删除本地消息。 |
+| 消息撤回 | 在指定时间内对已发送消息撤回。 |
+
+
+
+
diff --git a/docs/uikit/flutter/quickstart.md b/docs/uikit/flutter/quickstart.md
new file mode 100644
index 000000000..00bc54d0c
--- /dev/null
+++ b/docs/uikit/flutter/quickstart.md
@@ -0,0 +1,157 @@
+# 快速开始
+
+利用 flutter_chat_uikit 提供的 UI 组件,你可以轻松实现应用内的聊天。flutter_chat_uikit 支持单聊、群聊和聊天室会话。本文介绍如何实现在单聊会话中发送消息。
+
+
+
+
+## 前提条件
+
+集成 flutter_chat_uikit 前,你的开发环境需要满足以下条件:
+
+1. 有效的环信即时通讯 IM 开发者账号,创建应用并获取 App Key。
+2. 在[环信控制台](https://console.easemob.com/index)[创建两个用户用于聊天](/product/enable_and_configure_IM.html#创建-im-用户)。
+
+### Android 平台
+
+- Flutter 2.5.0 或以上版本
+- Dart 2.19.1 或以上版本
+- macOS 或 Windows 系统
+- 支持 JDK 1.8 或以上版本的 Android Studio 4.0 或以上版本
+- 运行 Android SDK API 级别 21 或以上的 Android 模拟器或真机
+
+### iOS 平台
+
+- Flutter 2.5.0 或以上版本
+- Dart 2.19.1 或以上版本
+- macOS
+- 安装有 Xcode 命令行工具的 Xcode 12.4 或以上版本
+- CocoaPods
+- 运行 iOS 10.0 或以上版本的 iOS 模拟器或真机
+
+## 所需权限
+
+### Android
+
+```xml
+
+
+
+
+
+
+
+```
+
+### iOS
+
+| 键 | 值 |
+| :------------ | :------- |
+| `Privacy - Microphone Usage Description` | 麦克风权限 |
+| `Privacy - Camera Usage Description` | 摄像头权限 |
+| `Privacy - Photo Library Usage Description` | 相册权限 |
+
+## 防止代码混淆
+
+安卓中需要配置防止代码混淆,在 `proguard-rules.pro` 文件中添加以下代码:
+
+```
+-keep class com.hyphenate.** {*;}
+-dontwarn com.hyphenate.**
+```
+
+## 发送第一条消息
+
+### 第一步 集成 flutter_chat_uikit
+
+flutter_chat_uikit 支持 pub.dev 接入和本地源码集成。
+
+- pub.dev 接入集成:
+
+```dart
+flutter pub add flutter_chat_uikit
+flutter pub get
+```
+
+- 本地源码集成:
+
+```dart
+dependencies:
+ flutter_chat_uikit:
+ path: `<#uikit path#>`
+```
+
+flutter_chat_uikit 使用了以下第三方依赖库:
+
+```dart
+dependencies:
+ intl: ^0.18.0
+ image_picker: ^0.8.6+4
+ file_picker: ^4.6.1
+ record: ^4.4.4
+ audioplayers: ^3.0.1
+ im_flutter_sdk: ^4.1.0
+```
+
+### 第二步 初始化即时通讯 IM SDK
+
+在 app 的 `main` 下调用 SDK 初始化方法。
+
+:::notice
+flutter_chat_uikit 不包含 IM SDK 的初始化和登录,使用时确保已完成 SDK 初始化和登录。
+:::
+
+```dart
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ final options = ChatOptions(
+ appKey: <#Your AppKey#>,
+ );
+ await EMClient.getInstance.init(options);
+ runApp(const MyApp());
+}
+```
+
+#### 第三步 创建聊天界面
+
+flutter_chat_uikit 提供了 `ChatMessagesView`,添加到 `build` 中,传入必填参数 `conversation` 及所需的可选参数即可。详见[聊天界面参数描述](key_function_chat_page.html#创建聊天界面)。
+
+1. 通过 IM SDK 获取一个本地会话。
+
+```dart
+// targetId: 接收方的用户 ID。单聊为对方的用户 ID,群聊为群组 ID,聊天室为聊天 室 ID。
+// type: 单聊为 `EMConversationType.Chat`, 群聊为 `EMConversationType.GroupChat`, 聊天室为 `EMConversationType.ChatRoom`。
+EMConversation conversation = await EMClient.getInstance.chatManager.getConversation(targetId, type: EMConversationType.Chat);
+```
+
+2. 将会话传递给 `ChatMessagesView`。
+
+```dart
+class MessagesPage extends StatefulWidget {
+ const MessagesPage(this.conversation, {super.key});
+
+ final EMConversation conversation;
+
+ @override
+ State createState() => _MessagesPageState();
+}
+
+class _MessagesPageState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.conversation.id),
+ ),
+ body: SafeArea(
+ // UIKit 中的聊天界面。
+ child: ChatMessagesView(
+ conversation: widget.conversation,
+ ),
+ ),
+ );
+ }
+}
+```
+
+
diff --git a/docs/uikit/ios/overview.md b/docs/uikit/ios/overview.md
index 8013c93ef..b3e5e2317 100644
--- a/docs/uikit/ios/overview.md
+++ b/docs/uikit/ios/overview.md
@@ -1 +1,1058 @@
-## UIKit 集成文档
+# EaseIMKit 使用指南
+
+
+
+仍在使用旧版 EaseUI 的用户可参考旧版 EaseUI 的文档,旧版已不再维护。 旧版文档地址:[EaseUI 集成](https://docs-im.easemob.com/im/ios/other/easeui)
+
+## 简介
+
+EaseIMKit 是什么?
+
+EaseIMKit 是基于环信 IM SDK 的一款 UI 组件库,它提供了一些通用的 UI 组件,例如 ‘会话列表’、‘聊天界面’ 和 ‘联系人列表’ 等,开发者可根据实际业务需求通过该组件库快速地搭建自定义 IM 应用。EaseIMKit 中的组件在实现 UI 功能的同时,调用 IM SDK 相应的接口实现 IM 相关逻辑和数据的处理,因而开发者在使用 EaseIMKit 时只需关注自身业务或个性化扩展即可。
+
+EaseIMKit 源码地址
+
+- [EaseIMKit 工程](https://github.com/easemob/easeui_ios/tree/EaseIMKit)
+
+使用 EaseIMKit 环信 IM App 地址:
+
+- [环信 IM](https://github.com/easemob/chat-ios)
+
+## 导入
+
+### 支持系统版本要求
+
+- EaseIMKit 支持 iOS 10.0 及以上系统版本
+- EaseIM 支持 iOS 11.0 及以上系统版本
+
+## 快速集成
+
+### pod 方式集成
+
+```
+pod 'EaseIMKit'
+```
+
+需要在 `Podfile` 文件加上 `use_frameworks!`
+
+:::notice
+EaseIMKit: 对应 HyphenateChat SDK(HyphenateChat 不包含实时音视频,EaseIMKit 不包含音视频,EaseIM 依赖音视频库 EaseCallKit 后实现了音视频功能)
+
+EaseIMKit 中包含了拍照,发语音,发图片,发视频,发位置,发文件的功能,需要使用录音,摄像头,相册,地理位置的权限。需要在您项目的 info.plist 中添加对应权限。
+:::
+
+### 源码集成
+
+- [Github 下载源码](https://github.com/easemob/easeui_ios.git)
+
+执行命令:`git clone https://github.com/easemob/easeui_ios.git`
+
+- 创建 `Podfile` 文件并添加 EaseIMKit 源码依赖
+
+ 1. 项目 `Podfile` 文件 和 `ProjectName.xcodeproj` 文件应在同一目录,如下图所示:
+
+ ![img](@static/images/ios/easeimkit1.png)
+
+ Podfile 文件示例:
+
+ ```
+ platform :ios, '11.0'
+
+ source 'https://github.com/CocoaPods/Specs.git'
+
+ target 'ProjectName' do
+ pod 'EaseIMKit', :path => "../EaseUI/EaseIMKit"
+ pod 'HyphenateChat', '3.8.4'
+ end
+ ```
+
+ 2. EaseIMKit path 路径(如:pod 'EaseIMKit', :path ⇒ “../EaseUI/EaseIMKit”)需指向 EaseIMKit.podspec 文件所在目录,如下图所示:
+
+ ![img](@static/images/ios/easeimkit2.png)
+
+- 项目集成本地 EaseIMKit 源码
+
+ 1. 终端 cd 到 Podfile 文件所在目录,执行 pod install 命令在项目中安装 EaseIMKit 本地源码
+ 2. 执行完成后,则在 Xcode 项目目录 Pods/Development Pods/ 可找到 EaseIMKit 源码,如下图所示:
+
+ ![img](@static/images/ios/easeimkit3.png)
+
+ 3. 可对源码进行符合自己项目目标的自定义修改
+
+- 成为社区贡献者
+
+如果在源码自定义过程中有任何通用自定义都可以给我们 [Github 仓库](https://github.com/easemob/easeui_ios.git) 提交代码成为社区贡献者!
+
+### 初始化
+
+第 1 步:引入相关头文件
+
+```objectivec
+#import
+```
+
+第 2 步:在在工程的 AppDelegate 中的以下方法中调用 EaseIMKitManager 的初始化方法一并初始化环信 SDK。(注: 此方法不需要重复调用)
+
+```objectivec
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ // Override point for customization after application launch.
+ EMOptions *options = [EMOptions optionsWithAppkey:@"您的APPKEY"];
+ [EaseIMKitManager initWithEMOptions:options];
+ //再做登录操作,可准确接收到系统通知
+ return YES;
+}
+```
+
+EaseIMKitManager 主要包含系统通知(好友申请,群邀请/申请)回调,未读总数回调等方法。 用户需要注册自己的类到 EaseIMKitManagerDelegate 才可收到未读总数变化回调。 用户需要添加 EaseIMKitSystemNotiDelegate 代理才可收到系统通知相关回调。
+
+系统通知相关回调接口,系统通知构造成了一个本地会话,每个新通知构造为一条本地消息。 系统通知所构造的会话的 conversationId 为 @“emsystemnotificationid”。
+
+#### 是否需要系统通知
+
+```objectivec
+/*!
+ @method
+ @brief 是否需要系统通知:好友/群 申请等
+ @discussion 默认需要系统通知
+ @result 返回: YES:需要; NO:不需要;
+ */
+- (BOOL)isNeedsSystemNotification;
+```
+
+#### 收到系统通知所展示信息回调接口
+
+```objectivec
+/*!
+ @method
+ @brief 收到请求返回展示信息
+ @param conversationId 会话 ID。
+ * 对于单聊类型,会话 ID 同时也是对方用户的名称。
+ * 对于群聊类型,会话 ID 同时也是对方群组的 ID,并不同于群组的名称。
+ * 对于聊天室类型,会话 ID 同时也是聊天室的 ID,并不同于聊天室的名称。
+
+ @param requestUser 请求方。
+ @param reason 请求原因。
+ @result 返回系统通知所展示信息。
+ */
+- (NSString *)requestDidReceiveShowMessage:(NSString *)conversationId requestUser:(NSString *)requestUser reason:(EaseIMKitCallBackReason)reason;
+```
+
+对于返回值处理:空字符串不产生新 message / nil:使用默认实现 / 非空字符串且长度大于 0,使用该字符串产生新 message
+
+#### 收到系统通知扩展信息回调接口
+
+```objectivec
+/*!
+ @method
+ @brief 收到请求返回扩展信息
+ @param conversationId 会话 ID。
+ * 对于单聊类型,会话 ID 同时也是对方用户的名称。
+ * 对于群聊类型,会话 ID 同时也是对方群组的 ID,并不同于群组的名称。
+ * 对于聊天室类型,会话 ID 同时也是聊天室的 ID,并不同于聊天室的名称。
+
+ @param requestUser 请求方。
+ @param reason 请求原因。
+ @result 返回系统通知携带扩展信息字典。
+ */
+- (NSDictionary *)requestDidReceiveConversationExt:(NSString *)conversationId requestUser:(NSString *)requestUser reason:(EaseIMKitCallBackReason)reason;
+```
+
+#### 未读总数变化回调接口
+
+```objectivec
+/*!
+ @method
+ @brief 会话未读总数变化。
+ @param unreadCount 当前会话列表的总未读数。
+ */
+- (void)conversationsUnreadCountUpdate:(NSInteger)unreadCount;
+```
+
+## 快速搭建
+
+### 聊天会话快速搭建
+
+导入 EaseIMKit 头文件
+
+```objectivec
+#import
+```
+
+EaseIMKit 提供现成的聊天会话 ViewController,可以通过创建 EaseChatViewController 对象实例,嵌入进自己的聊天控制器方式(参考 EaseIM 中 EMChatViewController)实现对 EaseIMKit 聊天会话的集成。 创建聊天会话页实例,需传递用户‘环信 ID’或‘群 ID’ ,会话类型(EMConversationType)以及必须传入聊天视图配置数据模型 EaseChatViewModel 实例。
+
+```objectivec
+EaseChatViewModel *viewModel = [[EaseChatViewModel alloc]init];
+EaseChatViewController *chatController = [EaseChatViewController initWithConversationId:@"custom"
+ conversationType:EMConversationTypeChat
+ chatViewModel:viewModel];
+[self addChildViewController:chatController];
+[self.view addSubview:chatController.view];
+chatController.view.frame = self.view.bounds;
+```
+
+聊天控制器嵌入自己的聊天页后还需传入消息列表 messageList 以供 EaseChatViewController 展示使用
+
+```objectivec
+//isScrollBottom 是否滑动到页面底部
+- (void)loadData:(BOOL)isScrollBottom
+{
+ __weak typeof(self) weakself = self;
+ void (^block)(NSArray *aMessages, EMError *aError) = ^(NSArray *aMessages, EMError *aError) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ //给 EaseChatViewController 传入消息列表messageList
+ //isScrollBottom 是否滑动到页面底部 isInsertBottom 消息数据集是否插入消息列表底部
+ //在传入消息列表之前不需要对列表做任何处理,只需传入列表数据即可,否则会刷新 UI 失败
+ [weakself.chatController refreshTableViewWithData:aMessages isInsertBottom:NO isScrollBottom:isScrollBottom];
+ });
+ };
+ [self.conversation loadMessagesStartFromId:nil count:50 searchDirection:EMMessageSearchDirectionUp completion:block];
+}
+```
+
+### 会话列表快速搭建
+
+导入 EaseIMKit 头文件
+
+```objectivec
+#import
+```
+
+在自己聊天控制器内可嵌入 EaseIMKit 的会话列表页,创建会话列表实例,实例化会话列表必须传入会话列表视图数据配置模型 EaseConversationViewModel 实例。
+
+```objectivec
+EaseConversationViewModel *viewModel = [[EaseConversationViewModel alloc] init];
+
+EaseConversationsViewController *easeConvsVC = [[EaseConversationsViewController alloc] initWithModel:viewModel];
+easeConvsVC.delegate = self;
+[self addChildViewController:easeConvsVC];
+[self.view addSubview:easeConvsVC.view];
+[easeConvsVC.view mas_makeConstraints:^(MASConstraintMaker *make) {
+ make.size.equalTo(self.view);
+}];
+```
+
+### 通讯录快速搭建
+
+导入 EaseIMKit 头文件
+
+```objectivec
+#import
+```
+
+在自己聊天控制器内可嵌入 EaseIMKit 的会话列表页,创建通讯录实例,必须传入通讯录视图数据模型 EaseContactsViewModel 实例以构建通讯录 UI 界面。
+
+```objectivec
+EaseContactsViewModel *model = [[EaseContactsViewModel alloc] init];
+EaseContactsViewController *contactsVC = [[EaseContactsViewController alloc] initWithModel:model];
+//通讯录头部功能区(加好友/群聊/聊天室 入口)
+contactsVC.customHeaderItems = [self items];
+contactsVC.delegate = self;
+[self addChildViewController:contactsVC];
+[self.view addSubview:contactsVC.view];
+[contactsVC.view mas_makeConstraints:^(MASConstraintMaker *make) {
+ make.size.equalTo(self.view);
+}];
+//设置通讯录头部功能区实例(EaseIM APP 有效):
+- (NSArray *)items {
+ EMContactModel *newFriends = [[EMContactModel alloc] init];
+ newFriends.huanXinId = @"newFriend";
+ newFriends.nickname = @"新的好友";
+ newFriends.avatar = [UIImage imageNamed:@"newFriend"];
+ EMContactModel *groups = [[EMContactModel alloc] init];
+ groups.huanXinId = @"groupList";
+ groups.nickname = @"群聊";
+ groups.avatar = [UIImage imageNamed:@"groupchat"];
+ EMContactModel *chatooms = [[EMContactModel alloc] init];
+ chatooms.huanXinId = @"chatroomList";
+ chatooms.nickname = @"聊天室";
+ chatooms.avatar = [UIImage imageNamed:@"chatroom"];
+ return (NSArray *)@[newFriends, groups, chatooms];
+}
+```
+
+## 设置样式
+
+### 聊天会话样式配置
+
+聊天会话可配置参数如下:
+
+```objectivec
+@property (nonatomic, strong) UIColor *chatViewBgColor; //聊天页背景色
+@property (nonatomic, strong) UIColor *chatBarBgColor; //输入区背景色
+@property (nonatomic, strong) EaseExtFuncModel *extFuncModel; //输入区扩展功能数据模型
+@property (nonatomic, strong) UIColor *msgTimeItemBgColor; //时间线背景色
+@property (nonatomic, strong) UIColor *msgTimeItemFontColor; //时间线字体颜色
+@property (nonatomic, strong) UIImage *receiveBubbleBgPicture; //所接收信息的气泡
+@property (nonatomic, strong) UIImage *sendBubbleBgPicture; //所发送信息的气泡
+@property (nonatomic, strong) UIColor *contentFontColor; //文本消息字体颜色
+@property (nonatomic) CGFloat contentFontSize; //文本消息字体大小
+@property (nonatomic) UIEdgeInsets bubbleBgEdgeInset; //消息气泡背景图保护区域
+@property (nonatomic) EaseInputBarStyle inputBarStyle; //输入区类型:(全部功能,无语音,无表情,无表情和语音,纯文本)
+@property (nonatomic) EaseAvatarStyle avatarStyle; //头像风格
+@property (nonatomic) CGFloat avatarCornerRadius; //头像圆角大小 默认:0 (只有头像类型是圆角生效)
+//仅群聊可设置
+@property (nonatomic) EaseAlignmentStyle msgAlignmentStyle; //聊天区域消息排列方式
+```
+
+其中参数:EaseExtFuncModel 输入区扩展功能数据配置模型(聊天会话页相机,相册,音视频等区域)内含可配参数:
+
+```objectivec
+@property (nonatomic, strong) UIColor *iconBgColor;//图标所在 view 背景色
+@property (nonatomic, strong) UIColor *viewBgColor;//视图背景色
+@property (nonatomic, strong) UIColor *fontColor;//字体颜色
+@property (nonatomic, assign) CGFloat fontSize;//字体大小
+@property (nonatomic, assign) CGSize collectionViewSize;//视图尺寸
+```
+
+其中参数:inputBarStyle(输入区)包含五种类型:
+
+```objectivec
+typedef NS_ENUM(NSInteger, EaseInputBarStyle) {
+ EaseInputBarStyleAll = 1, //全部功能
+ EaseInputBarStyleNoAudio, //无语音
+ EaseInputBarStyleNoEmoji, //无表情
+ EaseInputBarStyleNoAudioAndEmoji, //无表情和语音
+ EaseInputBarStyleOnlyText, //纯文本
+};
+```
+
+其中参数:EaseAlignmentStyle (消息排列方式,仅群聊可生效)包含两种类型
+
+```objectivec
+typedef enum {
+ EaseAlignmentNormal = 1, //左右排列
+ EaseAlignmentlLeft, //居左排列
+} EaseAlignmentStyle;
+```
+
+实例化的聊天控制器可通过重置视图 UI 配置模型刷新页面
+
+```objectivec
+//重置聊天控制器
+- (void)resetChatVCWithViewModel:(EaseChatViewModel *)viewModel;
+```
+
+聊天页背景色,输入区颜色配置示例:
+
+![背景色,输入区颜色](@static/images/ios/easeimkit4.png)
+
+聊天会话输入区类型参数配置示例:
+
+![全部功能,语音不可用,表情不可用,语音和表情不可用,纯文本](@static/images/ios/easeimkit5.png)
+
+输入区扩展功能参数配置示例:
+
+![输入区扩展](@static/images/ios/easeimkit6.jpeg)
+
+聊天会话群聊消息同左排列,时间线背景色,时间字体颜色配置示例:
+
+![群聊消息同左排列,时间线背景色,时间字体颜色](@static/images/ios/easeimkit7.jpeg)
+
+### 会话列表样式配置
+
+会话列表可配置参数如下:
+
+```objectivec
+@property (nonatomic) EaseAvatarStyle avatarType; // 头像样式
+@property (nonatomic, strong) UIImage *defaultAvatarImage; // 默认头像
+@property (nonatomic) CGSize avatarSize; // 头像尺寸
+@property (nonatomic) UIEdgeInsets avatarEdgeInsets; // 头像位置
+@property (nonatomic, strong) UIFont *nameLabelFont; // 昵称字体
+@property (nonatomic, strong) UIColor *nameLabelColor; // 昵称颜色
+@property (nonatomic) UIEdgeInsets nameLabelEdgeInsets; // 昵称位置
+@property (nonatomic, strong) UIFont *detailLabelFont; // 详情字体
+@property (nonatomic, strong) UIColor *detailLabelColor; // 详情字色
+@property (nonatomic) UIEdgeInsets detailLabelEdgeInsets; // 详情位置
+@property (nonatomic, strong) UIFont *timeLabelFont; // 时间字体
+@property (nonatomic, strong) UIColor *timeLabelColor; // 时间字色
+@property (nonatomic) UIEdgeInsets timeLabelEdgeInsets; // 时间位置
+@property (nonatomic) EMUnReadCountViewPosition badgeLabelPosition; // 未读数显示风格
+@property (nonatomic, strong) UIFont *badgeLabelFont; // 未读数字体
+@property (nonatomic, strong) UIColor *badgeLabelTitleColor; // 未读数字色
+@property (nonatomic, strong) UIColor *badgeLabelBgColor; // 未读数背景色
+@property (nonatomic) CGFloat badgeLabelHeight; // 未读数角标高度
+@property (nonatomic) CGVector badgeLabelCenterVector; // 未读数中心位置偏移
+@property (nonatomic) int badgeMaxNum; // 未读数显示上限, 超过上限后会显示 xx+
+```
+
+会话列表以及联系人列表共用其父类可配置参数如下:
+
+```objectivec
+@property (nonatomic) BOOL canRefresh; // 是否可下拉刷新
+@property (nonatomic, strong) UIView *bgView; // tableView 背景图
+@property (nonatomic, strong) UIColor *cellBgColor; // UITableViewCell 背景色
+@property (nonatomic) UIEdgeInsets cellSeparatorInset; // UITableViewCell 下划线位置
+@property (nonatomic, strong) UIColor *cellSeparatorColor; // UITableViewCell 下划线颜色
+```
+
+### 通讯录样式配置
+
+通讯录可配置参数如下:
+
+```objectivec
+@property (nonatomic) EaseAvatarStyle avatarType; // 头像样式
+@property (nonatomic, strong) UIImage *defaultAvatarImage; // 默认头像
+@property (nonatomic) CGSize avatarSize; // 头像尺寸
+@property (nonatomic) UIEdgeInsets avatarEdgeInsets; // 头像位置
+@property (nonatomic, strong) UIFont *nameLabelFont; // 昵称字体
+@property (nonatomic, strong) UIColor *nameLabelColor; // 昵称颜色
+@property (nonatomic) UIEdgeInsets nameLabelEdgeInsets; // 昵称位置
+@property (nonatomic, strong) UIFont *sectionTitleFont; // section title 字体
+@property (nonatomic, strong) UIColor *sectionTitleColor; // section title 颜色
+@property (nonatomic, strong) UIColor *sectionTitleBgColor; // section title 背景
+@property (nonatomic) UIEdgeInsets sectionTitleEdgeInsets; // section title 位置
+// section title label 高度 (section title view = sectionTitleLabelHeight + sectionTitleEdgeInsets.top + sectionTitleEdgeInsets.bottom)
+@property (nonatomic) CGFloat sectionTitleLabelHeight;
+```
+
+以及通讯录和会话列表共用的参数配置
+
+```objectivec
+@property (nonatomic) BOOL canRefresh; // 是否可下拉刷新
+@property (nonatomic, strong) UIView *bgView; // tableView 背景图
+@property (nonatomic, strong) UIColor *cellBgColor; // UITableViewCell 背景色
+@property (nonatomic) UIEdgeInsets cellSeparatorInset; // UITableViewCell 下划线位置
+@property (nonatomic, strong) UIColor *cellSeparatorColor; // UITableViewCell 下划线颜色
+```
+
+通讯录添加头部功能区:新的好友,群聊,聊天室示意图:
+
+![头部功能区:新的好友,群聊,聊天室以及联系人列表](@static/images/ios/easeimkit8.png)
+
+## 自定义功能扩展
+
+### 聊天会话自定义功能扩展
+
+实例化 EaseChatViewController 之后,可选择实现 EaseChatViewControllerDelegate 协议(聊天控制器回调代理),接收 EaseChatViewController 的回调并做进一步的自定义实现。
+
+EaseChatViewControllerDelegate
+
+#### 下拉加载更多消息回调
+
+下拉加载更多消息回调(可得到当前第一条消息 ID 作为下次加载更多消息的参考 ID;当前消息列表)
+
+```objectivec
+/**
+ * 下拉加载更多消息回调
+ *
+ * @param firstMessageId 第一条消息 ID
+ * @param messageList 当前消息列表
+ */
+- (void)loadMoreMessageData:(NSString *)firstMessageId currentMessageList:(NSArray *)messageList;
+```
+
+#### 自定义 cell
+
+通过实现聊天控制回调获取自定义消息 cell,根据 messageModel,用户自己判断是否显示自定义消息 cell。如果返回 nil 会显示默认;如果返回 cell 会显示用户自定义消息 cell。
+
+```objectivec
+/*!
+ @method
+ @brief 获取消息自定义 cell
+ @discussion 用户根据 messageModel 判断是否显示自定义 cell。返回 nil 显示默认 cell,否则显示用户自定义 cell
+ @param tableView 当前消息视图的 tableView
+ @param messageModel 消息的数据模型
+ @result 返回用户自定义 cell
+ */
+- (UITableViewCell *)cellForItem:(UITableView *)tableView messageModel:(EaseMessageModel *)messageModel;
+```
+
+具体创建自定义 Cell 的示例:
+
+```objectivec
+//自定义通话记录cell
+- (UITableViewCell *)cellForItem:(UITableView *)tableView messageModel:(EaseMessageModel *)messageModel
+{
+ //当消息是单聊音视频通话消息时,EaseIM 自定义通话记录 cell
+ if (messageModel.type == EMMessageTypePictMixText) {
+ EMMessageCell *cell = [[EMMessageCell alloc]initWithDirection:messageModel.direction type:messageModel.type];
+ cell.model = messageModel;
+ cell.delegate = self;
+ return cell;
+ }
+ return nil;
+}
+```
+
+通过自定义 cell 展示单聊音视频通话记录的效果图:
+
+![自定义 cell 展示单聊音视频通话记录](@static/images/ios/easeimkit9.png)
+
+#### 选中消息的回调
+
+选中消息的回调(EaseIMKit 没有对于自定义 cell 的选中事件回调,需用户自定义实现选中响应)
+
+```objectivec
+/*!
+ @method
+ @brief 消息点击事件
+ @discussion 用户根据 messageModel 判断,是否自定义处理消息选中时间。返回 NO 为自定义处理,返回 YES 为默认处理
+ @param message 当前点击的消息
+ @param userData 当前点击的消息携带的用户资料
+ @result 是否采用默认事件处理
+*/
+- (BOOL)didSelectMessageItem:(EMMessage*)message userData:(id)userData;
+```
+
+EaseIMKit 选中是消息气泡,自定义 cell 的点击事件需自定义实现,例:EaseIM 单聊通话记录 cell 点击事件再次发起通话
+
+```objectivec
+- (void)messageCellDidSelected:(EMMessageCell *)aCell
+{
+ //使用‘通知’的方式发起通话,其中所定义的宏仅在 EaseIM APP 中生效
+ NSString *callType = nil;
+ NSDictionary *dic = aCell.model.message.ext;
+ if ([[dic objectForKey:EMCOMMUNICATE_TYPE] isEqualToString:EMCOMMUNICATE_TYPE_VOICE])
+ callType = EMCOMMUNICATE_TYPE_VOICE;
+ if ([[dic objectForKey:EMCOMMUNICATE_TYPE] isEqualToString:EMCOMMUNICATE_TYPE_VIDEO])
+ callType = EMCOMMUNICATE_TYPE_VIDEO;
+ if ([callType isEqualToString:EMCOMMUNICATE_TYPE_VOICE])
+ [[NSNotificationCenter defaultCenter] postNotificationName:CALL_MAKE1V1 object:@{CALL_CHATTER:aCell.model.message.conversationId, CALL_TYPE:@(EMCallTypeVoice)}];
+ if ([callType isEqualToString:EMCOMMUNICATE_TYPE_VIDEO])
+ [[NSNotificationCenter defaultCenter] postNotificationName:CALL_MAKE1V1 object:@{CALL_CHATTER:aCell.model.message.conversationId, CALL_TYPE:@(EMCallTypeVideo)}];
+}
+```
+
+#### 用户资料回调
+
+用户资料回调(头像昵称等)
+
+```objectivec
+/*!
+ @method
+ @brief 返回用户资料
+ @discussion 用户根据 huanxinID 在自己的用户体系中匹配对应的用户资料,并返回相应的信息,否则默认实现
+ @param huanxinID 环信 ID
+ @result 返回与当前环信 ID 关联的用户资料
+ */
+- (id)userData:(NSString*)huanxinID;
+```
+
+#### 用户选中头像的回调
+
+```objectivec
+/*!
+ @method
+ @brief 点击消息头像
+ @discussion 获取用户点击头像回调
+ @param userData 当前点击的头像所指向的用户资料
+ */
+
+- (void)avatarDidSelected:(id)userData;
+```
+
+获取用户选中头像回调的样例:
+
+```objectivec
+//头像点击
+- (void)avatarDidSelected:(id)userData
+{
+ //EMPersonalDataViewController 用户自定义的个人信息视图
+ //样例逻辑是选中消息头像后,进入该消息发送者的个人信息页
+ if (userData && userData.easeId) {
+ EMPersonalDataViewController *controller = [[EMPersonalDataViewController alloc]initWithNickName:userData.easeId];
+ [self.navigationController pushViewController:controller animated:YES];
+ }
+}
+```
+
+#### 用户长按头像的回调
+
+```objectivec
+/*!
+ @method
+ @brief 点击消息头像
+ @discussion 获取用户长按头像回调
+ @param userData 当前长按的头像所指向的用户资料
+ */
+
+- (void)avatarDidLongPress:(id)userData;
+```
+
+#### 群通知回执详情
+
+```objectivec
+/*!
+ @method
+ @brief 群通知回执详情
+ @discussion 获取用户点击群通知的回调(仅在群聊中并且是点击用户群主有效)
+ @param message 当前群通知消息
+ @param groupId 当前消息所属群 ID
+ */
+
+- (void)groupMessageReadReceiptDetail:(EMMessage*)message groupId:(NSString*)groupId;
+```
+
+获取用户点击群通知回执详情的样例:
+
+```objectivec
+//群通知阅读回执详情
+- (void)groupMessageReadReceiptDetail:(EMMessage *)message groupId:(NSString *)groupId
+{
+ //EMReadReceiptMsgViewController用户自定义群通知阅读回执详情页(在 EaseIM APP 中有效)
+ EMReadReceiptMsgViewController *readReceiptControl = [[EMReadReceiptMsgViewController alloc] initWithMessage:message groupId:groupId];
+ readReceiptControl.modalPresentationStyle = 0;
+ [self presentViewController:readReceiptControl animated:YES completion:nil];
+}
+```
+
+#### 输入区回调
+
+当前会话输入扩展区数据模型组(UI 配置可在聊天视图配置数据模型中设置)
+
+```objectivec
+/*!
+ @method
+ @brief 当前会话输入扩展区数据模型组
+ @param defaultInputBarItems 默认功能数据模型组 (默认有序:相册,相机,位置,文件)
+ @param conversationType 当前会话类型:单聊,群聊,聊天室
+ @result 返回一组输入区扩展功能
+ */
+
+- (NSMutableArray*)inputBarExtMenuItemArray:
+ (NSMutableArray*)defaultInputBarItems
+ conversationType:(EMConversationType)conversationType;
+```
+
+当前会话输入扩展区数据模型组回调示例(EaseIM APP 有效):
+
+```objectivec
+- (NSMutableArray *)inputBarExtMenuItemArray:(NSMutableArray *)defaultInputBarItems conversationType:(EMConversationType)conversationType
+{
+NSMutableArray *menuArray = [[NSMutableArray alloc]init];
+//相册
+[menuArray addObject:[defaultInputBarItems objectAtIndex:0]];
+//相机
+[menuArray addObject:[defaultInputBarItems objectAtIndex:1]];
+//音视频
+__weak typeof(self) weakself = self;
+if (conversationType != EMConversationTypeChatRoom) {
+ EaseExtMenuModel *rtcMenu = [[EaseExtMenuModel alloc]initWithData:[UIImage imageNamed:@"video_conf"] funcDesc:@"音视频" handle:^(NSString * _Nonnull itemDesc, BOOL isExecuted) {
+ if (isExecuted) {
+ [weakself chatSealRtcAction];
+ }
+ }];
+ [menuArray addObject:rtcMenu];
+}
+//位置
+[menuArray addObject:[defaultInputBarItems objectAtIndex:2]];
+//文件
+[menuArray addObject:[defaultInputBarItems objectAtIndex:3]];
+//群组回执
+if (conversationType == EMConversationTypeGroupChat) {
+ if ([[EMClient.sharedClient.groupManager getGroupSpecificationFromServerWithId:_conversation.conversationId error:nil].owner isEqualToString:EMClient.sharedClient.currentUsername]) {
+ EaseExtMenuModel *groupReadReceiptExtModel = [[EaseExtMenuModel alloc]initWithData:[UIImage imageNamed:@"pin_readReceipt"] funcDesc:@"群组回执" handle:^(NSString * _Nonnull itemDesc, BOOL isExecuted) {
+ //群组回执发送消息页
+ [weakself groupReadReceiptAction];
+ }];
+ [menuArray addObject:groupReadReceiptExtModel];
+ }
+}
+return menuArray;
+}
+```
+
+#### 键盘输入变化回调
+
+```objectivec
+/*!
+ @method
+ @brief 输入区键盘输入变化回调 例:@群成员
+ @result 返回键入内容是否有效
+ */
+- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
+```
+
+输入区键盘回调示例(EaseIM APP 有效):
+
+```objectivec
+//@群成员
+- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
+{
+ if (self.conversation.type == EMConversationTypeGroupChat) {
+ if ([text isEqualToString:@"@"]) {
+ [self _willInputAt:textView];
+ } else if ([text isEqualToString:@""]) {
+ __block BOOL isAt = NO;
+ [textView.attributedText enumerateAttributesInRange:NSMakeRange(0, textView.text.length) options:0 usingBlock:^(NSDictionary * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
+ NSString *atUser = attrs[@"AtInfo"];
+ if (atUser) {
+ if (textView.selectedRange.location == range.location + range.length) {
+ isAt = YES;
+ NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithAttributedString:textView.attributedText];
+ [result deleteCharactersInRange:range];
+ textView.attributedText = result;
+ if ([atUser isEqualToString:@"All"]) {
+ [self.chatController removeAtAll];
+ } else {
+ [self.chatController removeAtUser:atUser];
+ }
+ *stop = YES;
+ }
+ }
+ }];
+ return !isAt;
+ }
+ }
+ return YES;
+}
+```
+
+#### 输入框选中回调
+
+```objectivec
+/**
+ * 输入区选中范围变化回调 例:@群成员
+ */
+- (void)textViewDidChangeSelection:(UITextView *)textView;
+```
+
+输入区选中范围变化回调示例(EaseIM APP 有效):
+
+```objectivec
+- (void)textViewDidChangeSelection:(UITextView *)textView
+{
+ [textView.attributedText enumerateAttributesInRange:NSMakeRange(0, textView.text.length) options:0 usingBlock:^(NSDictionary * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
+ if (attrs[@"AtInfo"]) {
+ NSUInteger min = textView.selectedRange.location;
+ NSUInteger max = textView.selectedRange.location + textView.selectedRange.length;
+ if (min > range.location && min <= range.location + range.length) {
+ NSUInteger location = range.location + range.length;
+ NSUInteger length = 0;
+ if (textView.selectedRange.location + textView.selectedRange.length > location) {
+ length = textView.selectedRange.location + textView.selectedRange.length - location;
+ }
+ textView.selectedRange = NSMakeRange(location, length);
+ *stop = YES;
+ } else if (max > range.location && max <= range.location + range.length) {
+ NSUInteger location = min;
+ NSUInteger length = textView.selectedRange.length - (max - range.location - range.length);
+ textView.selectedRange = NSMakeRange(location, length);
+ *stop = YES;
+ }
+ }
+ }];
+}
+```
+
+#### 对方正在输入状态回调
+
+对方正在输入状态回调(单聊有效)
+
+```objectivec
+/**
+ 对方正在输入
+*/
+- (void)beginTyping;
+```
+
+对方结束输入回调(单聊有效)
+
+```objectivec
+/**
+ 对方结束输入
+*/
+- (void)endTyping;
+```
+
+#### 消息长按事件回调
+
+默认消息 cell 长按回调
+
+```objectivec
+/*!
+ @method
+ @brief 默认消息 cell 长按回调
+ @param defaultLongPressItems 默认长按扩展区功能数据模型组(默认共有:复制,删除,撤回发送消息时 间距当前时间小于 2 分钟)
+ @param message 当前长按的消息
+ @result 返回默认消息长按扩展功能组
+ */
+- (NSMutableArray*)messageLongPressExtMenuItemArray:(NSMutableArray*)defaultLongPressItems message:(EMMessage*)message;
+```
+
+默认消息 cell 长按回调示例(EaseIM APP 有效):
+
+```objectivec
+//添加转发消息
+- (NSMutableArray *)messageLongPressExtMenuItemArray:(NSMutableArray *)defaultLongPressItems message:(EMMessage *)message
+{
+ NSMutableArray *menuArray = [[NSMutableArray alloc]initWithArray:defaultLongPressItems];
+ //转发
+ __weak typeof(self) weakself = self;
+ if (message.body.type == EMMessageBodyTypeText || message.body.type == EMMessageBodyTypeImage || message.body.type == EMMessageBodyTypeLocation || message.body.type == EMMessageBodyTypeVideo) {
+ EaseExtMenuModel *forwardMenu = [[EaseExtMenuModel alloc]initWithData:[UIImage imageNamed:@"forward"] funcDesc:@"转发" handle:^(NSString * _Nonnull itemDesc, BOOL isExecuted) {
+ //用户可自定义转发 CallBack
+ if (isExecuted) {
+ [weakself forwardMenuItemAction:message];
+ }
+ }];
+ [menuArray addObject:forwardMenu];
+ }
+ return menuArray;
+}
+```
+
+#### 自定义 cell 长按回调
+
+用户自定义消息 cell 长按事件回调
+
+```objectivec
+/*!
+ @method
+ @brief 当前所长按的 自定义 cell 的扩展区数据模型组
+ @param defaultLongPressItems 默认长按扩展区功能数据模型组。默认共有:复制,删除,撤回(发送消息时 间距当前时间小于 2 分钟)
+ @param customCell 当前长按的自定义 cell
+ @result 返回默认消息长按扩展功能组
+ */
+/**
+ *
+ *
+ * @param defaultLongPressItems 默认长按扩展区功能数据模型组 默认共有:复制,删除,撤回(发送消息时 间距当前时间小于 2 分钟))
+ * @param customCell 当前长按的自定义 cell
+ */
+- (NSMutableArray*)customCellLongPressExtMenuItemArray:(NSMutableArray*)defaultLongPressItems customCell:(UITableViewCell*)customCell;
+```
+
+### 会话列表自定义功能扩展
+
+实例化 `EaseConversationsViewController` 之后,可选择实现 `EaseConversationsViewControllerDelegate` 协议(会话列表回调代理),接收 `EaseConversationsViewController` 的回调并做进一步的自定义实现。
+
+`EaseConversationsViewControllerDelegate`
+
+#### 自定义 cell
+
+通过实现会话列表回调获取自定义消息 cell 如果返回 nil 会显示默认;如果返回 cell 则会显示用户自定义 cell。
+
+```objectivec
+/*!
+ @method
+ @brief 获取消息自定义 cell
+ @discussion 返回 nil 显示默认 cell,否则显示用户自定义 cell
+ @param tableView 当前消息视图的 tableView
+ @param indexPath 当前所要展示 cell 的 indexPath
+ @result 返回用户自定义cell
+ */
+- (UITableViewCell *)easeTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
+```
+
+#### 会话列表 cell 选中回调
+
+```objectivec
+/*!
+ @method
+ @brief 会话列表 cell 选中回调
+ @param tableView 当前消息视图的 tableView
+ @param indexPath 当前所要展示 cell 的 indexPath
+ */
+- (void)easeTableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
+```
+
+会话列表 cell 选中回调示例(EaseIM APP 有效):
+
+```objectivec
+- (void)easeTableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ EaseConversationCell *cell = (EaseConversationCell*)[tableView cellForRowAtIndexPath:indexPath];
+ //系统通知
+ if ([cell.model.easeId isEqualToString:@"emsystemnotificationid"]) {
+ //此实例仅为 EaseIM APP 展示系统通知
+ EMNotificationViewController *controller = [[EMNotificationViewController alloc] initWithStyle:UITableViewStylePlain];
+ [self.navigationController pushViewController:controller animated:YES];
+ return;
+ }
+ //跳转至聊天页
+ [[NSNotificationCenter defaultCenter] postNotificationName:CHAT_PUSHVIEWCONTROLLER object:cell.model];
+}
+```
+
+#### 会话列表用户资料回调
+
+```objectivec
+/*!
+ @method
+ @brief 会话列表用户资料回调
+ @discussion 可根据 conversationId 或 type 返回对应的用户资料数据集
+ @param conversationId 当前会话列表 cell 所拥有的会话 ID
+ @param type 当前会话列表 cell 所拥有的会话类型
+ */
+- (id)easeUserDelegateAtConversationId:(NSString *)conversationId
+ conversationType:(EMConversationType)type;
+```
+
+会话列表用户资料回调实例(EaseIM APP 有效)
+
+```objectivec
+- (id)easeUserDelegateAtConversationId:(NSString *)conversationId conversationType:(EMConversationType)type
+{
+ //EMConversationUserDataModel 为自定义用户资料数据模型,实现 EaseUserDelegate 接口返回参数
+ //@property (nonatomic, copy, readonly) NSString *easeId; // 环信 ID
+ //@property (nonatomic, copy, readonly) UIImage *defaultAvatar; // 默认头像显示
+ EMConversationUserDataModel *userData = [[EMConversationUserDataModel alloc]initWithEaseId:conversationId conversationType:type];
+ return userData;
+}
+```
+
+#### 会话列表 cell 侧滑项回调
+
+```objectivec
+/*!
+ @method
+ @brief 会话列表 cell 侧滑项回调
+ @param tableView 当前消息视图的 tableView
+ @param indexPath 当前所要侧滑 cell 的 indexPath
+ @param actions 返回侧滑项集合
+ */
+- (NSArray *)easeTableView:(UITableView *)tableView
+ trailingSwipeActionsForRowAtIndexPath:(NSIndexPath *)indexPath
+ actions:(NSArray *)actions;
+```
+
+会话列表 cell 侧滑项回调示例(EaseIM APP 有效)
+
+```objectivec
+- (NSArray *)easeTableView:(UITableView *)tableView trailingSwipeActionsForRowAtIndexPath:(NSIndexPath *)indexPath actions:(NSArray *)actions
+{
+ NSMutableArray *array = [[NSMutableArray alloc]init];
+ __weak typeof(self) weakself = self;
+ UIContextualAction *deleteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal
+ title:@"删除"
+ handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL))
+ {
+ //删除操作
+ }];
+ deleteAction.backgroundColor = [UIColor redColor];
+ [array addObject:deleteAction];
+ //返回的 actions 有序:删除,置顶
+ [array addObject:actions[1]];
+ return [array copy];
+}
+```
+
+### 通讯录自定义功能扩展
+
+#### 获取用户联系人资料
+
+获取用户自己的联系人列表填充到 EaseIMKit 通讯录中
+
+```objectivec
+- (void)setContacts:(NSArray * _Nonnull)contacts;
+```
+
+实例化 EaseContactsViewController 之后,可选择实现 EaseContactsViewControllerDelegate 协议(通讯录代理),接收 EaseContactsViewController 的回调并做进一步的自定义实现。
+
+EaseConversationsViewControllerDelegate
+
+#### 即将刷新通讯录填充数据
+
+```objectivec
+- (void)willBeginRefresh;
+```
+
+即将刷新通讯录填充数据示例(EaseIM APP 有效):
+
+```objectivec
+- (void)willBeginRefresh {
+ //从服务器获取当前登录账户的联系人列表
+ [EMClient.sharedClient.contactManager getContactsFromServerWithCompletion:^(NSArray *aList, EMError *aError) {
+ if (!aError) {
+ self->_contacts = [aList mutableCopy];
+ NSMutableArray *contacts = [NSMutableArray array];
+ for (NSString *username in aList) {
+ EMContactModel *model = [[EMContactModel alloc] init];
+ model.huanXinId = username;
+ [contacts addObject:model];
+ }
+ //填充联系人列表集合到 EaseIMKit 通讯录实例中
+ [self->_contactsVC setContacts:contacts];
+ }
+ [self->_contactsVC endRefresh];
+ }];
+}
+```
+
+#### 通讯录自定义 cell
+
+```objectivec
+/*!
+@method
+@brief 获取通讯录自定义 cell
+@discussion 返回 nil 显示默认 cell,否则显示用户自定义 cell
+@param tableView 当前消息视图的 tableView
+@param contact 当前所要展示 cell 所拥有的数据模型
+@result 返回用户自定义 cell
+*/
+- (UITableViewCell *)easeTableView:(UITableView *)tableView cellForRowAtContactModel:(EaseContactModel *) contact;
+```
+
+#### 通讯录 cell 条目选中回调
+
+```objectivec
+/*!
+@method
+@brief 通讯录 cell 条目选中回调
+@param tableView 当前消息视图的 tableView
+@param contact 当前所选中 cell 所拥有的数据模型
+@result 返回用户自定义 cell
+*/
+- (void)easeTableView:(UITableView *)tableView didSelectRowAtContactModel:(EaseContactModel *) contact;
+```
+
+通讯录 cell 条目选中回调示例:
+
+```objectivec
+- (void)easeTableView:(UITableView *)tableView didSelectRowAtContactModel:(EaseContactModel *)contact {
+ //跳转加好友页
+ if ([contact.easeId isEqualToString:@"newFriend"]) {
+ EMInviteFriendViewController *controller = [[EMInviteFriendViewController alloc] init];
+ [self.navigationController pushViewController:controller animated:YES];
+ return;
+ }
+ //跳转群组列表页
+ if ([contact.easeId isEqualToString:@"groupList"]) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:GROUP_LIST_PUSHVIEWCONTROLLER object:@{NOTIF_NAVICONTROLLER:self.navigationController}];
+ return;
+ }
+ //跳转聊天室列表页
+ if ([contact.easeId isEqualToString:@"chatroomList"]) {
+ [[NSNotificationCenter defaultCenter] postNotificationName:CHATROOM_LIST_PUSHVIEWCONTROLLER object:@{NOTIF_NAVICONTROLLER:self.navigationController}];
+ return;
+ }
+ //跳转好友个人资料页
+ [self personData:contact.easeId];
+}
+```
+
+#### 通讯录 cell 侧滑回调
+
+```objectivec
+/*!
+ @method
+ @brief 会话列表 cell 侧滑项回调
+ @param tableView 当前消息视图的 tableView
+ @param contact 当前所侧滑 cell 拥有的联系人数据
+ @param actions 返回侧滑项集合
+ */
+- (NSArray *)easeTableView:(UITableView *)tableView
+ trailingSwipeActionsForRowAtContactModel:(EaseContactModel *) contact
+ actions:(NSArray * __nullable)actions;
+```
+
+通讯录 cell 侧滑回调示例:
+
+```objectivec
+- (NSArray *)easeTableView:(UITableView *)tableView trailingSwipeActionsForRowAtContactModel:(EaseContactModel *)contact actions:(NSArray *)actions
+{
+ //通讯录头部非联系人列表禁止侧滑
+ if ([contact.easeId isEqualToString:@"newFriend"] || [contact.easeId isEqualToString:@"groupList"] || [contact.easeId isEqualToString:@"chatroomList"]) {
+ return nil;
+ }
+ __weak typeof(self) weakself = self;
+ UIContextualAction *deleteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
+ title:@"删除"
+ handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL))
+ {
+ //删除联系人操作
+ }];
+ return @[deleteAction];
+}
+```
diff --git a/docs/uikit/react-native/key_function_chat_page.md b/docs/uikit/react-native/key_function_chat_page.md
new file mode 100644
index 000000000..730aa11af
--- /dev/null
+++ b/docs/uikit/react-native/key_function_chat_page.md
@@ -0,0 +1,279 @@
+# 聊天页面
+
+聊天组件提供了丰富的功能,支持文本、表情、图片、语音、文件等多种类型消息的输入,支持显示消息列表、自定义头像、自定义消息状态、自定义消息气泡,并可以修改消息状态。
+
+## 集成聊天页面
+
+在项目中集成聊天页面组件 `ChatFragment`,并传入相应的参数即可使用。
+
+| 参数 | 是否必需 | 描述 |
+| :------------- | :-----| :----- |
+| `chatId` | 是 | 会话 ID。 |
+| `chatType` | 是 | - `0`:单聊;
- `1`:群聊;
- `2`:聊天室。 |
+| `propsRef` | 否 | 设置聊天组件控制器。 |
+| `screenParams` | 否 | 设置聊天组件的参数。 |
+| `messageBubbleList` | 否 | 设置自定义消息气泡组件。 |
+| `onUpdateReadCount` | 否 | 未读消息计数更新时发生。 |
+| `onClickMessageBubble` | 否 | 单击消息气泡通知时发生。 |
+| `onLongPressMessageBubble` | 否 | 按住消息气泡时发生。 |
+| `onClickInputMoreButton` | 否 | 按下**更多**按钮时发生。 |
+| `onPressInInputVoiceButton` | 否 | 按下语音按钮时发生。 |
+| `onPressOutInputVoiceButton` | 否 | 释放语音按钮时发生。 |
+| `onSendMessage` | 否 | 消息开始发送时发生。 |
+| `onSendMessageEnd` | 否 | 消息发送完成时发生。 |
+| `onVoiceRecordEnd` | 否 | 语音消息录制完成时发生。 |
+
+```typescript
+import * as React from "react";
+import { ChatFragment, ScreenContainer } from "react-native-chat-uikit";
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx"; // 会话 ID,String 类型。
+ const chatType = 0;
+ return (
+
+
+
+ );
+}
+```
+效果如下图所示:
+
+
+
+
+聊天页面相关的方法如下表所示:
+
+| 方法 | 描述 |
+| :------- | :----- |
+| `sendTextMessage` | 发送文本消息。 |
+| `sendImageMessage` | 发送图片消息。 |
+| `sendVoiceMessage` | 发送语音消息。 |
+| `sendCustomMessage` | 发送自定义消息。 |
+| `sendFileMessage` | 发送文件消息。 |
+| `sendVideoMessage` | 发送视频消息。 |
+| `sendLocationMessage` | 发送位置消息。 |
+| `loadHistoryMessage` | 加载历史消息。 |
+| `deleteLocalMessage` | 删除本地消息。 |
+| `resendMessage` | 重发发送失败的消息。 |
+| `downloadAttachment` | 下载附件。 |
+
+例如,你可以自行录制语音,然后调用 `sendVoiceMessage` 方法发送。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ chatRef.current.sendVoiceMessage(params);
+ }}
+ />
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+## 自定义实现
+
+### 自定义聊天气泡
+
+当默认的聊天气泡无法满足需求时,你可以自行设计聊天气泡的样式。
+
+`MessageBubbleList` 为自定义聊天气泡列表组件,可以修改任何气泡内容,例如:头像、文字、图片、语音样式、消息状态等。该组件提供子组件实现文本、图片、音视频等消息的样式:
+
+- `TextMessageItem`:自定义文本消息样式。
+- `LocationMessageItem`:自定义位置消息样式。
+- `CustomMessageItem`:自定义自定义消息样式。
+- `ImageMessageItem`:自定义图片消息样式。
+- `VideoMessageItem`:自定义视频消息样式。
+- `VoiceMessageItem`:自定义语音消息样式。
+- `FileMessageItem`:自定义文件消息样式。
+
+```typescript
+import type { MessageBubbleListProps } from "../fragments/MessageBubbleList";
+import MessageBubbleList from "../fragments/MessageBubbleList";
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+
+
+### 自定义背景色
+
+你可以设置 `MessageBubbleListPropsP` 中的 `backgroundColor` 自定义背景色。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+### 自定义时间标签显示和隐藏
+
+你可以设置 `messageBubbleList` 中的 `showTimeLabel` 自定义时间标签显示和隐藏。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+### 自定义点击消息气泡回调
+
+你可以设置 `ChatFragment` 中的 `onClickMessageBubble` 自定义点击消息气泡回调,例如:播放语音、预览图片等。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ // TODO:对于语音消息,进行播放;对于图片消息,进行预览。
+ }}
+ />
+
+ );
+}
+```
+
+### 自定义长按消息气泡回调
+
+你可以设置 `ChatFragment` 中的 `onLongPressMessageBubble` 自定义长按消息气泡回调,例如,可以显示不同右键菜单。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ // TODO:显示右键菜单。例如,消息转发、消息删除和消息重发等。
+ }}
+ />
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+### 自定义发送消息前回调
+
+你可以设置 `ChatFragment` 中的 `onSendMessage` 自定义发送消息前回调。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ // TODO: 更新消息。
+ }}
+ />
+
+ );
+}
+```
+
+### 自定义发送消息完成回调
+
+你可以设置 `ChatFragment` 中的 `onSendMessageEnd` 自定义发送消息完成,例如,更新消息发送状态。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ // TODO:更新消息发送状态,即发送成功或失败。
+ }}
+ />
+
+ );
+}
+```
+
+
+
+
+
diff --git a/docs/uikit/react-native/key_function_conversation_list.md b/docs/uikit/react-native/key_function_conversation_list.md
new file mode 100644
index 000000000..a6e2e57b6
--- /dev/null
+++ b/docs/uikit/react-native/key_function_conversation_list.md
@@ -0,0 +1,176 @@
+# 会话列表页面
+
+会话列表组件支持创建、更新、删除会话、会话样式修改、会话状态改变(如未读消息数)等。
+
+## 集成会话列表页面
+
+在项目中集成会话列表页面组件 `ConversationListFragment`,并传入相应的参数即可使用。
+
+| 参数 | 是否必需 | 描述 |
+| :------------- | :-----| :----- |
+| `chatId ` | 是 | 会话 ID。 |
+| `chatType` | 是 | 会话类型。
- `0`:单聊;
- `1`:群聊;
- `2`:聊天室。 |
+| `propsRef` | 否 | 设置会话列表控制器。通过会话列表控制器可以实现以下会话操作:
- 创建会话;
- 更新会话,如更新会话排序、会话样式和消息未读数;
- 删除会话;
- `updateRead`:将会话设置为已读;
- `updateExtension`:设置会话自定义字段。|
+| `onLongPress` | 否 | 按住会话列表项时发生。 |
+| `onPress` | 否 | 单击会话列表项时发生。 |
+| `onUpdateReadCount` | 否 | 更新会话列表项的消息未读数时发生。 |
+| `sortPolicy` | 否 | 设置会话列表项的排序规则。 |
+| `RenderItem` | 否 | 自定义会话列表项的样式。 |
+
+```typescript
+import * as React from "react";
+import {
+ ConversationListFragment,
+ ScreenContainer,
+} from "react-native-chat-uikit";
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx"; // 会话 ID,String 类型。
+ const chatType = 0;
+ return (
+
+
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+## 自定义实现
+### 自定义会话样式
+
+你可以设置 `ConversationListFragment` 中的 `RenderItem` 自定义会话样式,例如自定义头像、消息未读数以及消息时间戳等。
+
+:::notice
+如果开启侧滑功能,则需要设置侧滑组件的宽度。
+:::
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ return ;
+ }}
+ />
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+
+
+### 自定义会话排序
+
+会话默认按会话 ID `convId` 排序。你可以设置 `ConversationListFragment` 中的 `sortPolicy` 自定义会话排序,如置顶会话或按会话中的最新一条消息的时间戳排序。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ if (a.key > b.key) {
+ return 1;
+ } else if (a.key < b.key) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }}
+ />
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+### 自定义会话的未读消息数
+
+很多组件需要关注会话的未读消息数的通知来改变消息提醒的状态。
+
+你可以设置 `ConversationListFragment` 中的 `onUpdateReadCount` 自定义会话的未读消息数。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ // TODO:显示未读消息数
+ }}
+ />
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+### 自定义会话点击事件
+
+你可以设置 `ConversationListFragment` 中的 `onPress` 自定义会话点击事件。例如,你可以点击会话列表项,进入聊天页面。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ // TODO:进入聊天页面。
+ }}
+ />
+
+ );
+}
+```
+
+### 自定义会话列表项长按事件
+
+你可以设置 `ConversationListFragment` 中的 `onLongPress` 自定义会话列表项,例如长按后显示该项目的右键菜单。
+
+```typescript
+export default function ChatScreen(): JSX.Element {
+ const chatId = "xxx";
+ const chatType = 0;
+ return (
+
+ {
+ // TODO:显示自定义菜单。
+ }}
+ />
+
+ );
+}
+```
+
+效果如下图所示:
+
+
+
+
+
+
+
+
diff --git a/docs/uikit/react-native/overview.md b/docs/uikit/react-native/overview.md
index 8013c93ef..e18b23a07 100644
--- a/docs/uikit/react-native/overview.md
+++ b/docs/uikit/react-native/overview.md
@@ -1 +1,30 @@
-## UIKit 集成文档
+# 概述
+
+react-native-chat-uikit 是基于环信 IM SDK 的一款 UI 组件库,提供通用的 UI 组件,例如会话列表、聊天界面。利用该组件库,开发者可根据实际业务需求快速地搭建自定义 IM 应用。react-native-chat-uikit 中的组件在实现 UI 功能的同时,调用 IM SDK 相关接口实现 IM 相关逻辑和数据的处理,因而开发者在使用 react-native-chat-uikit 时只需关注自身业务或个性化扩展即可。
+
+查看 react-native-chat-uikit 源码,请点击[这里](https://github.com/easemob/react-native-chat-library/tree/dev/packages/react-native-chat-uikit)。
+
+## 功能
+
+目前,react-native-chat-uikit 提供以下功能:
+
+- 快速搭建聊天页面。
+- 快速搭建会话列表页面。
+
+react-native-chat-uikit 在单聊、群聊和聊天室会话中提供下列特性,提升聊天体验。
+
+| 特性 | 描述 |
+| :-------------- | :----------------------------- |
+| 文本消息 | 发送和接收文本消息。 |
+| 图片消息 | 发送和接收图片消息。 |
+| 语音消息 | 发送和接收语音消息。 |
+| 文件消息 | 发送和接收文件消息。 |
+| 已读回执 | 提示接收方的已读状态。该功能仅适用于单聊会话。 |
+| 未读消息数 | 显示收到消息的未读数。 |
+| 消息表情回复 | 回复消息表情。 |
+| 消息删除 | 删除本地消息。 |
+| 消息撤回 | 在指定时间内对已发送消息撤回。 |
+
+
+
+
diff --git a/docs/uikit/react-native/quickstart.md b/docs/uikit/react-native/quickstart.md
new file mode 100644
index 000000000..b7a01e55c
--- /dev/null
+++ b/docs/uikit/react-native/quickstart.md
@@ -0,0 +1,203 @@
+# 快速开始
+
+利用 react-native-chat-uikit 提供的 UI 组件,你可以轻松实现应用内的聊天。react-native-chat-uikit 支持单聊、群聊和聊天室会话,本文介绍如何实现在单聊会话中发送消息。
+
+## 前提条件
+
+集成 react-native-chat-uikit 前,你的开发环境需要满足以下条件:
+
+1. 有效的环信即时通讯 IM 开发者账号,创建应用并获取 App Key。
+2. 在[环信控制台](https://console.easemob.com/index)[创建两个用户用于聊天](/product/enable_and_configure_IM.html#创建-im-用户)。
+
+### iOS 平台
+
+- MacOS 10.15.7 或以上版本
+- Xcode 12.4 或以上版本,包括命令行工具
+- React Native 0.63.4 或以上版本
+- NodeJs 16 或以上版本,包含 npm 包管理工具
+- CocoaPods 包管理工具
+- Yarn 编译运行工具
+- Watchman 调试工具
+- 运行环境真机或模拟器 iOS 10.0 或以上版本
+
+### Android 平台
+
+- MacOS 10.15.7 或以上版本
+- Android Studio 4.0 或以上版本,包括 JDK 1.8 或以上版本
+- React Native 0.63.4 或以上版本
+- 如果用 Macos 系统开发,需要 CocoaPods 包管理工具
+- NodeJs 16 或以上版本,包含 npm 包管理工具
+- Yarn 编译运行工具
+- Watchman 调试工具
+- 运行环境真机或模拟器 Android 6.0 或以上版本
+
+## 发送第一条消息
+
+### 第一步 创建 RN 示例项目
+
+```sh
+npx react-native init RNUIkitQuickExample --version 0.71.11
+```
+
+### 第二步 初始化项目
+
+在生成的 `env` 中配置 `appkey` 等信息。
+
+```sh
+yarn && yarn run env
+```
+
+### 第三步 添加依赖到该项目
+
+其中 `react-native-chat-sdk` 为即时通讯 IM SDK, `react-native-chat-uikit` 为 UIkit,其它为三方依赖。
+
+```json
+{
+ "dependencies": {
+ "@react-native-async-storage/async-storage": "^1.17.11",
+ "@react-native-camera-roll/camera-roll": "^5.6.0",
+ "@react-native-clipboard/clipboard": "^1.11.2",
+ "@react-native-firebase/app": "^18.0.0",
+ "@react-native-firebase/messaging": "^18.0.0",
+ "react-native-audio-recorder-player": "^3.5.3",
+ "react-native-chat-sdk": "^1.2.0",
+ "react-native-chat-uikit": "^1.0.0",
+ "react-native-create-thumbnail": "^1.6.4",
+ "react-native-document-picker": "^9.0.1",
+ "react-native-fast-image": "^8.6.3",
+ "react-native-file-access": "^3.0.4",
+ "react-native-get-random-values": "~1.8.0",
+ "react-native-image-picker": "^5.4.2",
+ "react-native-permissions": "^3.8.0",
+ "react-native-safe-area-context": "4.5.0",
+ "react-native-screens": "^3.20.0",
+ "react-native-video": "^5.2.1"
+ }
+}
+```
+
+### 第四步 配置 iOS
+
+添加以下内容到 `ios/Podfile` 文件。
+
+```ruby
+target 'RNUIkitQuickExample' do
+
+ pod 'GoogleUtilities', :modular_headers => true
+ pod 'FirebaseCore', :modular_headers => true
+
+ permissions_path = File.join(File.dirname(`node --print "require.resolve('react-native-permissions/package.json')"`), "ios")
+ pod 'Permission-Camera', :path => "#{permissions_path}/Camera"
+ pod 'Permission-MediaLibrary', :path => "#{permissions_path}/MediaLibrary"
+ pod 'Permission-Microphone', :path => "#{permissions_path}/Microphone"
+ pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications"
+ pod 'Permission-PhotoLibrary', :path => "#{permissions_path}/PhotoLibrary"
+
+end
+```
+
+添加以下内容到 `ios/RNUIkitQuickExample/Info.plist` 文件。
+
+```xml
+
+ NSCameraUsageDescription
+
+ NSMicrophoneUsageDescription
+
+ NSPhotoLibraryUsageDescription
+
+
+```
+
+### 第五步 配置 Android
+
+添加以下内容到 `android/build.gradle` 文件。
+
+```groovy
+buildscript {
+ ext {
+ kotlinVersion = '1.6.10'
+ if (findProperty('android.kotlinVersion')) {
+ kotlinVersion = findProperty('android.kotlinVersion')
+ }
+ }
+ dependencies {
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
+ }
+}
+```
+
+添加以下内容到 `android/app/src/main/AndroidManifest.xml` 文件。
+
+```xml
+
+
+
+
+
+
+
+
+
+```
+
+### 第六步 初始化 UIKit 和进入聊天页面
+
+必要的代码包括初始化 react-native-chat-uikit、登录服务器、进入聊天页面开始聊天。下面添加了简单的页面路由和跳转用于演示目的。
+
+1. 初始化 react-native-chat-uikit:
+
+```typescript
+export const App = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+```
+
+2. 添加聊天详情页面:
+
+```typescript
+export function ChatScreen({
+ route,
+}: NativeStackScreenProps): JSX.Element {
+ return (
+
+
+
+ );
+}
+```
+
+### 第七步 运行示例项目
+
+```sh
+yarn run ios
+# or
+yarn run android
+```
+
+### 第八步 发送消息
+
+
+
+## 示例源码参考
+
+[查看示例完整源码](https://github.com/easemob/react-native-chat-library/tree/dev/examples/uikit-quick-start)。
diff --git a/static/images/uikitflutter/ChatConversationsView.png b/static/images/uikitflutter/ChatConversationsView.png
new file mode 100644
index 000000000..f836c13fd
Binary files /dev/null and b/static/images/uikitflutter/ChatConversationsView.png differ
diff --git a/static/images/uikitflutter/ChatConversationsView_avatar.png b/static/images/uikitflutter/ChatConversationsView_avatar.png
new file mode 100644
index 000000000..591235df4
Binary files /dev/null and b/static/images/uikitflutter/ChatConversationsView_avatar.png differ
diff --git a/static/images/uikitflutter/ChatConversationsView_click.png b/static/images/uikitflutter/ChatConversationsView_click.png
new file mode 100644
index 000000000..914b14c72
Binary files /dev/null and b/static/images/uikitflutter/ChatConversationsView_click.png differ
diff --git a/static/images/uikitflutter/ChatConversationsView_custom.png b/static/images/uikitflutter/ChatConversationsView_custom.png
new file mode 100644
index 000000000..644dc4a68
Binary files /dev/null and b/static/images/uikitflutter/ChatConversationsView_custom.png differ
diff --git a/static/images/uikitflutter/ChatConversationsView_nickname.png b/static/images/uikitflutter/ChatConversationsView_nickname.png
new file mode 100644
index 000000000..f52fa4cf2
Binary files /dev/null and b/static/images/uikitflutter/ChatConversationsView_nickname.png differ
diff --git a/static/images/uikitflutter/ChatMessagesView.png b/static/images/uikitflutter/ChatMessagesView.png
new file mode 100644
index 000000000..a182ae598
Binary files /dev/null and b/static/images/uikitflutter/ChatMessagesView.png differ
diff --git a/static/images/uikitflutter/MessagesPage.png b/static/images/uikitflutter/MessagesPage.png
new file mode 100644
index 000000000..d1b2c3c76
Binary files /dev/null and b/static/images/uikitflutter/MessagesPage.png differ
diff --git a/static/images/uikitflutter/MessagesPage_custom_avatar.png b/static/images/uikitflutter/MessagesPage_custom_avatar.png
new file mode 100644
index 000000000..1f47bdfd5
Binary files /dev/null and b/static/images/uikitflutter/MessagesPage_custom_avatar.png differ
diff --git a/static/images/uikitflutter/MessagesPage_custom_bubble.png b/static/images/uikitflutter/MessagesPage_custom_bubble.png
new file mode 100644
index 000000000..5e78aeef6
Binary files /dev/null and b/static/images/uikitflutter/MessagesPage_custom_bubble.png differ
diff --git a/static/images/uikitflutter/MessagesPage_custom_bubble_click.png b/static/images/uikitflutter/MessagesPage_custom_bubble_click.png
new file mode 100644
index 000000000..d1436ec19
Binary files /dev/null and b/static/images/uikitflutter/MessagesPage_custom_bubble_click.png differ
diff --git a/static/images/uikitflutter/MessagesPage_custom_input.png b/static/images/uikitflutter/MessagesPage_custom_input.png
new file mode 100644
index 000000000..083004df1
Binary files /dev/null and b/static/images/uikitflutter/MessagesPage_custom_input.png differ
diff --git a/static/images/uikitflutter/MessagesPage_custom_nickname.png b/static/images/uikitflutter/MessagesPage_custom_nickname.png
new file mode 100644
index 000000000..0b5c7d757
Binary files /dev/null and b/static/images/uikitflutter/MessagesPage_custom_nickname.png differ
diff --git a/static/images/uikitrn/chat_detail_bg.png b/static/images/uikitrn/chat_detail_bg.png
new file mode 100644
index 000000000..e8291d298
Binary files /dev/null and b/static/images/uikitrn/chat_detail_bg.png differ
diff --git a/static/images/uikitrn/chat_detail_msg_list_item_custom_1.png b/static/images/uikitrn/chat_detail_msg_list_item_custom_1.png
new file mode 100644
index 000000000..85ddcf491
Binary files /dev/null and b/static/images/uikitrn/chat_detail_msg_list_item_custom_1.png differ
diff --git a/static/images/uikitrn/chat_detail_msg_list_item_custom_2.png b/static/images/uikitrn/chat_detail_msg_list_item_custom_2.png
new file mode 100644
index 000000000..5d8400db0
Binary files /dev/null and b/static/images/uikitrn/chat_detail_msg_list_item_custom_2.png differ
diff --git a/static/images/uikitrn/chat_detail_msg_list_item_custom_3.png b/static/images/uikitrn/chat_detail_msg_list_item_custom_3.png
new file mode 100644
index 000000000..b356df900
Binary files /dev/null and b/static/images/uikitrn/chat_detail_msg_list_item_custom_3.png differ
diff --git a/static/images/uikitrn/chat_detail_msg_list_item_long_press.png b/static/images/uikitrn/chat_detail_msg_list_item_long_press.png
new file mode 100644
index 000000000..05ec3a425
Binary files /dev/null and b/static/images/uikitrn/chat_detail_msg_list_item_long_press.png differ
diff --git a/static/images/uikitrn/chat_detail_overview.png b/static/images/uikitrn/chat_detail_overview.png
new file mode 100644
index 000000000..afb3db12c
Binary files /dev/null and b/static/images/uikitrn/chat_detail_overview.png differ
diff --git a/static/images/uikitrn/chat_detail_send_voice_msg.png b/static/images/uikitrn/chat_detail_send_voice_msg.png
new file mode 100644
index 000000000..c20641190
Binary files /dev/null and b/static/images/uikitrn/chat_detail_send_voice_msg.png differ
diff --git a/static/images/uikitrn/chat_detail_time_label.png b/static/images/uikitrn/chat_detail_time_label.png
new file mode 100644
index 000000000..1ba9dc82a
Binary files /dev/null and b/static/images/uikitrn/chat_detail_time_label.png differ
diff --git a/static/images/uikitrn/conv_list_custom_1.png b/static/images/uikitrn/conv_list_custom_1.png
new file mode 100644
index 000000000..67c7f2e40
Binary files /dev/null and b/static/images/uikitrn/conv_list_custom_1.png differ
diff --git a/static/images/uikitrn/conv_list_custom_2.png b/static/images/uikitrn/conv_list_custom_2.png
new file mode 100644
index 000000000..a65ae0bdb
Binary files /dev/null and b/static/images/uikitrn/conv_list_custom_2.png differ
diff --git a/static/images/uikitrn/conv_list_long_press.png b/static/images/uikitrn/conv_list_long_press.png
new file mode 100644
index 000000000..fd52121ff
Binary files /dev/null and b/static/images/uikitrn/conv_list_long_press.png differ
diff --git a/static/images/uikitrn/conv_list_overview.png b/static/images/uikitrn/conv_list_overview.png
new file mode 100644
index 000000000..aa12df8e7
Binary files /dev/null and b/static/images/uikitrn/conv_list_overview.png differ
diff --git a/static/images/uikitrn/conv_list_sort.png b/static/images/uikitrn/conv_list_sort.png
new file mode 100644
index 000000000..45a65c637
Binary files /dev/null and b/static/images/uikitrn/conv_list_sort.png differ
diff --git a/static/images/uikitrn/conv_list_unread_count.png b/static/images/uikitrn/conv_list_unread_count.png
new file mode 100644
index 000000000..805853265
Binary files /dev/null and b/static/images/uikitrn/conv_list_unread_count.png differ
diff --git a/static/images/uikitrn/conversation_list_slide_menu.png b/static/images/uikitrn/conversation_list_slide_menu.png
new file mode 100644
index 000000000..e49471ad1
Binary files /dev/null and b/static/images/uikitrn/conversation_list_slide_menu.png differ
diff --git a/static/images/uikitrn/uikit_quick_start.png b/static/images/uikitrn/uikit_quick_start.png
new file mode 100644
index 000000000..4a3575635
Binary files /dev/null and b/static/images/uikitrn/uikit_quick_start.png differ