Android TTS实现简单阅读器

本文原创版权归 51CTO winorlose2000 所有,转载请按如下方式于文章显示位置详细标明原创作者及出处,以示尊重!!

作者:winorlose2000

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.  
扫描二维码关注公众号,回复: 765140 查看本文章
 
 
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.  
 
  3)公共工具
         这个也贴出来吧==。
 
GlobalUtil.java
  1. /** 
  2.  * @brief 全局公用类 
  3.  * @details 各部分公用内容都较少,就丢一起了==。 
  4.  */ 
  5. public final class GlobalUtil { 
  6.  
  7.     /** Bob Lee懒加载:内部类GlobalUtilHolder */ 
  8.     static class GlobalUtilHolder { 
  9.         static GlobalUtil instance = new GlobalUtil(); 
  10.     } 
  11.  
  12.     /** Bob Lee懒加载:返回GlobalUtil的单例 */ 
  13.     public static GlobalUtil getInstance() { 
  14.         return GlobalUtilHolder.instance; 
  15.     } 
  16.  
  17.     /** 获取窗口默认显示信息 */ 
  18.     public Display getDisplay(Context mContext) { 
  19.         WindowManager wm = (WindowManager) mContext 
  20.                 .getSystemService(Context.WINDOW_SERVICE); 
  21.         return wm.getDefaultDisplay(); 
  22.     } 
  23.  
  24.     /** 动画方式 */ 
  25.     public enum AnimMode { 
  26.         UP_IN, UP_OUT, DOWN_IN, DOWN_OUT, LEFT_IN, LEFT_OUT, RIGHT_IN, RIGHT_OUT 
  27.     }; 
  28.  
  29.     /** 
  30.      * @brief 横移或竖移动画 
  31.      *  
  32.      * @param v  移动视图 
  33.      * @param animMode 动画方式 
  34.      * @param durationMillis 持续时间 
  35.      */ 
  36.     public void startTransAnim(View v, AnimMode animMode, long durationMillis) { 
  37.         int w = v.getWidth(), h = v.getHeight(); // 获取移动视图宽高 
  38.         float fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0
  39.         switch (animMode) { 
  40.         case UP_IN: 
  41.             fromYDelta = -h; 
  42.             break
  43.         case UP_OUT: 
  44.             toYDelta = -h; 
  45.             break
  46.         case DOWN_IN: 
  47.             fromYDelta = h; 
  48.             break
  49.         case DOWN_OUT: 
  50.             toYDelta = h; 
  51.             break
  52.         case LEFT_IN: 
  53.             fromXDelta = -w; 
  54.             break
  55.         case LEFT_OUT: 
  56.             toXDelta = -w; 
  57.             break
  58.         case RIGHT_IN: 
  59.             fromXDelta = w; 
  60.             break
  61.         case RIGHT_OUT: 
  62.             toXDelta = w; 
  63.             break
  64.         } 
  65.         TranslateAnimation transAnim = new TranslateAnimation(fromXDelta, 
  66.                 toXDelta, fromYDelta, toYDelta); // 位移动画 
  67.         transAnim.setDuration(durationMillis); // 设置时间 
  68.         v.startAnimation(transAnim); // 开始动画 
  69.     } 
  70.  
  71.     /** 
  72.      * @brief InputStream转为String 
  73.      *  
  74.      * @param is 输入流 
  75.      * @param encoding 编码方式 
  76.      * @return 字符串结果 
  77.      * @throws UnsupportedEncodingException 不支持的编码 
  78.      */ 
  79.     public String is2Str(InputStream is, String encoding) 
  80.             throws UnsupportedEncodingException { 
  81.         /* 
  82.          * 不直接从InputStream里读byte[],再转成String,以避免截断汉字。 
  83.          * 如utf8一个汉字3字节,用byte[1024]会截断末尾而乱码。 
  84.          */ 
  85.         InputStreamReader isReader = new InputStreamReader(is, encoding); 
  86.         /* 以char[]方式读取 */ 
  87.         StringBuffer out = new StringBuffer(); 
  88.         try { 
  89.             char[] b = new char[4096]; // 1024*4*2Byte 
  90.             for (int n; (n = isReader.read(b)) != -1;) { 
  91.                 out.append(b, 0, n); 
  92.             } 
  93.             return out.toString(); 
  94.         } catch (IOException e) { 
  95.             e.printStackTrace(); 
  96.         } 
  97.         return ""
  98.     } 
  99.  
  100.     /** 
  101.      * @brief 带BOM的文本的FileInputStream转为String,自动判断编码类型 
  102.      *  
  103.      * @param fis 文件输入流 
  104.      * @return 字符串结果 
  105.      * @throws UnsupportedEncodingException 不支持的编码 
  106.      */ 
  107.     public String is2Str(FileInputStream fis) 
  108.             throws UnsupportedEncodingException { 
  109.         // 转成BufferedInputStream 
  110.         BufferedInputStream bis = new BufferedInputStream(fis); 
  111.         // 简单判断文本编码 
  112.         String encoding = getIsEncoding(bis); 
  113.         // 转成BufferedReader 
  114.         BufferedReader reader = new BufferedReader(new InputStreamReader(bis, 
  115.                 encoding)); 
  116.         /* 以readLine()方式读取 */ 
  117.         StringBuffer out = new StringBuffer(); 
  118.         try { 
  119.             for (String s; (s = reader.readLine()) != null;) { 
  120.                 out.append(s); 
  121.                 out.append("\n"); 
  122.             } 
  123.             return out.toString(); 
  124.         } catch (IOException e) { 
  125.             e.printStackTrace(); 
  126.         } 
  127.         return ""
  128.     } 
  129.  
  130.     /** 
  131.      * @brief 带BOM的文本判断,否则认为GB2312(即ANSI类型的TXT) 
  132.      * @details 复杂文件编码检测,请google cpdetector! 
  133.      *  
  134.      * @param is InputStream 
  135.      * @return 编码类型 
  136.      *  
  137.      * @warning markSupported为true时才进行判断,否则返回默认GB2312。 
  138.      * \n FileInputStream不支持mark/reset操作;BufferedInputStream支持此操作。 
  139.      */ 
  140.     public String getIsEncoding(InputStream is) { 
  141.         String code = "GB2312"
  142.         // Log.e("is.markSupported()", "==" + is.markSupported() + "=="); 
  143.         if (is.markSupported()) { // 支持mark() 
  144.             try { 
  145.                 is.mark(5); // 打个TAG(5>3) 
  146.                 byte[] head = new byte[3]; 
  147.                 is.read(head); 
  148.                 if (head[0] == -1 && head[1] == -2
  149.                     code = "UTF-16"
  150.                 if (head[0] == -2 && head[1] == -1
  151.                     code = "Unicode"
  152.                 if (head[0] == -17 && head[1] == -69 && head[2] == -65
  153.                     code = "UTF-8"
  154.                 is.reset(); // 返回TAG 
  155.             } catch (IOException e) { 
  156.                 e.printStackTrace(); 
  157.             } 
  158.         } 
  159.         return code; 
  160.     } 
  161.  
 
 
 
四、阅读器截图
1 )进入画面.png

应用图标.png

 
2 )长按屏幕.png

应用图标.png

 
3 )自带two.txt.png

应用图标.png

 
4 )“浏览… 操作”的截图

浏览...(1).png

浏览...(1).png
 

浏览...(1).png

浏览...(2).png
 

浏览...(1).png

浏览...(3).png
 
五、后记

         直接读大文本,后果自负啊。(没做控制呢T^T)

 

         ps:应用的入口图标 应用图标.png ,可爱吧^^。

附件下载:
作者:winorlose2000

猜你喜欢

转载自kenkao.iteye.com/blog/1703270