融云即时通讯SDK集成 -- 定制UI(二) ——添加自定义表情库
背景:
最近公司新上的app要加上即时通讯的功能, 自己快速实现一个当然是不可能的了(项目deadline也顶不住哇).就从各家成熟的SDK厂商选来选去的, 各有各的好也各有各的不足.最后点兵点将,选了融云家的SDK(老板说了算hhhh).
他家的官网和文档地址:
官网:https://www.rongcloud.cn/
文档:https://docs.rongcloud.cn/v4
这个任务当然还是落在我的头上. 我是使用的他们家的带UI的sdk,(他们家有带UI和不带UI的两种sdk, 不带UI的sdk就是只有即时通讯能力, 所有的UI都需要开发者自定实现, 带UI的sdk封装了一些基本的界面,例如会话列表, 和别人聊天的会话界面.)当然这些已经集成了UI的sdk并不能完全满足一个产品的需求, 所以这篇文章跟大家讲下如何添加一套自定义的表情包.
虽然这里有点难看了哈哈, 不过是为了给大家展示方法嘛, 就不管那么多了. 可以看到底下除了默认的emoji的表情包, 还多了一个tab, 我是从QQ表情搞了一套, 直接就加在这里了. 一个可爱的小猪猪哈哈哈.
添加步骤
需要改动的有这么几个类:
AndroidEmoji: 控制emoji图标资源, 编码, 以及相应展示的类
RongExtension: 会话界面除去聊天气泡与title bar的整个下方输入区域
IEmoticonTab: 表情tab
DefaultExtensionModule: 表情tab的上层控件.
对于AndroidEmoji这个类, 可以直接照抄, 把资源文件替换成自己准备好的图标, 以及编码/描述
public class ConversationListAdapter extends BaseAdapter {
private final static String TAG = "ConversationListAdapter";
LayoutInflater mInflater;
Context mContext;
@Override
public long getItemId(int position) {
UIConversation conversation = getItem(position);
if (conversation == null)
return 0;
return conversation.hashCode();
}
protected class ViewHolder {
public View layout;
public View leftImageLayout;
public View rightImageLayout;
public View leftUnReadView;
public View rightUnReadView;
public AsyncImageView leftImageView;
public TextView unReadMsgCount;
public ImageView unReadMsgCountIcon;
public AsyncImageView rightImageView;
public TextView unReadMsgCountRight;
public ImageView unReadMsgCountRightIcon;
public ProviderContainerView contentView;
}
public ConversationListAdapter(Context context) {
super();
mContext = context;
mInflater = LayoutInflater.from(mContext);
}
public int findGatheredItem(Conversation.ConversationType type) {
int index = getCount();
int position = -1;
while ((index-- > 0)) {
UIConversation uiConversation = getItem(index);
if (uiConversation.getConversationType().equals(type)) {
position = index;
break;
}
}
return position;
}
public int findPosition(Conversation.ConversationType type, String targetId) {
int index = getCount();
int position = -1;
while (index-- > 0) {
if (getItem(index).getConversationType().equals(type)
&& getItem(index).getConversationTargetId().equals(targetId)) {
position = index;
break;
}
}
return position;
}
@Override
protected View newView(Context context, int position, ViewGroup group) {
View result = mInflater.inflate(R.layout.rc_item_conversation, null);
ViewHolder holder = new ViewHolder();
holder.layout = findViewById(result, R.id.rc_item_conversation);
holder.leftImageLayout = findViewById(result, R.id.rc_item1);
holder.rightImageLayout = findViewById(result, R.id.rc_item2);
holder.leftUnReadView = findViewById(result, R.id.rc_unread_view_left);
holder.rightUnReadView = findViewById(result, R.id.rc_unread_view_right);
holder.leftImageView = findViewById(result, R.id.rc_left);
holder.rightImageView = findViewById(result, R.id.rc_right);
holder.contentView = findViewById(result, R.id.rc_content);
holder.unReadMsgCount = findViewById(result, R.id.rc_unread_message);
holder.unReadMsgCountRight = findViewById(result, R.id.rc_unread_message_right);
holder.unReadMsgCountIcon = findViewById(result, R.id.rc_unread_message_icon);
holder.unReadMsgCountRightIcon = findViewById(result, R.id.rc_unread_message_icon_right);
result.setTag(holder);
return result;
}
@Override
protected void bindView(View v, int position, final UIConversation data) {
ViewHolder holder = (ViewHolder) v.getTag();
if (data == null) {
return;
}
/*通过会话类型,获得对应的会话provider.ex: PrivateConversationProvider*/
IContainerItemProvider provider = RongContext.getInstance().getConversationTemplate(data.getConversationType().getName());
if (provider == null) {
RLog.e(TAG, "provider is null");
return;
}
View view = holder.contentView.inflate(provider);
provider.bindView(view, position, data);
//设置背景色
if (data.isTop())
holder.layout.setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.rc_item_top_list_selector));
else
holder.layout.setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.rc_item_list_selector));
ConversationProviderTag tag = RongContext.getInstance().getConversationProviderTag(data.getConversationType().getName());
int defaultId;
if (data.getConversationType().equals(Conversation.ConversationType.GROUP)) {
defaultId = R.drawable.rc_default_group_portrait;
} else if (data.getConversationType().equals(Conversation.ConversationType.DISCUSSION)) {
defaultId = R.drawable.rc_default_discussion_portrait;
} else {
defaultId = R.drawable.rc_default_portrait;
}
// 1:图像靠左显示。2:图像靠右显示。3:不显示图像。
if (tag.portraitPosition() == 1) {
holder.leftImageLayout.setVisibility(View.VISIBLE);
holder.leftImageLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnPortraitItemClick != null)
mOnPortraitItemClick.onPortraitItemClick(v, data);
}
});
holder.leftImageLayout.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnPortraitItemClick != null)
mOnPortraitItemClick.onPortraitItemLongClick(v, data);
return true;
}
});
if (data.getConversationGatherState()) {
holder.leftImageView.setAvatar(null, defaultId);
} else {
if (data.getIconUrl() != null) {
holder.leftImageView.setAvatar(data.getIconUrl().toString(), defaultId);
} else {
holder.leftImageView.setAvatar(null, defaultId);
}
}
if (data.getUnReadMessageCount() > 0) {
holder.unReadMsgCountIcon.setVisibility(View.VISIBLE);
setUnReadViewLayoutParams(holder.leftUnReadView, data.getUnReadType());
if (data.getUnReadType().equals(UIConversation.UnreadRemindType.REMIND_WITH_COUNTING)) {
if (data.getUnReadMessageCount() > 99) {
holder.unReadMsgCount.setText(mContext.getResources().getString(R.string.rc_message_unread_count));
} else {
holder.unReadMsgCount.setText(Integer.toString(data.getUnReadMessageCount()));
}
holder.unReadMsgCount.setVisibility(View.VISIBLE);
holder.unReadMsgCountIcon.setImageResource(R.drawable.rc_unread_count_bg);
} else {
holder.unReadMsgCount.setVisibility(View.GONE);
holder.unReadMsgCountIcon.setImageResource(R.drawable.rc_unread_remind_list_count);
}
} else {
holder.unReadMsgCountIcon.setVisibility(View.GONE);
holder.unReadMsgCount.setVisibility(View.GONE);
}
holder.rightImageLayout.setVisibility(View.GONE);
} else if (tag.portraitPosition() == 2) {
holder.rightImageLayout.setVisibility(View.VISIBLE);
holder.rightImageLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnPortraitItemClick != null)
mOnPortraitItemClick.onPortraitItemClick(v, data);
}
});
holder.rightImageLayout.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnPortraitItemClick != null)
mOnPortraitItemClick.onPortraitItemLongClick(v, data);
return true;
}
});
if (data.getConversationGatherState()) {
holder.rightImageView.setAvatar(null, defaultId);
} else {
if (data.getIconUrl() != null) {
holder.rightImageView.setAvatar(data.getIconUrl().toString(), defaultId);
} else {
holder.rightImageView.setAvatar(null, defaultId);
}
}
if (data.getUnReadMessageCount() > 0) {
holder.unReadMsgCountRightIcon.setVisibility(View.VISIBLE);
setUnReadViewLayoutParams(holder.rightUnReadView, data.getUnReadType());
if (data.getUnReadType().equals(UIConversation.UnreadRemindType.REMIND_WITH_COUNTING)) {
holder.unReadMsgCount.setVisibility(View.VISIBLE);
if (data.getUnReadMessageCount() > 99) {
holder.unReadMsgCountRight.setText(mContext.getResources().getString(R.string.rc_message_unread_count));
} else {
holder.unReadMsgCountRight.setText(Integer.toString(data.getUnReadMessageCount()));
}
holder.unReadMsgCountRightIcon.setImageResource(R.drawable.rc_unread_count_bg);
} else {
holder.unReadMsgCount.setVisibility(View.GONE);
holder.unReadMsgCountRightIcon.setImageResource(R.drawable.rc_unread_remind_without_count);
}
} else {
holder.unReadMsgCountIcon.setVisibility(View.GONE);
holder.unReadMsgCount.setVisibility(View.GONE);
}
holder.leftImageLayout.setVisibility(View.GONE);
} else if (tag.portraitPosition() == 3) {
holder.rightImageLayout.setVisibility(View.GONE);
holder.leftImageLayout.setVisibility(View.GONE);
} else {
throw new IllegalArgumentException("the portrait position is wrong!");
}
MessageContent content = data.getMessageContent();
if (content != null && content.isDestruct()) {
RongIMClient.getInstance().getMessage(data.getLatestMessageId(), new RongIMClient.ResultCallback() {
@Override
public void onSuccess(Message message) {
if (message == null) {
EventBus.getDefault().post(new Event.MessageDeleteEvent(data.getLatestMessageId()));
}
}
@Override
public void onError(RongIMClient.ErrorCode e) {
}
});
}
}
protected void setUnReadViewLayoutParams(View view, UIConversation.UnreadRemindType type) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
Context context = view.getContext();
if (type == UIConversation.UnreadRemindType.REMIND_WITH_COUNTING) {
params.width = (int) context.getResources().getDimension(R.dimen.rc_dimen_size_18);
params.height = (int) context.getResources().getDimension(R.dimen.rc_dimen_size_18);
params.leftMargin = (int) mContext.getResources().getDimension(R.dimen.rc_dimen_size_44);
params.topMargin = (int) context.getResources().getDimension(R.dimen.rc_dimen_size_5);
} else {
params.width = (int) context.getResources().getDimension(R.dimen.rc_dimen_size_9);
params.height = (int) context.getResources().getDimension(R.dimen.rc_dimen_size_9);
params.leftMargin = (int) context.getResources().getDimension(R.dimen.rc_dimen_size_50);
params.topMargin = (int) context.getResources().getDimension(R.dimen.rc_dimen_size_7);
}
view.setLayoutParams(params);
}
private OnPortraitItemClick mOnPortraitItemClick;
public interface OnPortraitItemClick {
void onPortraitItemClick(View v, UIConversation data);
boolean onPortraitItemLongClick(View v, UIConversation data);
}
public void setOnPortraitItemClick(OnPortraitItemClick onPortraitItemClick) {
this.mOnPortraitItemClick = onPortraitItemClick;
}
}
集成DefaultExtensionModule, 实现MyExtensionModule, 重写getEmoticonTabs()方法, 为每个图标设置监听.
@Override
public List getEmoticonTabs() {
List emoticonTabs = super.getEmoticonTabs();
MyEmoticonTab myEmoticonTab =new MyEmoticonTab();
myEmoticonTab.setOnItemClickListener(new IEmojiItemClickListener() {
@Override
public void onEmojiClick(String emoji) {
EditText editText = MyExtensionModule.this.mEditText;
if (editText != null) {
int start = editText.getSelectionStart();
editText.getText().insert(start, emoji);
}
}
@Override
public void onDeleteClick() {
EditText editText = MyExtensionModule.this.mEditText;
if (editText != null) {
editText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
}
}
});
emoticonTabs.add(myEmoticonTab);
return emoticonTabs;
}
继承RongExtension, 实现MyRongExtension, 然后获取到editText, 为其设置监听, 代码如下:
public class MyRongExtension extends RongExtension {
EditText mEditText;
public MyRongExtension(Context context) {
super(context);
tryAddTextChangedAction();
}
public MyRongExtension(Context context, AttributeSet attrs) {
super(context, attrs);
tryAddTextChangedAction();
}
private void tryAddTextChangedAction() {
mEditText = getInputEditText();
TextWatcher textWatcher = new TextWatcher() {
private int start;
private int count;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
this.start = start;
this.count = count;
}
@Override
public void afterTextChanged(Editable s) {
// 这块的检验规则是纯emoji的, 加其他表情的话直接给他去掉, 或者自己写规则。
// if (QQEmoji.isQQEmoji(s.subSequence(start, start + count).toString())) {
mEditText.removeTextChangedListener(this);
String resultStr = QQEmoji.replaceEmojiWithText(s.toString());
mEditText.setText(QQEmoji.ensure(resultStr), TextView.BufferType.SPANNABLE);
mEditText.setSelection(mEditText.getText().length());
mEditText.addTextChangedListener(this);
// }
}
};
mEditText.addTextChangedListener(textWatcher);
}
}
这样一来, 就大功告成啦. 添加好了一套属于自己的表情包!