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程序,一个是语音数据。下载网址:http://soft.shouji.com.cn/down/22160.html

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

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

 
TtsFatherActivity.java
 
  1. /** 
  2.  * 1)本类用于main.xml的布局控制。子类再去实现各控件的TTS相关功能。 
  3.  * 2)用继承方式实现是为了利用布局中控件的onClick属性(懒得多写代码==!)。 
  4.  */ 
  5. public abstract class TtsFatherActivity extends Activity { 
  6.  
  7.     private GestureDetector gd; // 手势检测 
  8.     private GlobalUtil globalUtil; // 全局公用类 
  9.  
  10.     private ScrollView scrollView; // 滚动视图 
  11.     private LinearLayout headerLayout, footerLayout; // 顶部、底部布局 
  12.     private TextView textView; // 文本标签 
  13.  
  14.     private static final long ANIM_DURATION = 500// 动画时间(毫秒) 
  15.  
  16.     private static final int DIALOG_TEXT_LIST = 0// 文本列表对话框id 
  17.     private final String[] textPaths = new String[] { "one.txt""two.txt"
  18.             "浏览..." }; // assets内文本资源路径 
  19.  
  20.     protected String textTitle; // 文本标题 
  21.     protected String textContent; // 文本内容 
  22.  
  23.     private Timer timer; // 计时器 
  24.     private static final long TIMEOUT = 2000// 超时时间 
  25.     private static final int TIMER_LAYOUT_OUT = 1// 布局收起 
  26.  
  27.     private boolean isLayoutOut = false// 布局收起状态 
  28.  
  29.     /** Handler处理操作 */ 
  30.     public Handler mHandler = new Handler() { 
  31.         @Override 
  32.         public void handleMessage(Message msg) { 
  33.             switch (msg.what) { 
  34.             case TIMER_LAYOUT_OUT: 
  35.                 /* headerLayout收起动画 */ 
  36.                 globalUtil.startTransAnim(headerLayout, 
  37.                         GlobalUtil.AnimMode.UP_OUT, ANIM_DURATION); 
  38.                 headerLayout.setVisibility(View.GONE); 
  39.                 /* footerLayout收起动画 */ 
  40.                 globalUtil.startTransAnim(footerLayout, 
  41.                         GlobalUtil.AnimMode.DOWN_OUT, ANIM_DURATION); 
  42.                 footerLayout.setVisibility(View.GONE); 
  43.                 isLayoutOut = true// 重置布局收起状态 
  44.                 break
  45.             } 
  46.         } 
  47.     }; 
  48.  
  49.     @Override 
  50.     public void onCreate(Bundle savedInstanceState) { 
  51.         super.onCreate(savedInstanceState); 
  52.         setContentView(R.layout.main); 
  53.  
  54.         gd = new GestureDetector(new MySimpleGesture()); // 手势检测处理 
  55.         globalUtil = GlobalUtil.getInstance(); // 获取全局公用类 
  56.  
  57.         scrollView = (ScrollView) findViewById(R.id.scrollView); // 获取滚动视图 
  58.         headerLayout = (LinearLayout) findViewById(R.id.headerLayout); // 获取顶部布局 
  59.         footerLayout = (LinearLayout) findViewById(R.id.footerLayout); // 获取底部布局 
  60.         textView = (TextView) findViewById(R.id.textView); 
  61.  
  62.         setText(0); // 默认显示“上邪.txt” 
  63.         newTimerLayoutOut(); // 定时收起布局 
  64.     } 
  65.  
  66.     /** 使用GestureDetector检测手势(ScrollView内也需监听时的方式) */ 
  67.     @Override 
  68.     public boolean dispatchTouchEvent(MotionEvent ev) { 
  69.         gd.onTouchEvent(ev); 
  70.         scrollView.onTouchEvent(ev); 
  71.         return super.dispatchTouchEvent(ev); 
  72.     } 
  73.  
  74.     /** onCreateDialog */ 
  75.     @Override 
  76.     protected Dialog onCreateDialog(int id) { 
  77.         switch (id) { 
  78.         case DIALOG_TEXT_LIST: 
  79.             return new AlertDialog.Builder(this).setItems(textPaths, 
  80.                     new DialogInterface.OnClickListener() { 
  81.                         @Override 
  82.                         public void onClick(DialogInterface dialog, int which) { 
  83.                             if (2 == which) { 
  84.                                 // 跳转到文件浏览Activity 
  85.                                 startActivityForResult(new Intent( 
  86.                                         TtsFatherActivity.this
  87.                                         FileBrowserActivity.class), 
  88.                                         FileBrowserActivity.CODE_FILE_BROWSER); 
  89.                             } else { 
  90.                                 setText(which); // 设置文本内容 
  91.                             } 
  92.                         } 
  93.                     }).create(); 
  94.         } 
  95.         return super.onCreateDialog(id); 
  96.     } 
  97.  
  98.     @Override 
  99.     protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
  100.         if (requestCode == FileBrowserActivity.CODE_FILE_BROWSER) { 
  101.             if (resultCode == RESULT_OK) { 
  102.                 // 获得文件名称 
  103.                 String filename = data.getExtras().getString( 
  104.                         FileBrowserActivity.KEY_FILENAME); 
  105.                 this.textTitle = filename; 
  106.                 try { 
  107.                     // FileInputStream fis = new FileInputStream( 
  108.                     // new File(filename)); 
  109.                     // // FileInputStream不支持mark/reset操作,不该直接这样 
  110.                     // String encoding = globalUtil.getIsEncoding(fis); 
  111.                     // textContent = globalUtil.is2Str(fis, encoding); 
  112.                     /** 
  113.                      * TXT简单判断编码类型后转字符串 
  114.                      *  
  115.                      * ps: 
  116.                      * 1)扯淡,3.58MB的txt读出来了== 
  117.                      *    看来需要转成BufferedReader以readLine()方式读好些啊 
  118.                      *     
  119.                      * 2)TextView将大文本全部显示,这貌似... 
  120.                      *  
  121.                      *       时间主要花费在文本显示过程,不改进了,暂时将就吧== 
  122.                      *    2.1)用View自定义个控件显示文本也蛮久的,未减少多少时间。 
  123.                      *    2.2)至于AsyncTask,文本显示还是要在UI线程的==。 
  124.                      *     
  125.                      *    如果我们要仿个阅读器,用View自定义个控件还是必须的。 
  126.                      *    1)分段读取大文本,可以考虑3段(前后两段用于缓冲) 
  127.                      *       根据滑屏&显示内容等,注意文本显示衔接。 
  128.                      *    2)滚动条可以外面套个ScrollView。由各属性判断出大文本需要显示的高度, 
  129.                      *       重写onMeasure用setMeasuredDimension()设置好,才会有滚动条。 
  130.                      *       当然自己用scrollTo()、scrollBy()实现动画也是好的。 
  131.                      *    3)至于其他选中当前行啊什么的,慢慢写就成了... 
  132.                      *     
  133.                      *    不知道大家还有什么好的想法没? 
  134.                      */ 
  135.                     // long time1 = System.currentTimeMillis(); 
  136.                     textContent = globalUtil.is2Str(new FileInputStream( 
  137.                             new File(filename))); 
  138.                     // long time2 = System.currentTimeMillis(); 
  139.                     // Log.e("TAG1", "==" + (time2 - time1) + "=="); 
  140.                     textView.setText(textContent); 
  141.                     // long time3 = System.currentTimeMillis(); 
  142.                     // Log.e("TAG1", "==" + (time3 - time2) + "=="); 
  143.                 } catch (Exception e) { 
  144.                     textView.setText(R.string.text_error); 
  145.                     textContent = ""
  146.                 } 
  147.             } 
  148.         } 
  149.     } 
  150.  
  151.     /** 设置文本内容 */ 
  152.     private void setText(int textIndex) { 
  153.         this.textTitle = textPaths[textIndex]; 
  154.         try { 
  155.             textContent = globalUtil.is2Str(getAssets().open(textTitle), 
  156.                     "UTF-8"); 
  157.             textView.setText(textContent); 
  158.         } catch (IOException e) { 
  159.             textView.setText(R.string.text_error); 
  160.             textContent = ""
  161.         } 
  162.     } 
  163.  
  164.     /** 定时收起布局(已定时时重新开始定时) */ 
  165.     protected void newTimerLayoutOut() { 
  166.         if (null != timer) { 
  167.             timer.cancel(); 
  168.         } 
  169.         timer = new Timer(); 
  170.         // 超时TIMEOUT退出 
  171.         timer.schedule(new TimerTask() { 
  172.             @Override 
  173.             public void run() { 
  174.                 mHandler.sendEmptyMessage(TIMER_LAYOUT_OUT); 
  175.             } 
  176.         }, TIMEOUT); 
  177.     } 
  178.  
  179.     /** 自定义手势类 */ 
  180.     private class MySimpleGesture extends SimpleOnGestureListener { 
  181.  
  182.         /** 双击第二下 */ 
  183.         @Override 
  184.         public boolean onDoubleTap(MotionEvent e) { 
  185.             if (isLayoutOut) { 
  186.                 /* headerLayout进入动画 */ 
  187.                 headerLayout.setVisibility(View.VISIBLE); 
  188.                 globalUtil.startTransAnim(headerLayout, 
  189.                         GlobalUtil.AnimMode.UP_IN, ANIM_DURATION); 
  190.                 /* footerLayout进入动画 */ 
  191.                 footerLayout.setVisibility(View.VISIBLE); 
  192.                 globalUtil.startTransAnim(footerLayout, 
  193.                         GlobalUtil.AnimMode.DOWN_IN, ANIM_DURATION); 
  194.                 newTimerLayoutOut(); // 定时收起布局 
  195.                 isLayoutOut = false// 重置布局收起状态 
  196.             } else { 
  197.                 /* headerLayout退出动画 */ 
  198.                 globalUtil.startTransAnim(headerLayout, 
  199.                         GlobalUtil.AnimMode.UP_OUT, ANIM_DURATION); 
  200.                 headerLayout.setVisibility(View.GONE); 
  201.                 /* footerLayout退出动画 */ 
  202.                 globalUtil.startTransAnim(footerLayout, 
  203.                         GlobalUtil.AnimMode.DOWN_OUT, ANIM_DURATION); 
  204.                 footerLayout.setVisibility(View.GONE); 
  205.                 // 取消定时收起动画 
  206.                 if (null != timer) { 
  207.                     timer.cancel(); 
  208.                 } 
  209.                 isLayoutOut = true// 重置布局收起状态 
  210.             } 
  211.             return false
  212.         } 
  213.  
  214.         /** 长按屏幕时 */ 
  215.         @Override 
  216.         public void onLongPress(MotionEvent e) { 
  217.             // 显示文本列表对话框 
  218.             showDialog(DIALOG_TEXT_LIST); 
  219.         } 
  220.     } 
  221.  
 
