package com.xscm.modulemain.adapter; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.constraintlayout.widget.ConstraintLayout; import com.blankj.utilcode.util.LogUtils; import com.blankj.utilcode.util.ToastUtils; import com.bumptech.glide.Glide; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.transition.Transition; import com.chad.library.adapter.base.BaseMultiItemQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.xscm.modulemain.FakeNinePatchDrawable; import com.xscm.modulemain.R; import com.xscm.moduleutil.bean.GiftBean; import com.xscm.moduleutil.bean.RoomMessageEvent; import com.xscm.moduleutil.bean.UserInfo; import com.xscm.moduleutil.bean.room.EMMessageInfo; import com.xscm.moduleutil.rtc.AgoraManager; import com.xscm.moduleutil.utils.ImageUtils; import com.xscm.moduleutil.utils.MeHeadView; import com.xscm.moduleutil.widget.AdaptiveImageView; import com.xscm.moduleutil.widget.ShineTextView; import com.xscm.moduleutil.widget.img.BubbleBackgroundHelper; import com.xscm.moduleutil.bean.room.EmotionDeatils; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; /** * @author xf */ public class EaseChatAdapter extends BaseMultiItemQuickAdapter { // private int[] a = {R.drawable.random0s, R.drawable.random1s, R.drawable.random2s, R.drawable.random3s, R.drawable.random4s, R.drawable.random5s, R.drawable.random6s, R.drawable.random7s, R.drawable.random8s, R.drawable.random9s}; private int listShowType = 1; public static int SHOW_TYPE_ALL = 1; public static int SHOW_TYPE_USER = 2; public static int SHOW_TYPE_SYSTEM = 3; private List allMsgList; private List userMsgList; private List systemMsgList; private String messageNameColor = "#00C8FF";//名称的颜色 private String messageGiftColor = "#FFE309";//礼物的颜色 public boolean isPriRoom = false; public List userIdList = new ArrayList<>(); public EaseChatAdapter() { super(null); addItemType(1, com.xscm.moduleutil.R.layout.ease_row_received_message_system);//系统消息、官方公告 addItemType(2, com.xscm.moduleutil.R.layout.ease_row_received_message_user_send);//用户发送的消息 addItemType(3, com.xscm.moduleutil.R.layout.ease_row_received_message_system);//加入房间 、赠送礼物、上下麦、禁言 2025年6月12日15:42:08,让和聊天显示的是同一个额布局,后面需要修改 addItemType(4, com.xscm.moduleutil.R.layout.ease_row_received_message_new_user);//新用户注册 addItemType(5, com.xscm.moduleutil.R.layout.ease_row_received_message_wagging);//摇签 addItemType(6, com.xscm.moduleutil.R.layout.ease_row_received_message_expression);//表情 // addItemType(8, com.qxcm.moduleutil.R.layout.ease_row_received_message_game);//球球大作战 // addItemType(9, com.qxcm.moduleutil.R.layout.ease_row_received_message_demand);//派单需求 // addItemType(10, com.qxcm.moduleutil.R.layout.ease_row_received_message_red_rain);//红包雨消息 // addItemType(11, com.qxcm.moduleutil.R.layout.ease_row_received_message_red_rain);//红包雨消息结束 allMsgList = new ArrayList<>(); userMsgList = new ArrayList<>(); systemMsgList = new ArrayList<>(); } public void setShowType(int type) { if (type == listShowType) { return; } listShowType = type; List currData = new ArrayList(); if (type == SHOW_TYPE_ALL) { currData.addAll(allMsgList); } if (type == SHOW_TYPE_USER) { currData.addAll(userMsgList); } if (type == SHOW_TYPE_SYSTEM) { currData.addAll(systemMsgList); } setNewData(currData); //notifyDataSetChanged(); } private SpannableStringBuilder getSpannable(RoomMessageEvent message) { // 在 Android 中实现类似的富文本功能 SpannableStringBuilder spannable = new SpannableStringBuilder(message.getText().getText() != null ? message.getText().getText() : ""); // 设置发送者昵称的颜色 if (message.getText().getFromUserInfo() != null && !TextUtils.isEmpty(message.getText().getFromUserInfo().getNickname())) { String nickname = message.getText().getFromUserInfo().getNickname(); int startIndex = message.getText().getText().indexOf(nickname); if (startIndex != -1) { int endIndex = startIndex + nickname.length(); spannable.setSpan(new ForegroundColorSpan(Color.parseColor(messageNameColor)), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } // 设置接收者昵称的颜色 if (message.getText().getToUserInfo() != null && !TextUtils.isEmpty(message.getText().getToUserInfo().getNickname())) { String nickname = message.getText().getToUserInfo().getNickname(); int startIndex = message.getText().getText().indexOf(nickname); if (startIndex != -1) { int endIndex = startIndex + nickname.length(); spannable.setSpan(new ForegroundColorSpan(Color.parseColor(messageNameColor)), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } // 处理礼物名称的颜色设置 if (message.getText().getGiftInfo() != null && !TextUtils.isEmpty(message.getText().getGiftInfo().getGift_name())) { String[] giftNames = message.getText().getGiftInfo().getGift_name().split(","); for (String giftName : giftNames) { List occurrences = findAllOccurrencesOfString(message.getText().getText(), giftName); for (int[] range : occurrences) { spannable.setSpan(new ForegroundColorSpan(Color.parseColor(messageGiftColor)), range[0], range[1], Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } // 处理多个礼物信息的颜色设置 if (message.getText().getGiftInfos() != null && !message.getText().getGiftInfos().isEmpty()) { for (GiftBean gift : message.getText().getGiftInfos()) { if (!TextUtils.isEmpty(gift.getGift_name())) { int startIndex = message.getText().getText().indexOf(gift.getGift_name()); if (startIndex != -1) { int endIndex = startIndex + gift.getGift_name().length(); spannable.setSpan(new ForegroundColorSpan(Color.parseColor(messageGiftColor)), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } } // 处理多个接收者信息的颜色设置 if (message.getText().getToUserInfos() != null && !message.getText().getToUserInfos().isEmpty()) { for (int i = 0; i < message.getText().getToUserInfos().size(); i++) { UserInfo userModel = message.getText().getToUserInfos().get(i); if (!TextUtils.isEmpty(userModel.getNickname())) { List occurrences = findAllOccurrencesOfString(message.getText().getText(), userModel.getNickname()); for (int[] range : occurrences) { spannable.setSpan(new ForegroundColorSpan(Color.parseColor(messageNameColor)), range[0], range[1], Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } } return spannable; } /** * 查找字符串中所有子字符串出现的位置 * * @param text 原始文本 * @param searchString 要查找的子字符串 * @return 包含所有出现位置的列表,每个位置用 [start, end] 表示 */ private List findAllOccurrencesOfString(String text, String searchString) { List occurrences = new ArrayList<>(); if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchString)) { return occurrences; } int index = text.indexOf(searchString); while (index >= 0) { occurrences.add(new int[]{index, index + searchString.length()}); index = text.indexOf(searchString, index + searchString.length()); } return occurrences; } @Override protected void convert(BaseViewHolder helper, EMMessageInfo item) { RoomMessageEvent emMessage = item.getEmMessage(); // 重置 ImageView 状态,防止复用导致的图片错乱 switch (helper.getItemViewType()) { case 1: helper.getView(com.xscm.moduleutil.R.id.tv_content).setVisibility(View.VISIBLE); TextView tv1Content = helper.getView(com.xscm.moduleutil.R.id.tv_content); if (isPriRoom){ if (emMessage.getRoomId().equals(AgoraManager.getInstance().getLastRoomId())){ tv1Content.setTextColor(Color.parseColor("#FF6B647E")); helper.setText(com.xscm.moduleutil.R.id.tv_content, emMessage.getText().getText()); }else { tv1Content.setTextColor(Color.parseColor("#FFFFFFFF")); helper.setText(com.xscm.moduleutil.R.id.tv_content, getSpannable(emMessage)); } }else { tv1Content.setTextColor(Color.parseColor("#FFFFFFFF")); helper.setText(com.xscm.moduleutil.R.id.tv_content, getSpannable(emMessage)); } break; case 2: AdaptiveImageView emojiImageView = helper.getView(com.xscm.moduleutil.R.id.im_emj); // 清除之前的图片加载任务,防止图片显示在错误的位置 Glide.with(mContext).clear(emojiImageView); emojiImageView.setTag(null); emojiImageView.setVisibility(View.VISIBLE); helper.getView(com.xscm.moduleutil.R.id.tv_content).setVisibility(View.GONE); MeHeadView avatar = helper.getView(com.xscm.moduleutil.R.id.avatar); avatar.setData(emMessage.getText().getFromUserInfo().getAvatar(), "", emMessage.getText().getFromUserInfo().getNobility_image()); // ImageUtils.loadHeadCC(emMessage.getText().getFromUserInfo().getAvatar(), helper.getView(com.xscm.moduleutil.R.id.avatar)); ShineTextView tvName = helper.getView(com.xscm.moduleutil.R.id.tv_name); // 先重置tvName状态,防止复用时出现混乱 tvName.setShine(false); tvName.clearAnimation(); tvName.setText(emMessage.getText().getFromUserInfo().getNickname()); if (!emMessage.getText().getFromUserInfo().getNickname_color().isEmpty()) { emMessage.getText().getFromUserInfo().setNickname_color("#ff5878"); tvName.setStartColor(Color.parseColor(emMessage.getText().getFromUserInfo().getNickname_color())); tvName.setShineColor(Color.parseColor(emMessage.getText().getFromUserInfo().getNickname_color())); tvName.setEndColor(Color.parseColor(emMessage.getText().getFromUserInfo().getNickname_color())); tvName.setShine(true); tvName.setShineType(0); } else { tvName.setStartColor(Color.parseColor("#FFFFFFFF")); tvName.setShineColor(Color.parseColor("#FFFFFFFF")); tvName.setEndColor(Color.parseColor("#FFFFFFFF")); tvName.setShine(true); tvName.setShineType(1); } // helper.setTextColor(com.xscm.moduleutil.R.id.tv_name, emMessage.getText().getFromUserInfo().getNickname_color() != null && !emMessage.getText().getFromUserInfo().getNickname_color().isEmpty() ? Color.parseColor(emMessage.getText().getFromUserInfo().getNickname_color()) : Color.parseColor("#CCA882")); if (emMessage.getText().getText() != null) { helper.setText(com.xscm.moduleutil.R.id.tv_content, getSpannable(emMessage)); } // helper.setText(com.xscm.moduleutil.R.id.tv_content, emMessage.getText().getText()); List images = emMessage.getText().getFromUserInfo().getIcon(); LinearLayout ll_images = helper.getView(com.xscm.moduleutil.R.id.line); ll_images.removeAllViews(); for (String url : images) { if (url != null && url.contains("http")) { ImageView imageView = new ImageView(helper.itemView.getContext()); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( helper.itemView.getContext().getResources().getDimensionPixelSize(com.xscm.moduleutil.R.dimen.dp_57), helper.itemView.getContext().getResources().getDimensionPixelSize(com.xscm.moduleutil.R.dimen.dp_15) ); params.setMargins(0, 0, helper.itemView.getContext().getResources().getDimensionPixelSize(com.xscm.moduleutil.R.dimen.dp_5), 0); // 右边距 imageView.setLayoutParams(params); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); // 使用 Glide 加载图片 ImageUtils.loadHeadCC(url, imageView); ll_images.addView(imageView); } } // 为聊天气泡设置唯一标识,防止异步加载后应用到错误的视图 String bubbleId = emMessage.getRoomId() + "_" + helper.getLayoutPosition() + "_bubble"; View bubbleView = helper.getView(com.xscm.moduleutil.R.id.bubble); bubbleView.setTag(bubbleId); if (emMessage.getText().getFromUserInfo().getChat_bubble() != null && !emMessage.getText().getFromUserInfo().getChat_bubble().isEmpty()) { // 使用 Glide 加载图片并设置名称 String imageUrl = emMessage.getText().getFromUserInfo().getChat_bubble(); Glide.with(mContext) .load(imageUrl) .into(new SimpleTarget() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { // 检查视图是否仍属于当前数据项 if (bubbleId.equals(bubbleView.getTag())) { // 将资源转换为 Bitmap if (resource instanceof BitmapDrawable) { Bitmap bitmap = ((BitmapDrawable) resource).getBitmap(); // 创建新的文件名 Drawable fakeNinePatch = new FakeNinePatchDrawable(bitmap, 0.4f, 0.4f, 0.6f, 0.5f); // 设置背景 bubbleView.setBackground(fakeNinePatch); } } } @Override public void onLoadFailed(@Nullable Drawable errorDrawable) { // 检查视图是否仍属于当前数据项 if (bubbleId.equals(bubbleView.getTag())) { // 加载失败时使用默认背景 bubbleView.setBackgroundResource(com.xscm.moduleutil.R.drawable.ease_row_pubilc_user_bg); } } }); } else { // 检查视图是否仍属于当前数据项 if (bubbleId.equals(bubbleView.getTag())) { bubbleView.setBackgroundResource(com.xscm.moduleutil.R.drawable.ease_row_pubilc_user_bg); } } if (emMessage.getText().getEmoji() != null && emMessage.getText().getEmoji().getAnimate_image() != null) { // 为每个加载任务设置唯一标识 String uniqueId = emMessage.getRoomId() + "_" + helper.getLayoutPosition() + "_emoji"; emojiImageView.setTag(uniqueId); //这段代码使用 Optional 链式调用安全地获取 emMessage 中的 animate_image 字段,避免空指针异常。如果任意环节为 null,则返回空字符串。 String animateImage = Optional.ofNullable(emMessage) .map(RoomMessageEvent::getText) .map(RoomMessageEvent.T::getEmoji) .map(EmotionDeatils::getAnimate_image) .orElse(""); // 如果有动画图片URL,加载动画 if (!animateImage.isEmpty()) { // 检查视图是否仍属于当前数据项 if (uniqueId.equals(emojiImageView.getTag())) { // 使用 RequestListener 监听加载完成 Glide.with(mContext) .load(animateImage) .listener(new RequestListener() { @Override public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { // 检查视图是否仍属于当前数据项 if (uniqueId.equals(emojiImageView.getTag())) { // 加载完成后执行 new Handler().postDelayed(() -> { // 再次检查视图状态 if (uniqueId.equals(emojiImageView.getTag())) { try { Glide.with(emojiImageView) .load(emMessage.getText().getEmoji().getImage()) .into(emojiImageView); }catch (Exception e){ LogUtils.e("e:"+e); } } }, 1500); } return false; } @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { // 加载失败时检查视图是否仍属于当前数据项 if (uniqueId.equals(emojiImageView.getTag())) { // 加载静态表情作为备选 Glide.with(mContext) .load(emMessage.getText().getEmoji().getImage()) .into(emojiImageView); } return false; } }) .into(emojiImageView); } return; } // 如果没有动画图片,直接加载静态图片 // 检查视图是否仍属于当前数据项 if (uniqueId.equals(emojiImageView.getTag())) { Glide.with(mContext) .load(emMessage.getText().getEmoji().getImage()) .into(emojiImageView); } } else { // 确保在非表情情况下,重置视图状态 helper.getView(com.xscm.moduleutil.R.id.tv_content).setVisibility(View.VISIBLE); emojiImageView.setVisibility(View.GONE); // 清除可能存在的异步加载任务 Glide.with(mContext).clear(emojiImageView); } TextView tvContent = helper.getView(com.xscm.moduleutil.R.id.tv_content); ShineTextView tvContentName = helper.getView(com.xscm.moduleutil.R.id.tv_name); if (isPriRoom){ if (emMessage.getRoomId().equals(AgoraManager.getInstance().getLastRoomId()) && !userIdList.contains(emMessage.getText().getFromUserInfo().getUser_id()+"")){ tvContent.setTextColor(Color.parseColor("#FF6B647E")); setTvNameColor(tvContentName,"#FF6B647E"); }else { tvContent.setTextColor(Color.parseColor("#FFFFFF")); setTvNameColor(tvContentName,"#FFFFFF"); } } break; case 3: helper.getView(com.xscm.moduleutil.R.id.tv_content).setVisibility(View.VISIBLE); TextView tv3Content = helper.getView(com.xscm.moduleutil.R.id.tv_content); if (isPriRoom){ if (emMessage.getRoomId().equals(AgoraManager.getInstance().getLastRoomId()) && !userIdList.contains(emMessage.getText().getFromUserInfo().getUser_id()+"")){ tv3Content.setTextColor(Color.parseColor("#FF6B647E")); helper.setText(com.xscm.moduleutil.R.id.tv_content, emMessage.getText().getText()); }else { tv3Content.setTextColor(Color.parseColor("#FFFFFFFF")); helper.setText(com.xscm.moduleutil.R.id.tv_content, getSpannable(emMessage)); } }else { tv3Content.setTextColor(Color.parseColor("#FFFFFFFF")); helper.setText(com.xscm.moduleutil.R.id.tv_content, getSpannable(emMessage)); } break; } } private void setTvNameColor(ShineTextView tvName,String color){ tvName.setStartColor(Color.parseColor(color)); tvName.setShineColor(Color.parseColor(color)); tvName.setEndColor(Color.parseColor(color)); tvName.setShine(true); tvName.setShineType(1); } private void loadBubbleBackground(Context context, String bubbleUrl, ConstraintLayout messageTextView) { // String bubbleUrl = "https://midi01.oss-cn-beijing.aliyuncs.com/iosaixinqipao_17610381079117.png"; BubbleBackgroundHelper.loadBubbleBackground(context, bubbleUrl, new BubbleBackgroundHelper.OnBubbleBackgroundLoadedListener() { @Override public void onBackgroundLoaded(Drawable bubbleDrawable) { // 设置背景 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { messageTextView.setBackground(bubbleDrawable); } else { messageTextView.setBackgroundDrawable(bubbleDrawable); } // 设置内边距,让文字不会贴边 int padding = dpToPx(context, 10); messageTextView.setPadding(padding, padding, padding, padding); } @Override public void onLoadFailed() { // 加载失败,使用默认背景 messageTextView.setBackgroundResource(com.xscm.moduleutil.R.drawable.ease_row_pubilc_user_bg); } }); } private int dpToPx(Context context, int dp) { return (int) (dp * context.getResources().getDisplayMetrics().density); } @Override public void onViewRecycled(@NonNull BaseViewHolder holder) { LogUtils.e("onViewRecycled", "onViewRecycled"); // AdaptiveImageView imEmj = (AdaptiveImageView)holder.getView(com.xscm.moduleutil.R.id.im_emj); MeHeadView avatarOvalView = holder.getView(com.xscm.moduleutil.R.id.avatar); ImageView ivNobility = holder.getView(com.xscm.moduleutil.R.id.iv_nobility); ImageView ivCharm = holder.getView(com.xscm.moduleutil.R.id.iv_charm); if (mContext instanceof Activity && (((Activity) mContext).isFinishing() || ((Activity) mContext).isDestroyed())) { return; } try { // 清理表情图片 if (holder.getView(com.xscm.moduleutil.R.id.im_emj) != null) { ImageView imEmj = (ImageView) holder.getView(com.xscm.moduleutil.R.id.im_emj); imEmj.setTag(null); imEmj.setImageDrawable(null); Glide.with(imEmj.getContext()).clear(imEmj); } // 清理其他图片 if (avatarOvalView != null) { Glide.with(avatarOvalView.getContext()).clear(avatarOvalView); } if (ivNobility != null) { ivNobility.setImageDrawable(null); Glide.with(ivNobility.getContext()).clear(ivNobility); } if (ivCharm != null) { ivCharm.setImageDrawable(null); Glide.with(ivCharm.getContext()).clear(ivCharm); } // 重置tvName的闪动状态,防止复用时出现混乱 ShineTextView tvName = holder.getView(com.xscm.moduleutil.R.id.tv_name); if (tvName != null) { tvName.setShine(false); tvName.clearAnimation(); } } catch (Exception e) { e.printStackTrace(); } super.onViewRecycled(holder); } /** * 清除数据 * * @param */ public void clearData() { this.mData.clear(); userMsgList.clear(); systemMsgList.clear(); allMsgList.clear(); getData().clear(); notifyDataSetChanged(); } public void clearSomeData() { if (this.mData != null && mData.size() > 500) { List emMessageInfos = mData.subList(500, mData.size()); setNewData(emMessageInfos); } if (allMsgList.size() > 500) { allMsgList = allMsgList.subList(500, allMsgList.size()); } if (userMsgList.size() > 500) { userMsgList = userMsgList.subList(500, userMsgList.size()); } if (systemMsgList.size() > 500) { systemMsgList = systemMsgList.subList(500, systemMsgList.size()); } } @Override public void addData(@NonNull EMMessageInfo data) { // 添加到对应的存储列表 addToStorageLists(data); // 维护数据量限制 maintainDataLimits(); // 根据显示类型决定是否添加到适配器 if (shouldAddToAdapter(data)) { super.addData(data); // 确保适配器数据量也不超过限制 if (mData.size() > 200) { int removeCount = mData.size() - 200; mData.subList(0, 100).clear(); notifyDataSetChanged(); } } } private void addToStorageLists(EMMessageInfo data) { switch (data.getItemType()) { case 1: // 系统消息 allMsgList.add(data); break; case 2: // 用户消息 userMsgList.add(data); allMsgList.add(data); break; case 3: // 系统消息类型3 systemMsgList.add(data); allMsgList.add(data); break; } } private void maintainDataLimits() { // 确保所有存储列表都不超过最大限制 trimListToMaxSize(allMsgList); trimListToMaxSize(userMsgList); trimListToMaxSize(systemMsgList); } private void trimListToMaxSize(List list) { if (list.size() > 200) { int removeCount = list.size() - 200; list.subList(0, 100).clear(); } } private boolean shouldAddToAdapter(EMMessageInfo data) { // return (listShowType == SHOW_TYPE_ALL && (data.getItemType() == 1 || data.getItemType() == 2)) || // (listShowType == SHOW_TYPE_USER && data.getItemType() == 2) || // (listShowType == SHOW_TYPE_SYSTEM && data.getItemType() == 3); return true; } @Override public void addData(@NonNull Collection newData) { // super.addData(newData); if (newData == null || newData.size() == 0) { return; } List list = new ArrayList<>(); list.addAll(newData); for (int i = 0; i < list.size(); i++) { addData(list.get(i)); } } }