Android TTS学习——继续爱的表白
一.?简单介绍
在上一篇里
http://blog.csdn.net/ichliebephone/archive/2010/08/10/5802739.aspx
?
我们讲到了TTS?最主要的一个API?:
public int speak ( String???text, int queueMode,???HashMap < String ,???String > params)
其中我们介绍了前两个参数,第三个参数设置了null?,并没有具体介绍。
而且我们在介绍Android TTS?提供的功能时,说到TTS?提供了两个接口,第一个初始化接口OnInitListener?我们已经使用过了,而第二个接口OnUtteranceCompletedListener?我们还没有使用到。
首先我们就来简单介绍一下这个接口的使用方式。TTS?引擎进行语音合成的每个语音片段叫做"utterance ",?而且在送给speak?函数进行语音合成时,可以为这次的语音片段"utterance "设置一个ID?。OnUtteranceCompletedListener?这个接口里定义了一个回调函数,当设置了ID?的语音片段"utterance "?语音合成结束后就会调用这个回调函数。然后我们就可以在这回调函数里进行我们需要的操作,比如在某个特定ID?的语音片段结束后播放音乐,或者跟踪记录哪些文本内容已经被朗读等。
???????下面利用这个接口的功能完成一个Demo?例子,朗读一段爱的表白,并且在程序退出后可以记住这次表白到哪了,下次在她再次打开程序时,就可以继续这爱的表白。
?
二.?实例分析
我们希望做的效果如下:
?
图1?实现效果图
?
在界面上体现不出这个Demo?的特点,除了一长串英文的爱的表白外,还有一个Speak?按钮。但当点击了Speak?按钮开始朗读上面的表白后,在没有读完就退出程序后,下次再启动后还会接着上次的继续朗读。
创建一个Android?工程,工程名为AndroidTTSDemoThird,?其中SDK?必须选择1.6?版本及以上。
其中Main.xml?文件很简单,如下所示:
?
- <?xml?version="1.0"?encoding="utf-8"?>??
- <LinearLayout?xmlns:android="http://schemas.android.com/apk/res/android"??
- ????android:orientation="vertical"??
- ????android:layout_width="fill_parent"??
- ????android:layout_height="fill_parent"??
- ????>??
- ????<EditText?android:id="@+id/inputText"???
- ????????????????android:hint="Input?the?text?here!"???
- ????????????????android:layout_width="fill_parent"???
- ????????????????android:layout_height="wrap_content">??
- ????????????????</EditText>??
- ????<Button?android:text="Speak"???
- ????????????????android:id="@+id/speakBtn"???
- ????????????????android:layout_width="wrap_content"???
- ????????????????android:layout_height="wrap_content"??
- ????????????????android:layout_gravity="center_horizontal"??
- ????????????????android:enabled="false"??
- ????????????????></Button>??
- ??????????????????
- </LinearLayout>??
?
?
Java?文件的编写:
这次要实现?OnInitListener?和?OnUtteranceCompletedListener?两个接口
?
- public?class?AndroidTTSDemoThird?extends?Activity?implements?OnInitListener,?OnUtteranceCompletedListener{??
- ????/**?Called?when?the?activity?is?first?created.?*/??
- ????@Override??
- ????public?void?onCreate(Bundle?savedInstanceState)?{??
- ????????super.onCreate(savedInstanceState);??
- ????????setContentView(R.layout.main);??
- ????}??
- ??????
- ????@Override??
- ????public?void?onInit(int?status)?{??
- ????????//?TODO?Auto-generated?method?stub??
- ????????//TTS?Engine初始化完成??
- ??????????
- ????}??
- ????@Override??
- ????public?void?onUtteranceCompleted(String?utteranceId)?{??
- ????????//?TODO?Auto-generated?method?stub??
- ????????//一个语音片段结束后的回调函数??
- ??????????
- ????}??
- }??
?
?
接着定义好下面要用到的几个变量:
?
- //定义变量??
- ????private?EditText?inputText?=?null;??
- ????private?Button?speakBtn?=?null;??
- ????private?TextToSpeech?mTts;??
- ????private?static?final?String?TAG?=?"TTS?Demo";??
- ????private?static?final?String?STORE_NAME?=?"preferenceFile";??
- ????private?static?final?String?loveConfession?=?"You?complete?me.?You?had?me?at?hello,?you?had?me?at?hello.?To?the?world?you?maybe?one?person,?but?to?one?person?you?maybe?the?world.?Please?believe?me,?I?was?prepared?for?everything,except?you.?I?love?that?you?are?the?last?person?I?want?to?talk?to?before?I?go?to?sleep?at?night.?Love?means?never?having?to?say?you're?sorry.?So?choose?me.?Marry?me.?Let?me?make?you?happy.?";??
- ????private?String[]?loveArray;???
- ????private?int?lastUtterance?=?-1;??
- ????private?HashMap<String,?String>?params?=?new?HashMap<String,?String>();??
?
?
因为通过前面的两个?Demo?,我们知道已经安装了?TTS?需要的数据,因此这里我们可以在onCreate?函数中直接创建一个?TextToSpeech?实例,
?
- //创建TextToSpeech实例,初始化完成后会调用OnInitListener(第二个参数)的回调函数??
- mTts?=?new?TextToSpeech(this,??
- ????????this??//?TextToSpeech.OnInitListener??
- ????????);??
?
?
成功创建一个?TextToSpeech?后,就会调用接口?OnInitListener?中定义的回调函数
?
- @Override??
- public?void?onInit(int?status)?{??
- ????//?TODO?Auto-generated?method?stub??
- ????//TTS?Engine初始化完成??
- ????if(status?==?TextToSpeech.SUCCESS)??
- ????{??
- ????????int?result?=?mTts.setLanguage(Locale.US);??
- ????????//设置发音语言??
- ????????if(result?==?TextToSpeech.LANG_MISSING_DATA?||?result?==?TextToSpeech.LANG_NOT_SUPPORTED)??
- ????????//判断语言是否可用??
- ????????{??
- ????????????Log.v(TAG,?"Language?is?not?available");??
- ????????????speakBtn.setEnabled(false);??
- ????????}??
- ????????else??
- ????????{??
- ????????????speakBtn.setEnabled(true);??
- ????????????//设置一个语音片段结束后的回调函数??
- ????????????mTts.setOnUtteranceCompletedListener(this);??
- ????????}??
- ????}?????
- }??
?
?
和前面的?Demo?相比,这里多了一句?mTts?.setOnUtteranceCompletedListener(?this?);
就是设置语音片段结束后的回调函数。
?
接着在?onCreate?函数中设置?EditText?和?Button?的使用:
?
- ??????//设置控件??
- ??????inputText?=?(EditText)findViewById(R.id.inputText);??
- ??????speakBtn?=?(Button)findViewById(R.id.speakBtn);??
- ???????
- ???inputText.setText(loveConfession);????
- ??????speakBtn.setOnClickListener(new?OnClickListener()?{?????????
- ????public?void?onClick(View?v)?{??
- ????????//?TODO?Auto-generated?method?stub??
- ????????//处理输入框里的内容??
- ????????StringTokenizer?loveTokens?=?new?StringTokenizer(inputText.getText().toString(),",.");??
- ????????int?i?=?0;??
- ????????loveArray?=?new?String[loveTokens.countTokens()];??
- ????????while(loveTokens.hasMoreTokens())??
- ????????{??
- ????????????loveArray[i++]?=?loveTokens.nextToken();??
- ????????}??
- ????????//朗读输入框里的内容??
- ????????speakText();??
- ????}??
- });??
?
?
在?SpeakBtn?的?onClick?事件中,先把输入框中的内容按?','?和?'.'?分割成一个个短的部分并保存在字符串数组中,然后调用我们写的?speakText()?函数来进行朗读。
speakText()?函数的定义如下:
?
- private?void?speakText()??
- {??
- ????lastUtterance++;??
- ????if(lastUtterance?>=?loveArray.length)??
- ????{??
- ????????lastUtterance?=?0;??
- ????}??
- ????Log.v(TAG,?"the?begin?utterance?is?"?+?lastUtterance);??
- ????for(int?i?=?lastUtterance;?i?<?loveArray.length;?i++)??
- ????{??
- ????????//为每个语音片段都设置一个标记??
- ????????params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,?String.valueOf(i));??
- ????????mTts.speak(loveArray[i],?TextToSpeech.QUEUE_ADD,?params);??
- ????}??
- }??
?
?
lastUtterance?变量保存上一个语音片段的?ID?,?params?是?HashMap<String, String>?类型,键值对形式的变量,在这里我们的键?name?为?TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,是一个?UTTERANCE_ID?,值?value?为?String.valueOf(i)?,其实只要是任何字符串变量都行,为了方便我们设置的为有顺序的数字。然后把这个键值对变量添加给?speak?函数,?TTS?引擎就会在这个语音片段朗读结束后通知我们,调用设置好的回调函数?onUtteranceCompleted(String utteranceId)?,这个回调函数的内容如下:
?
- @Override??
- public?void?onUtteranceCompleted(String?utteranceId)?{??
- ????//?TODO?Auto-generated?method?stub??
- ????//一个语音片段结束后的回调函数??
- ????Log.v(TAG,?"Get?completed?message?for?the?utteranceId?"?+?utteranceId);??
- ????//用lastUtterance记录当前结束的语音片段??
- ????lastUtterance?=?Integer.parseInt(utteranceId);??
- }??
?
?
很简单,主要就是把当前刚结束的这个语音片段?ID--utteranceId?保存到?lastUtterance?变量中,这样在程序退出时,我们就可以保存这个变量,
?
- @Override??
- protected?void?onDestroy()?{??
- ????//?TODO?Auto-generated?method?stub??
- ????//释放TTS的资源??
- ????if(mTts?!=?null)??
- ????{??
- ????????mTts.stop();??
- ????????mTts.shutdown();??
- ????}??
- ????//保存lastUtterance值??
- ????SharedPreferences?settings?=?getSharedPreferences(STORE_NAME,?0);??
- ????SharedPreferences.Editor?editor?=?settings.edit();??
- ????editor.putInt("lastUtterance",?lastUtterance);??
- ????editor.commit();??
- ????Log.v(TAG,?"the?stored?lastUtterance?is?"?+?lastUtterance);??
- ??????
- ????super.onDestroy();??
- }??
?
?
首先是释放?TTS?资源,然后把?lastUtterance?变量的值以键值对的方式保存在系统中,这样下次启动程序时?lastUtterance?就可以从系统中读取上次退出时保存的值。在?onCreate?函数中添加获取保存的?lastUtterance?代码:
?
- //获得保存的lastUtterance值??
- SharedPreferences?settings?=?getSharedPreferences(STORE_NAME,?0);??
- lastUtterance?=?settings.getInt("lastUtterance",?-1);??
- Log.v(TAG,?"the?restored?lastUtterance?is?"?+?lastUtterance);??
?
?
至此,这个?Demo?完成了,运行后点击?Speak?按钮会朗读文本框中的经典的爱的表白,一小段一小段朗读,每朗读完一小段程序都会调用回调函数?onUtteranceCompleted?记录刚朗读完的那个小段的?ID?,当所有的表白没朗读完就按返回退出程序时,程序在?onDestroy()?里把记录着的片段?ID保存在系统中,这样下次再次打开程序时就可以接着上次继续这爱的表白。
?
注:文章参加“?首届Google暑期大学生博客分享大赛——2010 Andriod篇”