失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android TTS实现简单阅读器(一)

Android TTS实现简单阅读器(一)

时间:2021-09-03 08:59:34

相关推荐

Android TTS实现简单阅读器(一)

Android TTS实现简单阅读器

简单的Txt文本阅读器,主要用于介绍Google Android的TTS接口。

一、TTS

在package android.speech.tts内,主要阅读下TextToSpeech.OnInitListener、TextToSpeech. OnUtteranceCompletedListener两个接口和TextToSpeech、TextToSpeech.Engine两个类。

具体还是自己去看下SDK文档吧。(我也是完整阅读过了的^^) 二、TTS引擎 以前在网上的例子,或者就我《Android基础样例》里的中文TTS例子,都是eSpeak引擎实现的。这种方式是要用其封装的TTS接口,再通过下载TTS数据使用。 而Android的SDK中还提供了TTS服务的接口,用于供应商提供服务的。也就是语音合成服务商只管提供它的服务,开发者只管使用Android的TTS接口,用户自己安装想要的服务自己进行选择。

总之呢,我用的是讯飞语音TTS v1.0。有两个文件,一个是Service程序,一个是语音数据。下载网址:/down/22160.html

1)关于讯飞(貌似广告?) 好吧,少说点了,它也提供了个开发者平台。如下: 讯飞语音云:/download.php?vt=1 有试了下它那语音分析,话说,弹出的框框能不能好看点啊。(做个小话筒就好了么T^T) 恩,还有,现在讯飞是要开始宣传了么?貌似3月22日什么开发者大会-_-!(又广告了?) 2)其他中文引擎 参见文章:Android中文语音合成(TTS)各家引擎对比。(原网址打不开==,另外的网址就不贴了,搜下吧) 三、阅读器工程现在学乖了,直接贴些代码得了==。代码中注释应该满清晰详细了^^。 1)界面布局

布局由main.xml includes header.xml & footer.xml组成,并写有了定时收起等。

TtsFatherActivity.java

