如何在Jetpack Compose中用Slider跟踪TTS朗读进度?
解决Jetpack Compose中TTS朗读进度与Slider关联的问题
核心问题在于Compose依赖状态驱动UI,Slider的进度必须绑定Compose状态变量,同时TTS的进度更新要触发状态变化,从而让Compose自动重组更新UI。以下是具体实现步骤:
1. 定义Compose状态变量
用mutableStateOf存储进度比例和总时长,确保进度变化时UI自动更新:
@Composable fun TtsPlayer() { val currentProgressRatio = remember { mutableStateOf(0f) } val totalDurationMs = remember { mutableStateOf(1f) } // 默认值避免除以0 // 其他代码... }
2. 实现Slider组件
将Slider的value绑定到状态变量,同时支持拖动跳转朗读位置:
Slider( value = currentProgressRatio.value, onValueChange = { newRatio -> currentProgressRatio.value = newRatio // 拖动时跳转TTS到对应位置 ttsManager.seekTo((newRatio * totalDurationMs.value).toInt()) }, valueRange = 0f..1f, modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp) )
3. 修改TTS管理器,添加进度回调
利用TTS的playbackPosition(API 26+)实时获取进度,通过回调通知Compose更新状态:
class TtsManager(private val context: Context) { private var tts: TextToSpeech? = null private var progressCallback: ((currentMs: Float, totalMs: Float) -> Unit)? = null private val progressHandler = Handler(Looper.getMainLooper()) private var progressRunnable: Runnable? = null fun init() { tts = TextToSpeech(context) { status -> if (status == TextToSpeech.SUCCESS) { // 初始化成功,可设置语言等 } } } fun setProgressCallback(callback: ((Float, Float) -> Unit)?) { progressCallback = callback } fun speak(text: String) { val params = hashMapOf(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID to "tts_utterance") tts?.speak(text, TextToSpeech.QUEUE_FLUSH, params, "tts_utterance") // 启动定时进度更新 progressRunnable = object : Runnable { override fun run() { val current = tts?.playbackPosition ?: 0 val total = tts?.totalPlaybackDuration ?: 1 // 主线程更新Compose状态 progressCallback?.invoke(current.toFloat(), total.toFloat()) if (current < total && tts?.isSpeaking == true) { progressHandler.postDelayed(this, 100) // 每100ms更新一次 } } } progressRunnable?.let { progressHandler.post(it) } } fun seekTo(positionMs: Int) { tts?.seekTo(positionMs) } fun stop() { tts?.stop() progressRunnable?.let { progressHandler.removeCallbacks(it) } } fun shutdown() { tts?.shutdown() progressHandler.removeCallbacksAndMessages(null) } }
4. 在Compose中关联TTS与状态
通过LaunchedEffect设置TTS的进度回调,将实时进度转换为比例更新状态:
@Composable fun TtsPlayer() { val context = LocalContext.current val ttsManager = remember { TtsManager(context).apply { init() } } val currentProgressRatio = remember { mutableStateOf(0f) } val totalDurationMs = remember { mutableStateOf(1f) } // 绑定进度回调 LaunchedEffect(Unit) { ttsManager.setProgressCallback { currentMs, totalMs -> currentProgressRatio.value = currentMs / totalMs totalDurationMs.value = totalMs } } Column(modifier = Modifier.padding(16.dp)) { Slider(/* 之前的Slider代码 */) Button(onClick = { ttsManager.speak("需要朗读的大段文本内容") }) { Text("开始朗读") } Button(onClick = { ttsManager.stop() }) { Text("停止朗读") } } // 页面销毁时释放TTS资源 DisposableEffect(Unit) { onDispose { ttsManager.shutdown() } } }
关键注意事项
- API兼容性:
playbackPosition和totalPlaybackDuration仅支持Android 8.0(API 26)及以上,低版本需通过文本长度、朗读速度估算进度(精度会降低)。 - 主线程更新:状态变量必须在主线程更新,上述代码通过
Handler(Looper.getMainLooper())确保这一点。 - 资源释放:页面销毁时务必调用
ttsManager.shutdown()并移除Handler回调,避免内存泄漏。
内容的提问来源于stack exchange,提问作者A. Cedano
相关产品推荐
相关产品推荐

