You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 00:52:35