2 )TTS 控制
         音量&语速控制也写了的^^。
 
TtsSampleActivity.java
 
  1. public class TtsSampleActivity extends TtsFatherActivity implements 
  2.         OnSeekBarChangeListener, TextToSpeech.OnInitListener, 
  3.         TextToSpeech.OnUtteranceCompletedListener { 
  4.  
  5.     // private static final String TAG = "TtsSampleActivity"; // 日志标记 
  6.  
  7.     private AudioManager audioManager; // 音频管理对象 
  8.  
  9.     // TTS音量类型(AudioManager.STREAM_MUSIC = AudioManager.STREAM_TTS = 11) 
  10.     private static final int STREAM_TTS = AudioManager.STREAM_MUSIC; 
  11.  
  12.     private TextToSpeech mTts; // TTS对象 
  13.     private static final int REQ_CHECK_TTS_DATA = 110// TTS数据校验请求值 
  14.     private boolean isSetting = false// 进入设置标记 
  15.     private boolean isRateChanged = false// 速率改变标记 
  16.     private boolean isStopped = false// TTS引擎停止发声标记 
  17.     private float mSpeechRate = 1.0f; // 朗读速率 
  18.  
  19.     private SeekBar volumeBar, speedBar; // 音量&语速 
  20.  
  21.     // 合成声音资源文件的路径 
  22.     private static final String SAVE_DIR_PATH = "/sdcard/AndroidTTS/"
  23.     private static final String SAVE_FILE_PATH = SAVE_DIR_PATH + "sound.wav"
  24.  
  25.     @Override 
  26.     public void onCreate(Bundle savedInstanceState) { 
  27.         super.onCreate(savedInstanceState); 
  28.  
  29.         // 获得音频管理对象 
  30.         audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 
  31.  
  32.         /* volumeBar */ 
  33.         volumeBar = (SeekBar) findViewById(R.id.volumeBar); 
  34.         volumeBar.setOnSeekBarChangeListener(this); 
  35.         // 由当前音量设置进度(需保证进度上限=音频上限=15,否则按比例设置) 
  36.         volumeBar.setProgress(audioManager.getStreamVolume(STREAM_TTS)); 
  37.  
  38.         /* speedBar */ 
  39.         speedBar = (SeekBar) findViewById(R.id.speedBar); 
  40.         speedBar.setOnSeekBarChangeListener(this); 
  41.  
  42.         initDirs(SAVE_DIR_PATH); // 初始化文件夹路径 
  43.     } 
  44.  
  45.     /** saveFileBtn点击事件 */ 
  46.     public void saveFile(View v) { 
  47.         // 将文本合成声音资源文件 
  48.         int resId = TextToSpeech.SUCCESS == ttsSaveFile(textContent, 
  49.                 SAVE_FILE_PATH) ? R.string.synt_success : R.string.synt_fail; 
  50.         Toast.makeText(this, resId, Toast.LENGTH_SHORT).show(); // Toast提示 
  51.         newTimerLayoutOut(); // 重新定时收起布局 
  52.     } 
  53.  
  54.     /** playFileBtn点击事件 */ 
  55.     public void playFile(View v) { 
  56.         ttsPlayFile(SAVE_FILE_PATH); // 播放指定的使用文件 
  57.         newTimerLayoutOut(); // 重新定时收起布局 
  58.     } 
  59.  
  60.     /** stopBtn点击事件 */ 
  61.     public void stop(View v) { 
  62.         ttsStop(); // 停止当前发声 
  63.         newTimerLayoutOut(); // 重新定时收起布局 
  64.     } 
  65.  
  66.     /** playBtn点击事件 */ 
  67.     public void play(View v) { 
  68.         ttsPlay(); // tts合成语音播放 
  69.         newTimerLayoutOut(); // 重新定时收起布局 
  70.     } 
  71.  
  72.     /** settingBtn点击事件 */ 
  73.     public void setting(View v) { 
  74.         // 跳转到“语音输入与输出”设置界面&设置标志位 
  75.         isSetting = toTtsSettings(); 
  76.         newTimerLayoutOut(); // 重新定时收起布局 
  77.     } 
  78.  
  79.     /** SeekBar进度改变时 */ 
  80.     @Override 
  81.     public void onProgressChanged(SeekBar seekBar, int progress, 
  82.             boolean fromUser) { 
  83.         switch (seekBar.getId()) { 
  84.         case R.id.volumeBar: 
  85.             // 由设置当前TTS音量(需保证进度上限=音频上限=15,否则按比例设置) 
  86.             audioManager.setStreamVolume(STREAM_TTS, progress, 0); 
  87.             break
  88.         case R.id.speedBar: 
  89.             /* 需要重新绑定TTS引擎,速度在onInit()里设置 */ 
  90.             isRateChanged = true// 速率改变标记 
  91.             // 最大值为20时,以下方式计算为0.5~2倍速 
  92.             mSpeechRate = (progress >= 10) ? (progress / 10f) 
  93.                     : (0.5f + progress / 20f); 
  94.             // 校验TTS引擎安装及资源状态,重新绑定引擎 
  95.             checkTtsData(); 
  96.             break
  97.         } 
  98.         newTimerLayoutOut(); // 重新定时收起布局 
  99.     } 
  100.  
  101.     /** SeekBar开始拖动时 */ 
  102.     @Override 
  103.     public void onStartTrackingTouch(SeekBar seekBar) { 
  104.     } 
  105.  
  106.     /** SeekBar结束拖动时 */ 
  107.     @Override 
  108.     public void onStopTrackingTouch(SeekBar seekBar) { 
  109.     } 
  110.  
  111.     /** 
  112.      * TTS引擎初始化时回调方法 
  113.      *  
  114.      * 引擎相关参数(音量、语速)等都需在这设置。 
  115.      * 1)创建完成后再去设置,会有意外的效果^^ 
  116.      * 2)音量也可由AudioManager进行控制(和音乐一个媒体流类型) 
  117.      */ 
  118.     @Override 
  119.     public void onInit(int status) { 
  120.         if (status == TextToSpeech.SUCCESS) { 
  121.             mTts.setSpeechRate(mSpeechRate); // 设置朗读速率 
  122.             // 设置发声合成监听,注意也需要在onInit()中做才有效 
  123.             mTts.setOnUtteranceCompletedListener(this); 
  124.             if (isRateChanged) { 
  125.                 ttsPlay(); // tts合成语音播放 
  126.                 isRateChanged = false// 重置标记位 
  127.             } 
  128.         } 
  129.     } 
  130.  
  131.     /**  
  132.      * TTS引擎完成发声完成时回调方法 
  133.      *  
  134.      * 1)stop()取消时也会回调 
  135.      * 2)需在onInit()内设置接口 
  136.      * 3)utteranceId由speak()时的请求参数设定 
  137.      * 参数key:TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID 
  138.      */ 
  139.     @Override 
  140.     public void onUtteranceCompleted(final String utteranceId) { 
  141.         /* 测试该接口的Toast提示 */ 
  142.         runOnUiThread(new Runnable() { 
  143.             @Override 
  144.             public void run() { 
  145.                 int resId = isStopped ? R.string.utte_stopped 
  146.                         : R.string.utte_completed; 
  147.                 // 提示文本发生完成 
  148.                 Toast.makeText(getApplicationContext(), 
  149.                         getString(resId, utteranceId), Toast.LENGTH_SHORT) 
  150.                         .show(); 
  151.             } 
  152.         }); 
  153.     } 
  154.  
  155.     /** onActivityResult */ 
  156.     @Override 
  157.     protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
  158.         if (requestCode == REQ_CHECK_TTS_DATA) { 
  159.             switch (resultCode) { 
  160.             case TextToSpeech.Engine.CHECK_VOICE_DATA_PASS: // TTS引擎可用 
  161.                 // 针对于重新绑定引擎,需要先shutdown() 
  162.                 if (null != mTts) { 
  163.                     ttsStop(); // 停止当前发声 
  164.                     ttsShutDown(); // 释放资源 
  165.                 } 
  166.                 mTts = new TextToSpeech(thisthis); // 创建TextToSpeech对象 
  167.                 break
  168.             case TextToSpeech.Engine.CHECK_VOICE_DATA_BAD_DATA: // 数据错误 
  169.             case TextToSpeech.Engine.CHECK_VOICE_DATA_MISSING_DATA: // 缺失数据资源 
  170.             case TextToSpeech.Engine.CHECK_VOICE_DATA_MISSING_VOLUME: // 缺少数据存储量 
  171.                 notifyReinstallDialog(); // 提示用户是否重装TTS引擎数据的对话框 
  172.                 break
  173.             case TextToSpeech.Engine.CHECK_VOICE_DATA_FAIL: // 检查失败 
  174.             default
  175.                 break
  176.             } 
  177.         } 
  178.         super.onActivityResult(requestCode, resultCode, data); 
  179.     } 
  180.  
  181.     /** 校验TTS引擎安装及资源状态 */ 
  182.     private boolean checkTtsData() { 
  183.         try { 
  184.             Intent checkIntent = new Intent(); 
  185.             checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); 
  186.             startActivityForResult(checkIntent, REQ_CHECK_TTS_DATA); 
  187.             return true
  188.         } catch (ActivityNotFoundException e) { 
  189.             return false
  190.         } 
  191.     } 
  192.  
  193.     /** 提示用户是否重装TTS引擎数据的对话框 */ 
  194.     private void notifyReinstallDialog() { 
  195.         new AlertDialog.Builder(this).setTitle("TTS引擎数据错误"
  196.                 .setMessage("是否尝试重装TTS引擎数据到设备上?"
  197.                 .setPositiveButton("是"new DialogInterface.OnClickListener() { 
  198.                     @Override 
  199.                     public void onClick(DialogInterface dialog, int which) { 
  200.                         // 触发引擎在TTS引擎在设备上安装资源文件 
  201.                         Intent dataIntent = new Intent(); 
  202.                         dataIntent 
  203.                                 .setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA); 
  204.                         startActivity(dataIntent); 
  205.                     } 
  206.                 }).setNegativeButton("否"null).show(); 
  207.     } 
  208.  
  209.     /** 跳转到“语音输入与输出”设置界面 */ 
  210.     private boolean toTtsSettings() { 
  211.         try { 
  212.             startActivity(new Intent("com.android.settings.TTS_SETTINGS")); 
  213.             return true
  214.         } catch (ActivityNotFoundException e) { 
  215.             return false
  216.         } 
  217.     } 
  218.  
  219.     @Override 
  220.     protected void onStart() { 
  221.         checkTtsData(); // 校验TTS引擎安装及资源状态 
  222.         super.onStart(); 
  223.     } 
  224.  
  225.     @Override 
  226.     protected void onResume() { 
  227.         /* 从设置返回后重新绑定TTS,避免仍用旧引擎 */ 
  228.         if (isSetting) { 
  229.             checkTtsData(); // 校验TTS引擎安装及资源状态 
  230.             isSetting = false
  231.         } 
  232.         super.onResume(); 
  233.     } 
  234.  
  235.     @Override 
  236.     protected void onStop() { 
  237.         /* HOME键 */ 
  238.         ttsStop(); // 停止当前发声 
  239.         super.onStop(); 
  240.     } 
  241.  
  242.     @Override 
  243.     public void onBackPressed() { 
  244.         /* BACK键 */ 
  245.         ttsStop(); // 停止当前发声 
  246.         ttsShutDown(); // 释放资源 
  247.         super.onBackPressed(); 
  248.     } 
  249.  
  250.     /** tts合成语音播放 */ 
  251.     private int ttsPlay() { 
  252.         if (null != mTts) { 
  253.             isStopped = false// 设置标记 
  254.             /** 
  255.              * 叙述text。 
  256.              *  
  257.              * 1) 参数2(int queueMode) 
  258.              *    1.1)QUEUE_ADD:增加模式。增加在队列尾,继续原来的说话。 
  259.              *    1.2)QUEUE_FLUSH:刷新模式。中断正在进行的说话,说新的内容。 
  260.              * 2)参数3(HashMap<String, String> params) 
  261.              *    2.1)请求的参数,可以为null。 
  262.              *    2.2)注意KEY_PARAM_UTTERANCE_ID。 
  263.              */ 
  264.             HashMap<String, String> params = new HashMap<String, String>(); 
  265.             params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, textTitle); 
  266.             return mTts.speak(textContent, TextToSpeech.QUEUE_FLUSH, params); 
  267.         } 
  268.         return TextToSpeech.ERROR; 
  269.     } 
  270.  
  271.     // /** 判断TTS是否正在发声 */ 
  272.     // private boolean isSpeaking() { 
  273.     // // 使用mTts.isSpeaking()判断时,第一次speak()返回true,多次就返回false了。 
  274.     // return audioManager.isMusicActive(); 
  275.     // } 
  276.  
  277.     /** 停止当前发声,同时放弃所有在等待队列的发声 */ 
  278.     private int ttsStop() { 
  279.         isStopped = true// 设置标记 
  280.         return (null == mTts) ? TextToSpeech.ERROR : mTts.stop(); 
  281.     } 
  282.  
  283.     /** 释放资源(解除语音服务绑定) */ 
  284.     private void ttsShutDown() { 
  285.         if (null != mTts) { 
  286.             mTts.shutdown(); 
  287.         } 
  288.     } 
  289.  
  290.     /** 初始化文件夹路径 */ 
  291.     private void initDirs(final String dirpath) { 
  292.         File file = new File(dirpath); 
  293.         if (!file.exists()) { 
  294.             file.mkdirs(); 
  295.         } 
  296.     } 
  297.  
  298.     /** 将文本合成声音资源文件 */ 
  299.     private int ttsSaveFile(String text, final String filename) { 
  300.         return (null == mTts) ? TextToSpeech.ERROR : mTts.synthesizeToFile( 
  301.                 text, null, filename); 
  302.     } 
  303.  
  304.     /** 播放指定的使用文件 */ 
  305.     private int ttsPlayFile(final String filename) { 
  306.         // 如果存在FILENAME_SAVE文件的话 
  307.         if (new File(filename).exists()) { 
  308.             try { 
  309.                 /* 使用MediaPlayer进行播放(没进行控制==) */ 
  310.                 MediaPlayer player = new MediaPlayer(); 
  311.                 player.setDataSource(filename); 
  312.                 player.prepare(); 
  313.                 player.start(); 
  314.                 return TextToSpeech.SUCCESS; 
  315.             } catch (Exception e) { 
  316.                 e.printStackTrace(); 
  317.                 return TextToSpeech.ERROR; 
  318.             } 
  319.         } 
  320.         return TextToSpeech.ERROR; 
  321.     } 
  322.  
 
         超了==,代码贴多了?下半部分,请至《 Android TTS实现简单阅读器(二)》。(工程附件也在后一部分中^^)






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