/***1)本类用于main.xml的布局控制。子类再去实现各控件的TTS相关功能。*2)用继承方式实现是为了利用布局中控件的onClick属性(懒得多写代码==!)。*/publicabstractclassTtsFatherActivityextendsActivity{privateGestureDetectorgd;//手势检测privateGlobalUtilglobalUtil;//全局公用类privateScrollViewscrollView;//滚动视图privateLinearLayoutheaderLayout,footerLayout;//顶部、底部布局privateTextViewtextView;//文本标签privatestaticfinallongANIM_DURATION=500;//动画时间(毫秒)privatestaticfinalintDIALOG_TEXT_LIST=0;//文本列表对话框idprivatefinalString[]textPaths=newString[]{"one.txt","two.txt","浏览..."};//assets内文本资源路径protectedStringtextTitle;//文本标题protectedStringtextContent;//文本内容privateTimertimer;//计时器privatestaticfinallongTIMEOUT=2000;//超时时间privatestaticfinalintTIMER_LAYOUT_OUT=1;//布局收起privatebooleanisLayoutOut=false;//布局收起状态/**Handler处理操作*/publicHandlermHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg){switch(msg.what){caseTIMER_LAYOUT_OUT:/*headerLayout收起动画*/globalUtil.startTransAnim(headerLayout,GlobalUtil.AnimMode.UP_OUT,ANIM_DURATION);headerLayout.setVisibility(View.GONE);/*footerLayout收起动画*/globalUtil.startTransAnim(footerLayout,GlobalUtil.AnimMode.DOWN_OUT,ANIM_DURATION);footerLayout.setVisibility(View.GONE);isLayoutOut=true;//重置布局收起状态break;}}};@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);gd=newGestureDetector(newMySimpleGesture());//手势检测处理globalUtil=GlobalUtil.getInstance();//获取全局公用类scrollView=(ScrollView)findViewById(R.id.scrollView);//获取滚动视图headerLayout=(LinearLayout)findViewById(R.id.headerLayout);//获取顶部布局footerLayout=(LinearLayout)findViewById(R.id.footerLayout);//获取底部布局textView=(TextView)findViewById(R.id.textView);setText(0);//默认显示“上邪.txt”newTimerLayoutOut();//定时收起布局}/**使用GestureDetector检测手势(ScrollView内也需监听时的方式)*/@OverridepublicbooleandispatchTouchEvent(MotionEventev){gd.onTouchEvent(ev);scrollView.onTouchEvent(ev);returnsuper.dispatchTouchEvent(ev);}/**onCreateDialog*/@OverrideprotectedDialogonCreateDialog(intid){switch(id){caseDIALOG_TEXT_LIST:returnnewAlertDialog.Builder(this).setItems(textPaths,newDialogInterface.OnClickListener(){@OverridepublicvoidonClick(DialogInterfacedialog,intwhich){if(2==which){//跳转到文件浏览ActivitystartActivityForResult(newIntent(TtsFatherActivity.this,FileBrowserActivity.class),FileBrowserActivity.CODE_FILE_BROWSER);}else{setText(which);//设置文本内容}}}).create();}returnsuper.onCreateDialog(id);}@OverrideprotectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){if(requestCode==FileBrowserActivity.CODE_FILE_BROWSER){if(resultCode==RESULT_OK){//获得文件名称Stringfilename=data.getExtras().getString(FileBrowserActivity.KEY_FILENAME);this.textTitle=filename;try{//FileInputStreamfis=newFileInputStream(//newFile(filename));////FileInputStream不支持mark/reset操作,不该直接这样//Stringencoding=globalUtil.getIsEncoding(fis);//textContent=globalUtil.is2Str(fis,encoding);/***TXT简单判断编码类型后转字符串**ps:*1)扯淡,3.58MB的txt读出来了==*看来需要转成BufferedReader以readLine()方式读好些啊**2)TextView将大文本全部显示,这貌似...**时间主要花费在文本显示过程,不改进了,暂时将就吧==*2.1)用View自定义个控件显示文本也蛮久的,未减少多少时间。*2.2)至于AsyncTask,文本显示还是要在UI线程的==。**如果我们要仿个阅读器,用View自定义个控件还是必须的。*1)分段读取大文本,可以考虑3段(前后两段用于缓冲)*根据滑屏&显示内容等,注意文本显示衔接。*2)滚动条可以外面套个ScrollView。由各属性判断出大文本需要显示的高度,*重写onMeasure用setMeasuredDimension()设置好,才会有滚动条。*当然自己用scrollTo()、scrollBy()实现动画也是好的。*3)至于其他选中当前行啊什么的,慢慢写就成了...**不知道大家还有什么好的想法没?*///longtime1=System.currentTimeMillis();textContent=globalUtil.is2Str(newFileInputStream(newFile(filename)));//longtime2=System.currentTimeMillis();//Log.e("TAG1","=="+(time2-time1)+"==");textView.setText(textContent);//longtime3=System.currentTimeMillis();//Log.e("TAG1","=="+(time3-time2)+"==");}catch(Exceptione){textView.setText(R.string.text_error);textContent="";}}}}/**设置文本内容*/privatevoidsetText(inttextIndex){this.textTitle=textPaths[textIndex];try{textContent=globalUtil.is2Str(getAssets().open(textTitle),"UTF-8");textView.setText(textContent);}catch(IOExceptione){textView.setText(R.string.text_error);textContent="";}}/**定时收起布局(已定时时重新开始定时)*/protectedvoidnewTimerLayoutOut(){if(null!=timer){timer.cancel();}timer=newTimer();//超时TIMEOUT退出timer.schedule(newTimerTask(){@Overridepublicvoidrun(){mHandler.sendEmptyMessage(TIMER_LAYOUT_OUT);}},TIMEOUT);}/**自定义手势类*/privateclassMySimpleGestureextendsSimpleOnGestureListener{/**双击第二下*/@OverridepublicbooleanonDoubleTap(MotionEvente){if(isLayoutOut){/*headerLayout进入动画*/headerLayout.setVisibility(View.VISIBLE);globalUtil.startTransAnim(headerLayout,GlobalUtil.AnimMode.UP_IN,ANIM_DURATION);/*footerLayout进入动画*/footerLayout.setVisibility(View.VISIBLE);globalUtil.startTransAnim(footerLayout,GlobalUtil.AnimMode.DOWN_IN,ANIM_DURATION);newTimerLayoutOut();//定时收起布局isLayoutOut=false;//重置布局收起状态}else{/*headerLayout退出动画*/globalUtil.startTransAnim(headerLayout,GlobalUtil.AnimMode.UP_OUT,ANIM_DURATION);headerLayout.setVisibility(View.GONE);/*footerLayout退出动画*/globalUtil.startTransAnim(footerLayout,GlobalUtil.AnimMode.DOWN_OUT,ANIM_DURATION);footerLayout.setVisibility(View.GONE);//取消定时收起动画if(null!=timer){timer.cancel();}isLayoutOut=true;//重置布局收起状态}returnfalse;}/**长按屏幕时*/@OverridepublicvoidonLongPress(MotionEvente){//显示文本列表对话框showDialog(DIALOG_TEXT_LIST);}}} 2)TTS控制 音量&语速控制也写了的^^。 TtsSampleActivity.java

publicclassTtsSampleActivityextendsTtsFatherActivityimplementsOnSeekBarChangeListener,TextToSpeech.OnInitListener,TextToSpeech.OnUtteranceCompletedListener{//privatestaticfinalStringTAG="TtsSampleActivity";//日志标记privateAudioManageraudioManager;//音频管理对象//TTS音量类型(AudioManager.STREAM_MUSIC=AudioManager.STREAM_TTS=11)privatestaticfinalintSTREAM_TTS=AudioManager.STREAM_MUSIC;privateTextToSpeechmTts;//TTS对象privatestaticfinalintREQ_CHECK_TTS_DATA=110;//TTS数据校验请求值privatebooleanisSetting=false;//进入设置标记privatebooleanisRateChanged=false;//速率改变标记privatebooleanisStopped=false;//TTS引擎停止发声标记privatefloatmSpeechRate=1.0f;//朗读速率privateSeekBarvolumeBar,speedBar;//音量&语速//合成声音资源文件的路径privatestaticfinalStringSAVE_DIR_PATH="/sdcard/AndroidTTS/";privatestaticfinalStringSAVE_FILE_PATH=SAVE_DIR_PATH+"sound.wav";@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);//获得音频管理对象audioManager=(AudioManager)getSystemService(Context.AUDIO_SERVICE);/*volumeBar*/volumeBar=(SeekBar)findViewById(R.id.volumeBar);volumeBar.setOnSeekBarChangeListener(this);//由当前音量设置进度(需保证进度上限=音频上限=15,否则按比例设置)volumeBar.setProgress(audioManager.getStreamVolume(STREAM_TTS));/*speedBar*/speedBar=(SeekBar)findViewById(R.id.speedBar);speedBar.setOnSeekBarChangeListener(this);initDirs(SAVE_DIR_PATH);//初始化文件夹路径}/**saveFileBtn点击事件*/publicvoidsaveFile(Viewv){//将文本合成声音资源文件intresId=TextToSpeech.SUCCESS==ttsSaveFile(textContent,SAVE_FILE_PATH)?R.string.synt_success:R.string.synt_fail;Toast.makeText(this,resId,Toast.LENGTH_SHORT).show();//Toast提示newTimerLayoutOut();//重新定时收起布局}/**playFileBtn点击事件*/publicvoidplayFile(Viewv){ttsPlayFile(SAVE_FILE_PATH);//播放指定的使用文件newTimerLayoutOut();//重新定时收起布局}/**stopBtn点击事件*/publicvoidstop(Viewv){ttsStop();//停止当前发声newTimerLayoutOut();//重新定时收起布局}/**playBtn点击事件*/publicvoidplay(Viewv){ttsPlay();//tts合成语音播放newTimerLayoutOut();//重新定时收起布局}/**settingBtn点击事件*/publicvoidsetting(Viewv){//跳转到“语音输入与输出”设置界面&设置标志位isSetting=toTtsSettings();newTimerLayoutOut();//重新定时收起布局}/**SeekBar进度改变时*/@OverridepublicvoidonProgressChanged(SeekBarseekBar,intprogress,booleanfromUser){switch(seekBar.getId()){caseR.id.volumeBar://由设置当前TTS音量(需保证进度上限=音频上限=15,否则按比例设置)audioManager.setStreamVolume(STREAM_TTS,progress,0);break;caseR.id.speedBar:/*需要重新绑定TTS引擎,速度在onInit()里设置*/isRateChanged=true;//速率改变标记//最大值为20时,以下方式计算为0.5~2倍速mSpeechRate=(progress>=10)?(progress/10f):(0.5f+progress/20f);//校验TTS引擎安装及资源状态,重新绑定引擎checkTtsData();break;}newTimerLayoutOut();//重新定时收起布局}/**SeekBar开始拖动时*/@OverridepublicvoidonStartTrackingTouch(SeekBarseekBar){}/**SeekBar结束拖动时*/@OverridepublicvoidonStopTrackingTouch(SeekBarseekBar){}/***TTS引擎初始化时回调方法**引擎相关参数(音量、语速)等都需在这设置。*1)创建完成后再去设置,会有意外的效果^^*2)音量也可由AudioManager进行控制(和音乐一个媒体流类型)*/@OverridepublicvoidonInit(intstatus){if(status==TextToSpeech.SUCCESS){mTts.setSpeechRate(mSpeechRate);//设置朗读速率//设置发声合成监听,注意也需要在onInit()中做才有效mTts.setOnUtteranceCompletedListener(this);if(isRateChanged){ttsPlay();//tts合成语音播放isRateChanged=false;//重置标记位}}}/***TTS引擎完成发声完成时回调方法**1)stop()取消时也会回调*2)需在onInit()内设置接口*3)utteranceId由speak()时的请求参数设定*参数key:TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID*/@OverridepublicvoidonUtteranceCompleted(finalStringutteranceId){/*测试该接口的Toast提示*/runOnUiThread(newRunnable(){@Overridepublicvoidrun(){intresId=isStopped?R.string.utte_stopped:R.string.utte_completed;//提示文本发生完成Toast.makeText(getApplicationContext(),getString(resId,utteranceId),Toast.LENGTH_SHORT).show();}});}/**onActivityResult*/@OverrideprotectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){if(requestCode==REQ_CHECK_TTS_DATA){switch(resultCode){caseTextToSpeech.Engine.CHECK_VOICE_DATA_PASS://TTS引擎可用//针对于重新绑定引擎,需要先shutdown()if(null!=mTts){ttsStop();//停止当前发声ttsShutDown();//释放资源}mTts=newTextToSpeech(this,this);//创建TextToSpeech对象break;caseTextToSpeech.Engine.CHECK_VOICE_DATA_BAD_DATA://数据错误caseTextToSpeech.Engine.CHECK_VOICE_DATA_MISSING_DATA://缺失数据资源caseTextToSpeech.Engine.CHECK_VOICE_DATA_MISSING_VOLUME://缺少数据存储量notifyReinstallDialog();//提示用户是否重装TTS引擎数据的对话框break;caseTextToSpeech.Engine.CHECK_VOICE_DATA_FAIL://检查失败default:break;}}super.onActivityResult(requestCode,resultCode,data);}/**校验TTS引擎安装及资源状态*/privatebooleancheckTtsData(){try{IntentcheckIntent=newIntent();checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);startActivityForResult(checkIntent,REQ_CHECK_TTS_DATA);returntrue;}catch(ActivityNotFoundExceptione){returnfalse;}}/**提示用户是否重装TTS引擎数据的对话框*/privatevoidnotifyReinstallDialog(){newAlertDialog.Builder(this).setTitle("TTS引擎数据错误").setMessage("是否尝试重装TTS引擎数据到设备上?").setPositiveButton("是",newDialogInterface.OnClickListener(){@OverridepublicvoidonClick(DialogInterfacedialog,intwhich){//触发引擎在TTS引擎在设备上安装资源文件IntentdataIntent=newIntent();dataIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);startActivity(dataIntent);}}).setNegativeButton("否",null).show();}/**跳转到“语音输入与输出”设置界面*/privatebooleantoTtsSettings(){try{startActivity(newIntent("com.android.settings.TTS_SETTINGS"));returntrue;}catch(ActivityNotFoundExceptione){returnfalse;}}@OverrideprotectedvoidonStart(){checkTtsData();//校验TTS引擎安装及资源状态super.onStart();}@OverrideprotectedvoidonResume(){/*从设置返回后重新绑定TTS,避免仍用旧引擎*/if(isSetting){checkTtsData();//校验TTS引擎安装及资源状态isSetting=false;}super.onResume();}@OverrideprotectedvoidonStop(){/*HOME键*/ttsStop();//停止当前发声super.onStop();}@OverridepublicvoidonBackPressed(){/*BACK键*/ttsStop();//停止当前发声ttsShutDown();//释放资源super.onBackPressed();}/**tts合成语音播放*/privateintttsPlay(){if(null!=mTts){isStopped=false;//设置标记/***叙述text。**1)参数2(intqueueMode)*1.1)QUEUE_ADD:增加模式。增加在队列尾,继续原来的说话。*1.2)QUEUE_FLUSH:刷新模式。中断正在进行的说话,说新的内容。*2)参数3(HashMap<String,String>params)*2.1)请求的参数,可以为null。*2.2)注意KEY_PARAM_UTTERANCE_ID。*/HashMap<String,String>params=newHashMap<String,String>();params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,textTitle);returnmTts.speak(textContent,TextToSpeech.QUEUE_FLUSH,params);}returnTextToSpeech.ERROR;}///**判断TTS是否正在发声*///privatebooleanisSpeaking(){////使用mTts.isSpeaking()判断时,第一次speak()返回true,多次就返回false了。//returnaudioManager.isMusicActive();//}/**停止当前发声,同时放弃所有在等待队列的发声*/privateintttsStop(){isStopped=true;//设置标记return(null==mTts)?TextToSpeech.ERROR:mTts.stop();}/**释放资源(解除语音服务绑定)*/privatevoidttsShutDown(){if(null!=mTts){mTts.shutdown();}}/**初始化文件夹路径*/privatevoidinitDirs(finalStringdirpath){Filefile=newFile(dirpath);if(!file.exists()){file.mkdirs();}}/**将文本合成声音资源文件*/privateintttsSaveFile(Stringtext,finalStringfilename){return(null==mTts)?TextToSpeech.ERROR:mTts.synthesizeToFile(text,null,filename);}/**播放指定的使用文件*/privateintttsPlayFile(finalStringfilename){//如果存在FILENAME_SAVE文件的话if(newFile(filename).exists()){try{/*使用MediaPlayer进行播放(没进行控制==)*/MediaPlayerplayer=newMediaPlayer();player.setDataSource(filename);player.prepare();player.start();returnTextToSpeech.SUCCESS;}catch(Exceptione){e.printStackTrace();returnTextToSpeech.ERROR;}}returnTextToSpeech.ERROR;}} 超了==,代码贴多了?下半部分,请至《Android TTS实现简单阅读器(二)》。(工程附件也在后一部分中^^)

本文转自winorlose2000 51CTO博客,原文链接:/vaero/809873,如需转载请自行联系原作者

如果觉得《Android TTS实现简单阅读器(一)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。