Android RecyclerView中ChatGPT式聊天界面的播放/暂停图标切换问题
RecyclerView中TTS播放/暂停图标切换实现方案
核心思路
- 给聊天消息数据模型添加播放状态标记,用于控制图标显示
- 绑定TTS的UtteranceProgressListener,通过唯一标识关联列表对应item
- 点击播放按钮时,处理状态切换、TTS控制及其他item的状态重置
- 仅刷新状态变化的单个item,提升性能
具体实现步骤
1. 扩展聊天消息数据模型
在你的ChatMessage类中添加isPlaying字段,标记当前消息是否正在播放:
data class ChatMessage( val content: String, val isAiMessage: Boolean, var isPlaying: Boolean = false // 新增播放状态字段 )
2. 改造ChatAdapter
在Adapter中处理播放按钮点击事件与状态更新逻辑:
class ChatAdapter(private val tts: TextToSpeech) : RecyclerView.Adapter<ChatAdapter.ChatViewHolder>() { private var messageList = mutableListOf<ChatMessage>() private var currentPlayingPosition = -1 // 记录当前播放的item位置 fun setData(newList: List<ChatMessage>) { messageList.clear() messageList.addAll(newList) notifyDataSetChanged() } inner class ChatViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val playBtn: ImageView = itemView.findViewById(R.id.play_btn) val messageText: TextView = itemView.findViewById(R.id.message_text) fun bind(message: ChatMessage, position: Int) { // 根据播放状态切换图标 playBtn.setImageResource(if (message.isPlaying) R.drawable.ic_pause else R.drawable.ic_play) playBtn.setOnClickListener { if (message.isPlaying) { // 暂停播放 tts.stop() updatePlayingState(position, false) } else { // 停止之前正在播放的item if (currentPlayingPosition != -1) { updatePlayingState(currentPlayingPosition, false) } // 开始播放当前item updatePlayingState(position, true) val params = HashMap<String, String>() params[TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID] = position.toString() tts.speak(message.content, TextToSpeech.QUEUE_FLUSH, params) } } } } private fun updatePlayingState(position: Int, isPlaying: Boolean) { messageList[position].isPlaying = isPlaying notifyItemChanged(position) currentPlayingPosition = if (isPlaying) position else -1 } // 省略onCreateViewHolder、getItemCount等基础重写方法 }
3. 配置TTS的UtteranceProgressListener
在初始化TTS的位置,设置监听器处理播放完成/中断时的状态重置:
val tts = TextToSpeech(context) { status -> if (status == TextToSpeech.SUCCESS) { tts.language = Locale.CHINESE tts.setOnUtteranceProgressListener(object : UtteranceProgressListener() { override fun onStart(utteranceId: String?) { utteranceId?.toIntOrNull()?.let { position -> Handler(Looper.getMainLooper()).post { updatePlayingState(position, true) } } } override fun onDone(utteranceId: String?) { utteranceId?.toIntOrNull()?.let { position -> Handler(Looper.getMainLooper()).post { updatePlayingState(position, false) } } } override fun onError(utteranceId: String?) { utteranceId?.toIntOrNull()?.let { position -> Handler(Looper.getMainLooper()).post { updatePlayingState(position, false) } } } }) } }
关键注意事项
- 必须在主线程更新UI,所以UtteranceProgressListener回调中要通过Handler切换线程
- 用
currentPlayingPosition避免多个item同时显示播放状态 - 使用
notifyItemChanged(position)替代全量刷新,减少UI性能消耗 - 在页面销毁时调用
tts.shutdown()释放TTS资源
内容的提问来源于stack exchange,提问作者Tushal Mer
相关产品推荐
相关产品推荐