相关文章
|
21天前
|
Java Android开发
Android 开发获取通知栏权限时会出现两个应用图标
Android 开发获取通知栏权限时会出现两个应用图标
12 0
|
1月前
|
XML 缓存 Android开发
Android开发,使用kotlin学习多媒体功能(详细)
Android开发,使用kotlin学习多媒体功能(详细)
97 0
|
1月前
|
设计模式 人工智能 开发工具
安卓应用开发:构建未来移动体验
【2月更文挑战第17天】 随着智能手机的普及和移动互联网技术的不断进步,安卓应用开发已成为一个热门领域。本文将深入探讨安卓平台的应用开发流程、关键技术以及未来发展趋势。通过分析安卓系统的架构、开发工具和框架,本文旨在为开发者提供全面的技术指导,帮助他们构建高效、创新的移动应用,以满足不断变化的市场需求。
18 1
|
1月前
|
机器学习/深度学习 调度 Android开发
安卓应用开发:打造高效通知管理系统
【2月更文挑战第14天】 在移动操作系统中,通知管理是影响用户体验的关键因素之一。本文将探讨如何在安卓平台上构建一个高效的通知管理系统,包括服务、频道和通知的优化策略。我们将讨论最新的安卓开发工具和技术,以及如何通过这些工具提高通知的可见性和用户互动性,同时确保不会对用户造成干扰。
33 1
|
12天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
15天前
|
监控 算法 Android开发
安卓应用开发:打造高效启动流程
【4月更文挑战第5天】 在移动应用的世界中,用户的第一印象至关重要。特别是对于安卓应用而言,启动时间是用户体验的关键指标之一。本文将深入探讨如何优化安卓应用的启动流程,从而减少启动时间,提升用户满意度。我们将从分析应用启动流程的各个阶段入手,提出一系列实用的技术策略,包括代码层面的优化、资源加载的管理以及异步初始化等,帮助开发者构建快速响应的安卓应用。
|
15天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
19 1
Android开发之使用OpenGL实现翻书动画
|
15天前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
13 1
Android开发之OpenGL的画笔工具GL10
|
21天前
|
Android开发
Android开发小技巧:怎样在 textview 前面加上一个小图标。
Android开发小技巧:怎样在 textview 前面加上一个小图标。
12 0