====== EaseUI 使用指南 ====== ---- 内容已不再维护,请参考 [[ccim:android:easeimkit|EaseIMKit 使用指南]]。 ===== 简介 ===== EaseUI 是一个 UI 库,封装了 IM 功能常用的控件、fragment 等等,旨在帮助开发者快速集成环信 SDK。 Fragment 的使用更适用于ui改动较少或者着急出功能的开发者,建议开发者基于 EaseUI 的控件做开发,这样更灵活(后续会把 fragment 级别的封装放到 demo 里)。 EaseUI 包含一个最简单的使用 demo:simpledemo,开发者可导出查看。 EaseUI 及 Demo 的 GitHub 下载地址为: * https://github.com/easemob/easeui * https://github.com/easemob/sdkdemoapp3.0_android 3.0 的代码需要切换到 SDK3.0 分支。 **注意:**因为这是一个 UI 库,后续很可能还会继续改动,新旧版本在 API 的兼容上不会像 IM SDK 那样绝对的兼容。 **注意:**单独使用 EaseUI 时请参考 demo 中的实现,确保申请了相关的权限。 ===== 视频教程 ===== 以下是 EaseUI 集成参考视频,您可以通过视频学习如何集成环信 SDK。 * [[https://ke.qq.com/webcourse/index.html#cid=320169&term_id=100380031&taid=2357941340791465&vid=x1428006jpu|Android_EaseUI集成]] ===== 代码导入 ===== EaseUI 库的代码是开源的,下载的 SDK 压缩包里面已经包含此库,解压后路径为 /examples/easeui。 ==== Eclipse 中导入 ==== - 先把 EaseUI 项目导入到 Eclipse 中。 - 在自己的项目中把 EaseUI 作为一个 library 引入。右键你的项目->Android->点击右下角的Add按钮->选中 EaseUI->OK->OK。 ==== Android Studio 中导入 ==== 打开你的 AS 项目->File->New->Import Module->选择或输入 EaseUI 库路径->Next->Next->Finish。 导入完可能会有如下错误: {{:start:200androidcleintintegration:studio_import_error.png?nolink|导入后可能会出现的错误}} 找到相应报错的地方把 ''swipeRefreshLayout.setColorSchemeResources'' 改成 ''swipeRefreshLayout.setColorScheme'',''ViewCompat.getX'' 改成 ''new ViewCompat().getX'',然后重新 build 即可。 **或者,**把 v4 包的版本号加大,譬如 compile 'com.android.support:support-v4:23.1.1'。 ===== 主要控件列表 ===== * EaseChatMessageList -- 聊天消息列表控件 * EaseConversationList -- 会话列表控件 * EaseContactList -- 联系人列表页面 * EaseChatInputMenu -- 聊天输入菜单栏- * EaseImageView -- 自定义的 ImageView,支持设置 ImageView 形状、倒角大小等 * EaseTitleBar -- 标题栏 * 其他控件,后文会挑选一些介绍 这里对聊天页面几个控件做简单图示: {{:start:200androidcleintintegration:chat_widget_simple.png?nolink|主要控件}} ===== 主要 fragment 列表 ===== * EaseChatFragment -- 聊天页面,最主要的 fragment * EaseConversationListFragment -- 会话列表页面 * EaseContactListFragment -- 联系人页面 {{:start:200androidcleintintegration:ease_fragments.png?nolink|主要fragment}} ===== 初始化 ===== 正式使用 EaseUI 需要先调用初始化方法,在 Application 的 oncreate 里调用初始化。 EMOptions options = new EMOptions(); // 默认添加好友时,是不需要验证的,改成需要验证 options.setAcceptInvitationAlways(false); ... EaseUI.getInstance().init(applicationContext, options); ===== 控件使用指南 ===== **注意:**使用 EaseUI 中的自定义控件时,如果需要 xml 中设置其属性(具体哪些属性可查看 attrs 文件), 务必在 xml 根节点中加上 ''xmlns:hyphenate="http://schemas.android.com/apk/res-auto"''。 ==== 标题栏控件 EaseTitleBar 使用 ==== {{:start:200androidcleintintegration:title_jianguo1.png?nolink|标题栏控件 EaseTitleBar 使用}} 在 xml 中声明标题栏控件,可以在 xml 直接设置标题内容,左右图片,在 Java 文件中亦可以设置这些属性以及相关的点击事件。 示例: titleBar = (EaseTitleBar) getView().findViewById(R.id.title_bar); titleBar.setTitle("张建国"); titleBar.setRightImageResource(R.drawable.ease_mm_title_remove); 可以调用 fragment 里的 hideTitleBar() 隐藏标题栏 ==== 聊天消息列表控件 EaseChatMessageList 使用 ==== {{:start:200androidcleintintegration:chat_message_list.png?nolink|聊天消息列表控件EaseChatMessageList使用}} EaseChatMessageList默认包含文字、表情、图片、语音、视频、文件消息的显示。 使用该控件,可以在 xml 中设置其 item(chatrow) 的背景图片,是否显示用户头像、昵称等属性。 常用API: messageList = (EaseChatMessageList) getView().findViewById(R.id.message_list); //初始化messagelist messageList.init(toChatUsername, chatType, null); //设置item里的控件的点击事件 messageList.setItemClickListener(new EaseChatMessageList.MessageListItemClickListener() { @Override public void onUserAvatarClick(String username) { //头像点击事件 } @Override public void onResendClick(final EMMessage message) { //重发消息按钮点击事件 } @Override public void onBubbleLongClick(EMMessage message) { //气泡框长按事件 } @Override public boolean onBubbleClick(EMMessage message) { //气泡框点击事件,EaseUI有默认实现这个事件,如果需要覆盖,return值要返回true return false; } }); //获取下拉刷新控件 swipeRefreshLayout = messageList.getSwipeRefreshLayout(); //刷新消息列表 messageList.refresh(); messageList.refreshSeekTo(position); messageList.refreshSelectLast(); ==== 自定义 EaseChatMessageList 的 item(chatrow) ==== {{:start:200androidcleintintegration:chat_message_list_item.png?nolink|自定义 EaseChatMessageList 的 item(chatrow)}} 在 EaseUI 里,每一个 messagelist 的 item 称为一个 chatrow,开发者可以覆盖默认的 chatrow 或者自定义自己的 chatrow。 简单示例(具体可参考的 chatfragment 和 easechatfragment 类): private final class CustomChatRowProvider implements EaseCustomChatRowProvider { @Override public int getCustomChatRowTypeCount() { //音、视频通话发送、接收共 4 种 return 4; } @Override public int getCustomChatRowType(EMMessage message) { if(message.getType() == EMMessage.Type.TXT){ //语音通话类型 if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false)){ return message.direct == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VOICE_CALL : MESSAGE_TYPE_SENT_VOICE_CALL; }else if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){ //视频通话 return message.direct == EMMessage.Direct.RECEIVE ? MESSAGE_TYPE_RECV_VIDEO_CALL : MESSAGE_TYPE_SENT_VIDEO_CALL; } } return 0; } @Override public EaseChatRow getCustomChatRow(EMMessage message, int position, BaseAdapter adapter) { if(message.getType() == EMMessage.Type.TXT){ // 语音通话、视频通话 if (message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VOICE_CALL, false) || message.getBooleanAttribute(Constant.MESSAGE_ATTR_IS_VIDEO_CALL, false)){ //ChatRowVoiceCall为一个继承自EaseChatRow的类 return new ChatRowVoiceCall(getActivity(), message, position, adapter); } } return null; } } //初始化的时候把 provider 传入 messageList.init(toChatUsername, chatType, new CustomChatRowProvider()); **chatrow 基类里默认会去查找头像、气泡框等控件,但是并不强制子类一定要有这些控件,子类的布局可以和基类是完全不一样的。** ==== 底部操作栏 EaseChatInputMenu 使用 ==== {{:start:200androidcleintintegration:chat_input_extend.png?nolink|底部操作栏EaseChatInputMenu使用1}} {{:start:200androidcleintintegration:chat_input_emojicon.png?nolink|底部操作栏EaseChatInputMenu使用2}} ''EaseChatInputMenu''包含3个控件: * ''EaseChatPrimaryMenu''(主菜单栏,包含文字输入、发送等功能) * ''EaseChatExtendMenu''(扩展栏,点击加号按钮出来的小宫格的菜单栏) * ''EaseEmojiconMenu''(表情栏) xml 中使用示例: **EaseChatPrimaryMenu 扩展:** 考虑到 EaseChatPrimaryMenu 变动的复杂性,EaseUI 未提供具体修改的 API,但是提供了一个 API 让开发者替换成自己写的控件。 /** * 设置自定义的表情栏,该控件需要继承自EaseEmojiconMenuBase, * 以及回调你想要回调出去的事件给设置的EaseEmojiconMenuListener * @param customEmojiconMenu */ public void setCustomEmojiconMenu(EaseEmojiconMenuBase customEmojiconMenu){ this.emojiconMenu = customEmojiconMenu; } 通过 inputmenu 对象调用此方法就行。 **EaseChatExtendMenu 扩展:** 扩展菜单栏默认是不包含任何按钮的(EaseChatFragment 中会默认加上拍照、图片、位置),通过调用 registerExtendMenuItem 就行。 inputMenu = (EaseChatInputMenu) getView().findViewById(R.id.input_menu); //注册底部菜单扩展栏item //传入item对应的文字,图片及点击事件监听,extendMenuItemClickListener实现EaseChatExtendMenuItemClickListener inputMenu.registerExtendMenuItem(R.string.attach_video, R.drawable.em_chat_video_selector, ITEM_VIDEO, extendMenuItemClickListener); inputMenu.registerExtendMenuItem(R.string.attach_file, R.drawable.em_chat_file_selector, ITEM_FILE, extendMenuItemClickListener); ... **EaseEmojiconMenu扩展:** EaseEmojiconMenu 默认包含一套默认的表情,通过 API 可替换或增加表情。 两种方法: * 调用 inputmenu init 方法的时候传入表情组列表,传 null 会显示一套默认表情,''inputMenu.init(null);'' * 通过获取到 EmojiconMenu 对象添加或删除表情组 ((EaseEmojiconMenu)inputMenu.getEmojiconMenu()).addEmojiconGroup(EmojiconExampleGroupData.getData()), ((EaseEmojiconMenu)inputMenu.getEmojiconMenu()).removeEmojiconGroup(1) 如果想完全自己实现这套表情的布局,通过调用 setCustomEmojiconMenu 实现。 /** * 设置自定义的表情栏,该控件需要继承自EaseEmojiconMenuBase, * 以及回调你想要回调出去的事件给设置的EaseEmojiconMenuListener * @param customEmojiconMenu */ public void setCustomEmojiconMenu(EaseEmojiconMenuBase customEmojiconMenu){ this.emojiconMenu = customEmojiconMenu; } 整体代码示例: inputMenu = (EaseChatInputMenu) getView().findViewById(R.id.input_menu); //注册底部菜单扩展栏item //传入item对应的文字,图片及点击事件监听,extendMenuItemClickListener实现EaseChatExtendMenuItemClickListener inputMenu.registerExtendMenuItem(R.string.attach_video, R.drawable.em_chat_video_selector, ITEM_VIDEO, extendMenuItemClickListener); inputMenu.registerExtendMenuItem(R.string.attach_file, R.drawable.em_chat_file_selector, ITEM_FILE, extendMenuItemClickListener); //初始化,此操作需放在registerExtendMenuItem后 inputMenu.init(); //设置相关事件监听 inputMenu.setChatInputMenuListener(new ChatInputMenuListener() { @Override public void onSendMessage(String content) { // 发送文本消息 sendTextMessage(content); } @Override public boolean onPressToSpeakBtnTouch(View v, MotionEvent event) { ////把touch事件传入到EaseVoiceRecorderView 里进行录音 return voiceRecorderView.onPressToSpeakBtnTouch(v, event, new EaseVoiceRecorderCallback() { @Override public void onVoiceRecordComplete(String voiceFilePath, int voiceTimeLength) { // 发送语音消息 sendVoiceMessage(voiceFilePath, voiceTimeLength); } }); } }); ==== 会话列表 EaseConversationList 使用 ==== 可在 xml 中设置 listview 的 item 的文字颜色、大小等,具体可查看 attrs 定义的 EaseConversationList 里的属性。 示例: API 调用示例: //会话列表控件 conversationListView = (EaseConversationList) getView().findViewById(R.id.list); //初始化,参数为会话列表集合 conversationListView.init(conversationList); //刷新列表 conversationListView.refresh(); 其他方法参照 listview 使用即可。 ==== 联系人列表控件 EaseContactList 使用 ==== 可直接在 xml 里设置 list item 文字颜色、大小等等,详细可设置的属性可查看 attrs 文件。 示例: API 调用示例: contactListLayout = (EaseContactList) getView().findViewById(R.id.contact_list); //初始化时需要传入联系人list contactListLayout.init(contactList); //刷新列表 contactListLayout.refresh(); ==== EaseImageView使用 ==== 自定义的ImageView,默认是矩形,支持设置ImageView形状、倒角大小等, 在xml和java文件中都可以设置 示例: xml: ''app:ease_shape_type="round"''表示将控件设置成圆形的,更多选项可以通过AS提示查看 java: EaseImageView avatarView = (EaseImageView) findViewById(R.id.iv_userhead); //设置倒角 imageView.setRadius(5); //设置成圆形,设成2为矩形 imageView.setShapeType(1); //设置边框 imageView.setBorderWidth(); imageView.setBorderColor(); ===== Fragment使用 ===== 开发者可以直接启动fragment,或者继承easeui中的fragment扩展自己所需的功能 ===== 聊天会话 fragment ===== ==== 启动聊天会话 fragment ==== new 出 EaseChatFragment 或者其子类,调用 setArguments 方法传入 chatType(会话类型)和 userId(用户或群id),通过 getSupportFragmentManager() 启动 fragment。 //new出EaseChatFragment或其子类的实例 EaseChatFragment chatFragment = new EaseChatFragment(); //传入参数 Bundle args = new Bundle(); args.putInt(EaseConstant.EXTRA_CHAT_TYPE, EaseConstant.CHATTYPE_GROUP); args.putString(EaseConstant.EXTRA_USER_ID, "zw123"); chatFragment.setArguments(args); getSupportFragmentManager().beginTransaction().add(R.id.container, chatFragment).commit(); ==== 继承 EaseChatFragment 实现会话页面 ==== 继承 EaseChatFragment扩展自己所需要的功能 由于聊天页面比较复杂,EaseUI 把 fragment 中常见的扩展功能放到一个 interface——EaseChatFragmentHelper 中,在''setUpView()''方法中调用''setChatFragmentListener''实现需要的功能,具体代码可参考 EaseUIDemo。 public interface EaseChatFragmentHelper{ /** * 设置消息扩展属性 */ void onSetMessageAttributes(EMMessage message); /** * 进入会话详情 */ void onEnterToChatDetails(); /** * 用户头像点击事件 * @param username */ void onAvatarClick(String username); /** * 消息气泡框点击事件 */ boolean onMessageBubbleClick(EMMessage message); /** * 消息气泡框长按事件 */ void onMessageBubbleLongClick(EMMessage message); /** * 扩展输入栏item点击事件,如果要覆盖EaseChatFragment已有的点击事件,return true * @param view * @param itemId * @return */ boolean onExtendMenuItemClick(int itemId, View view); /** * 设置自定义chatrow提供者 * @return */ EaseCustomChatRowProvider onSetCustomChatRowProvider(); } 虽然标题栏控件提供了修改的 API,但是如果完全不想要时,可以调用隐藏方法(所有 fragment 都提供此方法)。 //隐藏标题栏 hideTitleBar(); //显示标题栏 showTitleBar(); 其他可根据自己需求调用或者覆盖某些方法。 ===== 会话列表 fragment ===== ==== 直接启动 EaseConversationListFragment ==== 示例: conversationListFragment = new EaseConversationListFragment(); conversationListFragment.setConversationListItemClickListener(new EaseConversationListItemClickListener() { @Override public void onListItemClicked(EMConversation conversation) { startActivity(new Intent(MainActivity.this, ChatActivity.class).putExtra(EaseConstant.EXTRA_USER_ID, conversation.getUserName())); } }); //通过getSupportFragmentManager启动fragment即可 ==== 继承 EaseConversationListFragment 扩展 fragment==== 此页面比较简单,调用 conversationListView 设置 item 点击事件,其余按自己需求编写即可,譬如 listview 长按事件,可参考 Demo。 //conversationListView为EaseConversationList conversationListView.setOnItemClickListener(new OnItemClickListener() {}) ===== 联系人列表fragment ===== ====直接启动EaseContactListFragment==== 示例: contactListFragment= new EaseContactListFragment(); //需要设置联系人列表才能启动fragment contactListFragment.setContactsMap(getContacts()); //设置item点击事件 contactListFragment.setContactListItemClickListener(new EaseContactListItemClickListener() { @Override public void onListItemClicked(EaseUser user) { startActivity(new Intent(MainActivity.this, ChatActivity.class).putExtra(EaseConstant.EXTRA_USER_ID, user.getUsername())); } }); ==== 继承自 EaseContactListListFragment ==== 同 EaseConversationListFragment 一样,此 fragment 比较简单,注意在''setUpView()''里调用''setContactsMap''即可,其余按需求自由实现,可参考 ContactListFragment。 ===== 其他功能及控件使用 ===== ==== 设置用户信息提供者 ==== 设置了此提供者后,EaseUI 里的 fragment 及相关控件就会自动显示用户头像和昵称了,当然前提是你设置的 provider 返回的 user 对象设置了昵称和头像地址。 示例(具体可参考 demohelper 类): //get easeui instance easeUI = EaseUI.getInstance(); //需要EaseUI库显示用户头像和昵称设置此provider easeUI.setUserProfileProvider(new EaseUserProfileProvider() { @Override public EaseUser getUser(String username) { return getUserInfo(username); } }); ==== 设置声音振动等提示的提供者(不设置则使用 EaseUI 默认的) ==== easeUI.setSettingsProvider(new EaseSettingsProvider() {}) ==== 设置表情信息提供者 ==== 表情的显示,通过设置此 provider 实现。 //设置表情provider easeUI.setEmojiconInfoProvider(new EaseEmojiconInfoProvider() { @Override public EaseEmojicon getEmojiconInfo(String emojiconIdentityCode) { //通过表情id返回具体表情数据 EaseEmojiconGroupEntity data = EmojiconExampleGroupData.getData(); for(EaseEmojicon emojicon : data.getEmojiconList()){ if(emojicon.getIdentityCode().equals(emojiconIdentityCode)){ return emojicon; } } return null; } @Override public Map getTextEmojiconMapping() { //返回文字表情emoji文本和图片(resource id或者本地路径)的映射map return null; } }); ==== 设置通知栏内容提供者(不设置则使用默认的) ==== 示例: easeUI.getNotifier().setNotificationInfoProvider(new EaseNotificationInfoProvider() { @Override public String getTitle(EMMessage message) { //修改标题,这里使用默认 return null; } @Override public int getSmallIcon(EMMessage message) { //设置小图标,这里为默认 return 0; } @Override public String getDisplayedText(EMMessage message) { // 设置状态栏的消息提示,可以根据message的类型做相应提示 String ticker = EaseCommonUtils.getMessageDigest(message, appContext); if(message.getType() == Type.TXT){ ticker = ticker.replaceAll("\\[.{2,3}\\]", "[表情]"); } EaseUser user = getUserInfo(message.getFrom()); if(user != null){ return getUserInfo(message.getFrom()).getNick() + ": " + ticker; }else{ return message.getFrom() + ": " + ticker; } } @Override public String getLatestText(EMMessage message, int fromUsersNum, int messageNum) { return null; // return fromUsersNum + "个基友,发来了" + messageNum + "条消息"; } @Override public Intent getLaunchIntent(EMMessage message) { //设置点击通知栏跳转事件 Intent intent = new Intent(appContext, ChatActivity.class); //有电话时优先跳转到通话页面 if(isVideoCalling){ intent = new Intent(appContext, VideoCallActivity.class); }else if(isVoiceCalling){ intent = new Intent(appContext, VoiceCallActivity.class); }else{ ChatType chatType = message.getChatType(); if (chatType == ChatType.Chat) { // 单聊信息 intent.putExtra("userId", message.getFrom()); intent.putExtra("chatType", Constant.CHATTYPE_SINGLE); } else { // 群聊信息 // message.getTo()为群聊id intent.putExtra("userId", message.getTo()); if(chatType == ChatType.GroupChat){ intent.putExtra("chatType", Constant.CHATTYPE_GROUP); }else{ intent.putExtra("chatType", Constant.CHATTYPE_CHATROOM); } } } return intent; } }); ---- 上一页:[[im:android:other:demo|Demo 介绍]] 下一页:[[im:android:other:privatecloud|私有云SDK集成配置]]