package com.ckkj.water.chat; import static android.app.Activity.RESULT_OK; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.widget.NestedScrollView; import androidx.recyclerview.widget.GridLayoutManager; import com.chad.library.adapter.base.BaseQuickAdapter; import com.ckkj.water.AppConfig; import com.ckkj.water.EventMessage; import com.ckkj.water.R; import com.ckkj.water.base.BaseFragment; import com.ckkj.water.chat.adapter.ChatQuestionDetailAdapter; import com.ckkj.water.chat.adapter.ChatZixunAdapter; import com.ckkj.water.chat.entity.ChatQuestionEntity; import com.ckkj.water.custom.GridSpacingItemDecoration; import com.ckkj.water.databinding.IndexLayoutBinding; import com.ckkj.water.chat.adapter.IndexChatAdapter; import com.ckkj.water.chat.adapter.IndexOptionAdapter; import com.ckkj.water.chat.adapter.IndexEnterHintAdapter; import com.ckkj.water.chat.adapter.IndexQuickAskAdapter; import com.ckkj.water.chat.entity.CharHistoryEntity; import com.ckkj.water.chat.entity.ChatAiMsgEntity; import com.ckkj.water.chat.entity.ChatMultiItem; import com.ckkj.water.chat.entity.IndexTemplateEntity; import com.ckkj.water.chat.mvp.IndexContract; import com.ckkj.water.chat.mvp.IndexImpl; import com.ckkj.water.chat.mvp.IndexPresenter; import com.ckkj.water.dialog.ContentReportDialog; import com.ckkj.water.login.LoginActivity; import com.ckkj.water.network.BaseBean; import com.ckkj.water.plate.entity.BaziResultBean; import com.ckkj.water.plate.entity.PaipanCache; import com.ckkj.water.utils.click.ClickUtil; import com.ckkj.water.utils.manager.ColorManage; import com.ckkj.water.utils.show.ToastUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import de.greenrobot.event.Subscribe; import de.greenrobot.event.ThreadMode; import android.content.ClipData; import android.content.ClipboardManager; /** * @author fengxiaoyang * @date 2025/7/7 * @description TODO: 描述该类的作用 */ public class ChatFragment extends BaseFragment implements IndexContract.IndexView { //顶部 adapter private IndexOptionAdapter mOptionAdapter; private ChatZixunAdapter mZixunQuestAdapter; private ChatQuestionDetailAdapter mQuesDetailAdapter; //快速提问 adapter private IndexQuickAskAdapter mQuickAskAdapter; private List mQuickAskList = new ArrayList<>(); private List mChatMsgList = new ArrayList<>(); //聊天 adapter private IndexChatAdapter mChatAdapter; //欢迎语 adapter private IndexEnterHintAdapter mHintAdapter; //输入的内容 private String mCurrentInputContent = ""; //当前options 的 pos private int mCurrentStreamPos = 0; //模板数据 list private List mIndexTemList = new ArrayList<>(); //输出是否完成 private boolean mIsStreamStop = true; //当前选中 option 的 typeID private String mSelectTypeID = "1"; //会话 ID private String mSessionId = ""; // 添加流式数据累积变量 private StringBuilder mStreamContentBuilder = new StringBuilder(); private ChatAiMsgEntity mCurrentStreamEntity = null; // 智能滚动管理 private boolean mIsUserScrolling = false; // 优化防抖机制 private Handler mUpdateHandler = new Handler(); private Runnable mUpdateRunnable; private List mQuesTypeList = new ArrayList<>(); private boolean mIsShowQuestions = true; @Override public void initData(Bundle state) { mOptionAdapter = new IndexOptionAdapter(mContext, R.layout.index_device_list_item, mIndexTemList); viewBinding.rvOptions.setAdapter(mOptionAdapter); mQuickAskAdapter = new IndexQuickAskAdapter(mContext, R.layout.index_home_quick_ask_item, mQuickAskList); viewBinding.rvQuickAsk.setAdapter(mQuickAskAdapter); mChatAdapter = new IndexChatAdapter(mContext, mChatMsgList, viewBinding.rvChat); viewBinding.rvChat.setAdapter(mChatAdapter); //提问类型 GridLayoutManager manager = new GridLayoutManager(mContext, 2); viewBinding.rvChatQuick.setLayoutManager(manager); List subList = new ArrayList<>(); mQuesDetailAdapter = new ChatQuestionDetailAdapter(R.layout.chat_quest_detail_item, subList); viewBinding.rvChatQuick.setAdapter(mQuesDetailAdapter); viewBinding.rvChatQuick.addItemDecoration(new GridSpacingItemDecoration(2, 10, true)); mZixunQuestAdapter = new ChatZixunAdapter(mContext, R.layout.chat_zixun_type_item, mQuesTypeList); viewBinding.rvChatType.setAdapter(mZixunQuestAdapter); // 设置聊天RecyclerView的LayoutManager(禁用滚动) androidx.recyclerview.widget.LinearLayoutManager chatLayoutManager = new androidx.recyclerview.widget.LinearLayoutManager(mContext); chatLayoutManager.setStackFromEnd(false); viewBinding.rvChat.setLayoutManager(chatLayoutManager); // 禁用RecyclerView的滚动,让NestedScrollView处理滚动 viewBinding.rvChat.setNestedScrollingEnabled(false); mHintAdapter = new IndexEnterHintAdapter(mContext, R.layout.index_enter_hint_item, new ArrayList<>()); viewBinding.rvEnterHint.setAdapter(mHintAdapter); setAdapterClick(); // 添加EditText的焦点监听器 viewBinding.editInput.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { // EditText获取焦点时,延迟滚动到底部,确保键盘弹出后再滚动 new Handler().postDelayed(new Runnable() { @Override public void run() { scrollNestedScrollViewToBottom(); } }, 300); // 延迟300ms,等待键盘弹出 } } }); viewBinding.editInput.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if (s != null) { mCurrentInputContent = s.toString().trim(); if (TextUtils.isEmpty(s.toString().trim())) { viewBinding.imgSend.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_index_send)); viewBinding.editInput.setHint("请输入您的问题"); } else { viewBinding.imgSend.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_index_send_b)); } } } }); //请求页面模板数据 mPresenter.getIndexTemplateDatas(); mPresenter.getChatTemplateDatas(); //组件增加点击区域 ClickUtil.addClickPart(viewBinding.imgCloseQuick, 30); ClickUtil.addClickPart(viewBinding.imgRefresh, 30); ClickUtil.addClickPart(viewBinding.imgHistory, 30); // 设置智能滚动监听 setupSmartScrollListener(); // 设置页面点击监听,用于隐藏键盘 setupPageClickListener(); // 检查是否有传递过来的参数 checkAndHandleBundle(); } /** * 检查并处理传递过来的Bundle参数 */ private void checkAndHandleBundle() { Bundle args = getArguments(); if (args != null) { handleBirthInfoBundle(args); } } /** * 处理出生信息Bundle参数 * * @param bundle 包含出生信息的Bundle */ public void handleBirthInfoBundle(Bundle bundle) { if (bundle != null) { BaziResultBean baseEntity = PaipanCache.get(); // 获取出生信息 BaziResultBean.BaseInfoDTO birthInfo = baseEntity.getBaseInfo(); BaziResultBean.DetailInfoDTO.SizhuInfoDTO sizhuInfoDTO = baseEntity.getDetailInfo().getSizhuInfo(); BaziResultBean.DetailInfoDTO.DayunInfoDTO defaulLiunian = (BaziResultBean.DetailInfoDTO.DayunInfoDTO) bundle.getSerializable("defaul_liunian"); if (birthInfo != null) { viewBinding.editInput.setText("我叫" + birthInfo.getName() + "," + ((birthInfo.getSex().equals("乾造") ? "男" : "女") + "," + "生于公历" + birthInfo.getGongli() + "," + "当前大运为" + defaulLiunian.getDayunStartYear() + "--" + defaulLiunian.getDayunEndYear() + "," + defaulLiunian.getDayunStartGanzhi() + "大运,四柱为" + sizhuInfoDTO.getYear().getTg() + sizhuInfoDTO.getYear().getDz() + "、" + sizhuInfoDTO.getMonth().getTg() + sizhuInfoDTO.getMonth().getDz() + "、" + sizhuInfoDTO.getDay().getTg() + sizhuInfoDTO.getDay().getDz() + "、" + sizhuInfoDTO.getHour().getTg() + sizhuInfoDTO.getHour().getDz() + "," + "现在需要你根据我的八字,给我具体分析一下我的事业、感情、健康、财富、格局、喜用神")); } } } /** * 当Fragment参数更新时调用(用于处理onNewIntent的情况) */ @Override public void onResume() { super.onResume(); // 检查是否有新的参数需要处理 Bundle args = getArguments(); if (args != null && args.getBoolean("goto_index_fragment", false)) { // 处理参数 handleBirthInfoBundle(args); // 清除标记,避免重复处理 args.remove("goto_index_fragment"); } } @Subscribe(threadMode = ThreadMode.MainThread) public void onGetMessage(EventMessage message) { //用户添加地址成功 if (message.getEventStrKey().equals(EventMessage.HISTORY_SESSIONID_DELETE_SUCCESS)) { if (message.getMsg().equals(mSessionId)) { // viewBinding.linearOptionHint.setVisibility(View.VISIBLE); mChatMsgList.clear(); mChatAdapter.setNewData(mChatMsgList); mSessionId = ""; } } } /** * 设置页面点击监听器 */ private void setupPageClickListener() { viewBinding.rvChat.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // 当用户触摸NestedScrollView时,隐藏键盘并清除焦点 if (event.getAction() == MotionEvent.ACTION_DOWN) { hideKeyboardAndClearFocus(); } return false; } }); } /** * 隐藏键盘并清除EditText焦点 */ private void hideKeyboardAndClearFocus() { // 清除EditText焦点 viewBinding.editInput.clearFocus(); // 隐藏键盘 InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(viewBinding.editInput.getWindowToken(), 0); } } /** * 滚动NestedScrollView到底部 */ private void scrollNestedScrollViewToBottom() { if (viewBinding.nestedScrollChat != null) { // 获取NestedScrollView的子视图(RecyclerView) View childView = viewBinding.nestedScrollChat.getChildAt(0); if (childView != null) { // 滚动到底部 viewBinding.nestedScrollChat.smoothScrollTo(0, childView.getHeight()); } } } /** * 设置智能滚动监听器(修改为监听NestedScrollView) */ private void setupSmartScrollListener() { // 监听NestedScrollView的滚动 viewBinding.nestedScrollChat.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { @Override public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { // 检测用户是否向上滑动 if (scrollY < oldScrollY) { // 用户向上滑动 mIsUserScrolling = true; } else if (scrollY > oldScrollY) { // 用户向下滑动 mIsUserScrolling = true; } // 检查是否滚动到底部 if (scrollY >= (v.getChildAt(0).getHeight() - v.getHeight())) { // 滚动到底部,恢复自动滚动 mIsUserScrolling = false; } } }); } private void setAdapterClick() { //顶部的选项 adapter 点击事件 mOptionAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { if (mCurrentStreamPos != position) { if (mIsStreamStop) { viewBinding.rlQuickAsk.setVisibility(View.GONE); mOptionAdapter.setSelectPos(position); setOptionSelStyle(position); mCurrentStreamPos = position; mSessionId = ""; mSelectTypeID = mIndexTemList.get(position).getId(); } else { ToastUtil.showShort(mContext, "Ai解答中,请稍后操作"); } } } }); //咨询类型 adapter mZixunQuestAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { if (mIsStreamStop) { mZixunQuestAdapter.setSelectPos(position); mQuesDetailAdapter.setNewData(mQuesTypeList.get(position).getSubheadings()); } else { ToastUtil.showShort(mContext, "Ai解答中,请稍后操作"); } } }); //快速提问 mQuesDetailAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { if (!AppConfig.USER_LOGIN) { goTo(mContext, LoginActivity.class, null); return; } if (!mIsStreamStop) { ToastUtil.showShort(mContext, "Ai解答中,请稍后操作"); return; } String quickAskContent = mQuesTypeList.get(mZixunQuestAdapter.getmSelPos()). getSubheadings().get(position).getContent(); sendMsgToAi(quickAskContent); } }); //流式输出,是否完成 mChatAdapter.setOnStreamCompleteListener(new IndexChatAdapter.OnStreamCompleteListener() { @Override //此时已经流式输出完成 public void onStreamComplete(int position) { mCurrentStreamPos = position; viewBinding.imgSend.setVisibility(View.VISIBLE); viewBinding.imgStop.setVisibility(View.GONE); mIsStreamStop = true; } @Override public void onStreamStop() { viewBinding.imgSend.setVisibility(View.VISIBLE); viewBinding.imgStop.setVisibility(View.GONE); mIsStreamStop = true; } }); mChatAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() { @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { switch (view.getId()) { case R.id.img_jb: ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, ""); cancelDialog.show(); cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() { @Override public void callBackDatas(String reason) { showLoadingDialog(); //举报 mPresenter.commitReport(reason); } }); break; } } }); // 设置长按菜单监听器 mChatAdapter.setOnItemLongClickMenuListener(new IndexChatAdapter.OnItemLongClickMenuListener() { @Override public void onCopyClick(ChatMultiItem item, int position) { // 处理复制操作 String content = item.getMessage().getContent(); if (!TextUtils.isEmpty(content)) { ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE); ClipData clip = ClipData.newPlainText("chat_content", content); clipboard.setPrimaryClip(clip); ToastUtil.showShort(mContext, "内容已复制"); } } @Override public void onReportClick(ChatMultiItem item, int position) { // 处理举报操作 ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, ""); cancelDialog.show(); cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() { @Override public void callBackDatas(String reason) { showLoadingDialog(); //举报 mPresenter.commitReport(reason); } }); } }); // 添加长按监听器 // mChatAdapter.setOnItemChildLongClickListener(new BaseQuickAdapter.OnItemChildLongClickListener() { // @Override // public boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position) { // switch (view.getId()) { // case R.id.tv_text: // 文本内容的view id // XPopup.fixLongClick(view); // 能保证弹窗弹出后,下层的View无法滑动 // // // 获取当前消息内容 // ChatMultiItem item = mChatMsgList.get(position); // String content = item.getMessage().getContent(); // // // 使用链式调用,确保atView在asAttachList之前 // new XPopup.Builder(getContext()) // .hasShadowBg(false) // .atView(view) // 使用atView而不是watchView // .asAttachList(new String[]{"复制", "举报"}, // null, // new OnSelectListener() { // @Override // public void onSelect(int selectPosition, String text) { // switch (selectPosition) { // case 0: // 复制 // if (!TextUtils.isEmpty(content)) { // ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE); // ClipData clip = ClipData.newPlainText("chat_content", content); // clipboard.setPrimaryClip(clip); // ToastUtil.showShort(mContext, "内容已复制"); // } // break; // case 1: // 举报 // ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, ""); // cancelDialog.show(); // cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() { // @Override // public void callBackDatas(String reason) { // showLoadingDialog(); // //举报 // mPresenter.commitReport(reason); // } // }); // break; // } // } // }) // .show(); // return true; // } // return false; // } // }); } /** * 停止输出 */ private void stopSteamOutput() { // 停止当前网络请求 mPresenter.stopCurrentRequest(); // 获取当前正在流式输出的位置 int currentPosition = mChatMsgList.size() - 1; // 标记当前消息为被停止状态 mChatAdapter.markAsStopped(currentPosition); // 停止适配器中的流式输出 mChatAdapter.setStreamerStop(currentPosition); // 清除当前流式输出位置 mChatAdapter.setCurrentStreamingPosition(-1); // 强制更新当前item以显示停止标识 mChatAdapter.notifyItemChanged(currentPosition); // 恢复UI状态 viewBinding.imgSend.setVisibility(View.VISIBLE); viewBinding.imgStop.setVisibility(View.GONE); mIsStreamStop = true; } @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data != null) { if (resultCode == RESULT_OK) { switch (requestCode) { case REQUEST_EVENT_CODE_10001: String sessionId = data.getStringExtra("session_id"); String numerologyType = data.getStringExtra("numerology_type"); int showSelPos = -1; for (int i = 0; i < mIndexTemList.size(); i++) { if (numerologyType.equals(mIndexTemList.get(i).getId())) { showSelPos = i; } } if (mCurrentStreamPos != showSelPos) { } if (mIsStreamStop) { viewBinding.rlQuickAsk.setVisibility(View.GONE); mOptionAdapter.setSelectPos(showSelPos); setOptionSelStyle(showSelPos); mCurrentStreamPos = showSelPos; mSelectTypeID = mIndexTemList.get(showSelPos).getId(); if (!TextUtils.isEmpty(sessionId)) { mPresenter.getChatDetailByID(sessionId); } } else { ToastUtil.showShort(mContext, "Ai解答中,请稍后操作"); } break; } } } } /** * 发送消息给 AI */ private void sendMsgToAi(String senMsg) { if (!AppConfig.USER_LOGIN) { goTo(mContext, LoginActivity.class, null); return; } viewBinding.cardQuestion.setVisibility(View.GONE); viewBinding.imgClose.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_chat_down)); mIsShowQuestions = false; viewBinding.linearOptionHint.setVisibility(View.GONE); // 重置滚动状态 mIsUserScrolling = false; // 清理适配器缓存 mChatAdapter.clearCache(); // 创建用户消息 ChatAiMsgEntity chatMsgEntity = new ChatAiMsgEntity(); chatMsgEntity.setSessionId(mSessionId); chatMsgEntity.setType(mSelectTypeID); chatMsgEntity.setContent(senMsg); ChatMultiItem userEntity = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_RIGHT, chatMsgEntity); mChatMsgList.add(userEntity); mChatAdapter.notifyItemInserted(mChatMsgList.size() - 1); // 创建AI回复消息(初始为空) mCurrentStreamEntity = new ChatAiMsgEntity(); mCurrentStreamEntity.setSessionId(mSessionId); mCurrentStreamEntity.setType(mSelectTypeID); mCurrentStreamEntity.setContent(""); // 初始为空 ChatMultiItem aiEntity = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_LEFT, mCurrentStreamEntity); mChatMsgList.add(aiEntity); // 设置当前流式输出位置 int aiMessagePosition = mChatMsgList.size() - 1; mChatAdapter.setCurrentStreamingPosition(aiMessagePosition); mChatAdapter.notifyItemInserted(aiMessagePosition); // 重置流式数据累积器 mStreamContentBuilder.setLength(0); // 清空输入框 viewBinding.editInput.setText(""); // 设置UI状态 viewBinding.imgSend.setVisibility(View.GONE); viewBinding.imgStop.setVisibility(View.VISIBLE); // 发送消息后自动滚动到底部 scrollToBottomAfterSend(); // 发起流式请求 mPresenter.talkToAi(senMsg, mSelectTypeID, mSessionId); } @Override public IndexPresenter createPresenter() { return new IndexPresenter(new IndexImpl()); } @Override public void getTalkResultSuccess(ChatAiMsgEntity bean) { hideDialogLoading(); } /** * 首页数据获取成功 * * @param list */ @Override public void getIndexTemplateSuccess(List list) { if (list != null && list.size() != 0) { mIndexTemList = list; mOptionAdapter.setNewData(mIndexTemList); setAdapterClick(); setOptionSelStyle(0); mSelectTypeID = mIndexTemList.get(0).getId(); } } /** * 切换设置显示内容 * * @param pos */ private void setOptionSelStyle(int pos) { try { mChatMsgList.clear(); mChatAdapter.setNewData(mChatMsgList); // 清理缓存 mChatAdapter.clearCache(); // viewBinding.linearOptionHint.setVisibility(View.VISIBLE); IndexTemplateEntity defaulEntity = mIndexTemList.get(pos); viewBinding.tvTitleExpert.setText(defaulEntity.getTitle() + "专家"); viewBinding.tvSelectOptionLable.setText(defaulEntity.getTitle() + "专家"); viewBinding.tvSelectOptionSlogan.setText(defaulEntity.getSubheading()); if (ColorManage.getInstance().isParsableColor(defaulEntity.getColor())) { viewBinding.tvSelectOptionSlogan.setTextColor(Color.parseColor(defaulEntity.getColor())); } else { viewBinding.tvSelectOptionSlogan.setTextColor(ContextCompat.getColor(mContext, R.color.color_zhu)); } //提示语 List hintMsgList = new ArrayList<>(); hintMsgList = Arrays.asList(defaulEntity.getIntermediateContent().split("#")); mHintAdapter.setNewData(hintMsgList); //快速提问 mQuickAskList.clear(); List quickAskList = new ArrayList<>(); quickAskList = Arrays.asList(defaulEntity.getQuickQuestions().split("#")); for (String content : quickAskList) { IndexTemplateEntity entity = new IndexTemplateEntity(); entity.setColor(defaulEntity.getColor()); entity.setBlackcolor(defaulEntity.getBlackcolor()); entity.setSpliteItemContent(content); mQuickAskList.add(entity); } mQuickAskAdapter.setNewData(mQuickAskList); } catch (Exception e) { } } @Override public void onNetChange(int netModel) { } @Override public void showError(String message) { hideDialogLoading(); ToastUtil.showShort(mContext, message); // 流式传输完成,恢复UI状态 viewBinding.imgSend.setVisibility(View.VISIBLE); viewBinding.imgStop.setVisibility(View.GONE); mIsStreamStop = true; // 可以在这里处理会话ID等 if (mCurrentStreamEntity != null && !TextUtils.isEmpty(mCurrentStreamEntity.getSessionId())) { mSessionId = mCurrentStreamEntity.getSessionId(); } // 清理当前流式实体 mCurrentStreamEntity = null; mStreamContentBuilder.setLength(0); } @Override public void logOut(BaseBean bean) { } // 实现流式消息接收回调 @Override public void onStreamMessageReceived(String messageChunk) { mIsStreamStop = false; if (mCurrentStreamEntity != null) { // 更新当前AI消息的内容 mCurrentStreamEntity.setContent(messageChunk); // 取消之前的更新任务 if (mUpdateRunnable != null) { mUpdateHandler.removeCallbacks(mUpdateRunnable); } mUpdateRunnable = () -> { // 只更新最后一个item int lastPosition = mChatMsgList.size() - 1; if (lastPosition >= 0) { // 使用更轻量的更新方式,避免重新布局 mChatAdapter.notifyItemChanged(lastPosition, "content_only"); // 流式输出时,如果用户没有手动滑动,则自动滚动NestedScrollView if (!mIsUserScrolling) { smoothScrollNestedScrollView(); } } }; // 增加延迟时间,减少更新频率 mUpdateHandler.postDelayed(mUpdateRunnable, 100); } } // 实现流式完成回调 @Override public void onStreamComplete(String complateMsg) { // 流式传输完成,恢复UI状态 viewBinding.imgSend.setVisibility(View.VISIBLE); viewBinding.imgStop.setVisibility(View.GONE); mIsStreamStop = true; // 清除当前流式输出位置 mChatAdapter.setCurrentStreamingPosition(-1); // 注意:这里不标记为停止状态,因为这是正常完成 // 可以在这里处理会话ID等 if (mCurrentStreamEntity != null && !TextUtils.isEmpty(mCurrentStreamEntity.getSessionId())) { mSessionId = mCurrentStreamEntity.getSessionId(); } // 清理当前流式实体 mCurrentStreamEntity = null; mStreamContentBuilder.setLength(0); // 假设你最后一个消息是流式的 placeholder,现在替换它 ChatMultiItem lastMessage = mChatMsgList.get(mChatMsgList.size() - 1); lastMessage.getMessage().setContent(complateMsg); // 你可以解析 Markdown 变 PlainText,或保留 Markdown 渲染 // mChatAdapter.notifyItemChanged(mChatMsgList.size() - 1); // 更新 UI int lastPos = mChatAdapter.getItemCount() - 1; mChatAdapter.updateTipsVisibility(lastPos); } /** * 根据会话 id 查询对话详情 * * @param list */ @Override public void getChatDetailSuccess(List list) { mChatMsgList.clear(); if (list != null && list.size() != 0) { mSessionId = list.get(0).getSessionId(); } for (ChatAiMsgEntity entity : list) { ChatMultiItem item; if (entity.getRole().equals("user")) { item = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_RIGHT, entity); } else { item = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_LEFT, entity); } mChatMsgList.add(item); } mChatAdapter.setNewData(mChatMsgList); viewBinding.linearOptionHint.setVisibility(View.GONE); } @Override public void loginLost() { if (isFragmentVisible()) { handleLoginLost(); } stopSteamOutput(); if (mChatAdapter != null) { mChatAdapter.remove(mChatMsgList.size() - 1); } } /** * 获取咨询模板成功 * * @param list */ @Override public void getChatZixunSuccess(List list) { this.mQuesTypeList = list; if (list != null) { mZixunQuestAdapter.setNewData(list); mQuesDetailAdapter.setNewData(list.get(0).getSubheadings()); setAdapterClick(); } } /** * 举报成功 * @param bean */ @Override public void commitReportSuccess(BaseBean bean) { ToastUtil.showShort(mContext, "星语明灯感谢您的反馈"); hideDialogLoading(); } // 实现sessionId接收回调 @Override public void onSessionReceived(String sessionId) { // 保存sessionId mSessionId = sessionId; } @Override public void getHistoryDataSuccess(CharHistoryEntity entity) { } @Override public void deleteHistorySuccess(BaseBean bean, int delPos) { } /** * 平滑滚动NestedScrollView到底部 */ private void smoothScrollNestedScrollView() { if (mChatAdapter != null && mChatAdapter.getItemCount() > 0) { // 直接滚动NestedScrollView到底部 viewBinding.nestedScrollChat.post(() -> { viewBinding.nestedScrollChat.fullScroll(View.FOCUS_DOWN); }); } } /** * 发送消息后滚动到底部 */ private void scrollToBottomAfterSend() { new Handler().postDelayed(() -> { if (isFragmentVisible()) { viewBinding.nestedScrollChat.fullScroll(View.FOCUS_DOWN); } }, 100); } @Override public void onPermissionsGranted(int requestCode, @NonNull List perms) { } @Override public void onPermissionsDenied(int requestCode, @NonNull List perms) { } }