【问题标题】:TextToSpeech in non-activity class/View Adapter class, speak failed: not bound to TTS engine非活动类/视图适配器类中的 TextToSpeech,说话失败:未绑定到 TTS 引擎
【发布时间】:2025-12-03 05:10:01
【问题描述】:

我已经使用 Tabbed-Fragments 中的 RecyclerView 为我的应用开发了一个模块。

场景

使用 RecyclerView 在每个片段中显示多个动态加载的图像。当用户点击任何图像时,系统会使用 TextToSpeech 说出一些关于它的信息。

当前代码

我正在使用 RecyclerView 的适配器类,它成功地将图像加载到视图中。代码如下:

import android.speech.tts.TextToSpeech;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private ArrayList<CreateList> galleryList;
private Context context;
TextToSpeech tts;

public MyAdapter(Context context, ArrayList<CreateList> galleryList) {
    this.galleryList = galleryList;
    this.context = context;
}

@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
.....
}

@Override
public void onBindViewHolder(MyAdapter.ViewHolder viewHolder, int i) {
.....
.....
viewHolder.img.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        .....
        .....
        tts=new TextToSpeech(context, new TextToSpeech.OnInitListener() {

                    @Override
                    public void onInit(int status) {
                        // TODO Auto-generated method stub
                        if(status !=TextToSpeech.ERROR){
                            tts.setLanguage(Locale.UK);
                        }
                    }
                });
        tts.setPitch(pitch);
        tts.setSpeechRate(speechRate);
        tts.speak(StringToSpeak, TextToSpeech.QUEUE_FLUSH, null);
}

@Override
public int getItemCount() {
    return galleryList.size();
}
.....
.....
}

如您所见,我在 viewHolder 上添加了一个 OnClickListener,它使用 onInitListener 初始化 TTS 对象并说出信息。但是,每当我单击片段中的任何图像/视图/项目时,它都不会说出任何内容。 LogCat 中没有崩溃,也没有例外,我得到的只是以下消息和其他消息:

I/TextToSpeech: Sucessfully bound to com.google.android.tts
W/TextToSpeech: speak failed: not bound to TTS engine

我尝试调试应用程序,发现在初始化 TTS 对象时,它在这一行返回 engine=Null

tts=new TextToSpeech(context, new TextToSpeech.OnInitListener() {
....});

在扩展 Activity 的其他类中,代码和 TTS 工作正常,但在我的 Adapter/Non-Activity 类中,它不会启动 TTS,因为它无法绑定到 TTS 引擎。我尝试在我的适配器类中使用以下功能实现接口implements TextToSpeech.OnInitListener

@Override
public void onInit(int status) {

    if (status == TextToSpeech.SUCCESS) {
        tts.setLanguage(Locale.UK);
    }
}

但没有结果 :( 我还尝试创建一个扩展 Activity 的抽象类,它在其 OnCreate() 中实现 TTS 并包含一个自定义函数 SpeakMessage(),我尝试在我的适配器类中调用它但失败了。它一直几个小时我试图找出问题及其解决方案,深入研究了 * 和其他网站上的每个 TTS 相关问题,但找不到任何解决我的问题的方法。请帮助我确定问题及其正确的解决方案。谢谢提前很多。另一件事是,在 Views 的同一动作侦听器中,使用 MediaPlayer 播放录制的音频消息效果很好。唯一的问题是使用 TTS 说出字符串消息。

【问题讨论】:

  • 你可以查看我的答案here...

标签: android adapter actionlistener text-to-speech google-text-to-speech


【解决方案1】:

我已经成功实现了文本转语音

下面是我的代码

我已经实现了两种方法,一种用于 21api

@SuppressWarnings("deprecation")
private void ttsUnder20(String text) {
    HashMap<String, String> map = new HashMap<>();
    map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "MessageId");
    tts.speak(text, TextToSpeech.QUEUE_FLUSH, map);
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void ttsGreater21(String text) {
    String utteranceId = this.hashCode() + "";
    Bundle params = new Bundle();
    params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "");
   tts.speak(text, TextToSpeech.QUEUE_FLUSH, params, utteranceId);
}

这里是调用这个方法的方法。

 private void playNextChunk(String text) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        ttsGreater21(text);
    } else {
        ttsUnder20(text);
    }}

从onInit方法调用playChunk方法

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onInit(int status) {

    if (status == TextToSpeech.SUCCESS) {
       playNextChunk(String text)
      }
   }

如果您有超过 4000 个字符的字符串,您需要分块播放,另外一个建议文本到语音可以一次说出 4000 个字符。为此,您需要实现此侦听器 tts.setOnUtteranceProgressListener

【讨论】:

  • 字符
【解决方案2】:

试试这个代码,我已经在你的适配器中添加了代码。

public class MyAdapter extends  
    RecyclerView.Adapter<MyAdapter.ViewHolder>{
    private ArrayList<CreateList> galleryList;
    private Context context;
    TextToSpeech tts;
    public MyAdapter(Context context, ArrayList<CreateList> galleryList) {
    this.galleryList = galleryList;
    this.context = context;
}

@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
.....
}

@Override
public void onBindViewHolder(MyAdapter.ViewHolder viewHolder, int i) {
.....
.....
viewHolder.img.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        .....
        .....
        tts=new TextToSpeech(context, new TextToSpeech.OnInitListener() {

                    @Override
                    public void onInit(int status) {
                        // TODO Auto-generated method stub
                        // edit from original answer: I put double equal on this line
                        if(status == TextToSpeech.SUCCESS){
                            tts.setLanguage(Locale.UK);
                            playNextChunk(StringToSpeak);
                        }
                    }
                });

}
    private void playNextChunk(String text) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ttsGreater21(text);
        } else {
            ttsUnder20(text);
        }}
    @SuppressWarnings("deprecation")
    private void ttsUnder20(String text) {
        HashMap<String, String> map = new HashMap<>();
        map.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "MessageId");
        tts.speak(text, TextToSpeech.QUEUE_FLUSH, map);
    }
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void ttsGreater21(String text) {
        String utteranceId = this.hashCode() + "";
        Bundle params = new Bundle();
        params.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "");
        tts.speak(text, TextToSpeech.QUEUE_FLUSH, params, utteranceId);
    }
    @Override
public int getItemCount() {
    return galleryList.size();
}
.....
.....
}

【讨论】:

  • 你能解释一下你传递给两个speak函数的参数吗
  • Params :Bundle:请求的参数。可以为空。支持的参数名称:KEY_PARAM_STREAM、KEY_PARAM_VOLUME、KEY_PARAM_PAN。可以传入引擎特定的参数,但参数键必须以它们所使用的引擎名称作为前缀。例如,如果正在使用键“com.svox.pico_foo”和“com.svox.pico:bar”,则将传递给名为“com.svox.pico”的引擎。 utteranceId :String: 此请求的唯一标识符。