序言

最近在研究读报的功能,想实现自动阅读,即能朗读,还能提示读到什么地方,反正是越方便越好。通过多次试验终于成功了。实现了逐字朗读变色,自动滚屏,屏幕常亮等功能。接下来你们将我再一次见识我的聪明才智。

效果

逐字朗读,和自动滚屏

实现

1.逐字朗读

先从朗读开始,朗读实现采用的是科大讯飞的语言合成技术。

讯飞开放平台

在使用语言合成的时候可以设置一个合成监听器,监听器有如下的回调。

public interface SynthesizerListener {void onSpeakBegin();void onBufferProgress(int var1, int var2, int var3, String var4);void onSpeakPaused();void onSpeakResumed();void onSpeakProgress(int var1, int var2, int var3);void onCompleted(SpeechError var1);void onEvent(int var1, int var2, int var3, Bundle var4);
}

大家可能觉得只要在onSpeakProgress拿到进度就能逐字显示了,那么你们就太too young too simple了。因为官方文档这样说的。

在onSpeakProgress有两个位置,一个是beginPos,一个是endPos这两个位置在朗读同一句话的时候是不会变的变得是第一个参数也就是progress。而progress是整段文字的进度,不是阅读一句话的进度。而且onSpeakProgress并不是读了一个字就回调一次,它回调的次数是不确定的。所以使用这个方法,我们可以轻松的实现整句的变色,但是想实现逐字的变色还差得远,这个时候就不得不夸一下我的机智了(每天我不夸自己几遍我就浑身难受 (~ ̄▽ ̄)~)。

下面谈谈我的思路,我原来写过音乐播放器,里面有歌词显示功能,歌词文件一般长这个样子的。

一个是歌词出现的时间点,一个歌词的内容。我在想如果我有一份语言文件的歌词文件就好了。但是我没有,可是这难得到我吗,显然是不可能的。既然没有歌词文件,那么我们自己就记录歌词文件,那么记录什么内容了,其实只需要记录两个量就行了。一个是这句话在整段文字中开始的index,还有就是这句话每个字的平均时间就行了。代码如下:

 //合成监听器private SynthesizerListener mSynListener = new SynthesizerListener() {@Overridepublic void onSpeakBegin() {}@Overridepublic void onBufferProgress(int i, int i1, int i2, String s) {}@Overridepublic void onSpeakPaused() {}@Overridepublic void onSpeakResumed() {}@Overridepublic void onSpeakProgress(int percent, int beginPos, int endPos) {//更新文字颜色updateText(beginPos, endPos);//自动滚屏autoScroll(beginPos);}@Overridepublic void onCompleted(SpeechError speechError) {if (!haveRecord) {//记录最后一句话的时间int size = content.length() - lastBegin;int avTime = (int) (System.currentTimeMillis() - lastTime) / size;readTimeMap.put(lastBegin, avTime);haveRecord = true;}reset();}@Overridepublic void onEvent(int i, int i1, int i2, Bundle bundle) {}};private void reset() {//重置mHandler.stopUpdate();btn_read.setText("开始读报");styled.removeSpan(spanRed);tv_content.setText(styled);scroll_news.smoothScrollTo(0, 0);lastBegin = -1;}private void updateText(int beginPos, int endPos) {if (lastBegin != beginPos) {if (beginPos == 0) {//第一次,记录开始时间lastTime = System.currentTimeMillis();}styled.removeSpan(spanRed);if (!haveRecord) {//整句更新styled.setSpan(spanRed, beginPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);tv_content.setText(styled);if (beginPos != 0) {//从第二句开始,记录上一句的平均用时//计算平均用时long now = System.currentTimeMillis();long useTime = now - lastTime;int averageTime = (int) (useTime / (beginPos - lastBegin));Log.i("zzz", "save begin=" + lastBegin + " avTime=" + averageTime);readTimeMap.put(lastBegin, averageTime);lastTime = now;}} else {//逐字更新Message msg = new Message();msg.what = MSG_UPDATE;msg.arg1 = beginPos;msg.arg2 = endPos;mHandler.sendMessage(msg);}lastBegin = beginPos;}}

我用了一个boolean的haveRecord变量标识是否生成了歌词文件,如果没有的话也就是第一遍播放的时候,就使用整段的显示,同时记录生成歌词文件。否则的话就在每句话开始的时候使用一个handler去获取这句话每个字的平均阅读时间然后自动更新文字。

   long lastTime = 0;int lastBegin = -1;//是否记录过时间线boolean haveRecord = false;Map<Integer, Integer> readTimeMap = new HashMap<>();private static final int MSG_UPDATE = 1;private static final int MSG_UPDATE_LOOP = 2;UpdateHandler mHandler = new UpdateHandler();class UpdateHandler extends Handler {boolean needUpdate = false;int begin = 0;int end = 0;int avTime = 0;int index = 0;@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_UPDATE:index=0;needUpdate = true;//起始位置begin = msg.arg1;//结束位置end = msg.arg2;//平均用时Integer integer = readTimeMap.get(begin);if (integer == null || integer == 0) {needUpdate = false;} else {avTime = integer;sendEmptyMessage(MSG_UPDATE_LOOP);}break;case MSG_UPDATE_LOOP:if (!needUpdate) {removeMessages(MSG_UPDATE_LOOP);return;}index++;int newEnd = begin + index;if (newEnd > end) {stopUpdate();} else {styled.setSpan(spanRed, begin, newEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);tv_content.setText(styled);sendEmptyMessageDelayed(MSG_UPDATE_LOOP, avTime);}break;}}public void stopUpdate() {needUpdate = false;removeMessages(MSG_UPDATE_LOOP);}}

大家可能觉得这样不方便,每一次显示逐字阅读的时候都还需要先读一遍,主要是因为这只是用来测试的APP,如果在真实环境中完全可以在服务器生成语言文件,同时生成歌词文件,到时候客户端就能直接逐字阅读了,也不需要再去请求科大讯飞了。

3.自动滚屏

自动滚屏的实现就比较简单了,主要是通过TextView的Layout实现的。通过调用TextView.getLayout()就能获取到。

  /*** @return the Layout that is currently being used to display the text.* This can be null if the text or width has recently changes.*/public final Layout getLayout() {return mLayout;}

但是需要注意的是获取时机,可能会为null。
可以采用下面这种方式获取的

      final ViewTreeObserver vto = tv_content.getViewTreeObserver();vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {mLayout = tv_content.getLayout();tv_content.getViewTreeObserver().removeOnGlobalLayoutListener(this);}});

在onSpeakProgress中更新完文字颜色就开始自动滚屏。

      @Overridepublic void onSpeakProgress(int percent, int beginPos, int endPos) {//更新文字颜色updateText(beginPos, endPos);//自动滚屏autoScroll(beginPos);}

而在自动滚屏的时候需要判断这一句话有没有引起,当前阅读行数的变化,因为可能一句话很短,它和上一句在同一行,这种情况就不需要滚屏。

// 自动滚屏相关代码Layout mLayout;int lastLine = 0;private void autoScroll(int beginPos) {int line = getLine(beginPos);//如果行数发生变化if (line != lastLine) {//保持有3行在最上面,正在朗读的文字在中间,符合人们的视线。if (line >= 3) {scroll_news.smoothScrollTo(0, tv_content.getTop() + mLayout.getLineTop(line - 3));}lastLine = line;}}private int getLine(int staPos) {int lineNumber = 0;if (mLayout != null) {int line = mLayout.getLineCount();for (int i = 0; i < line - 1; i++) {if (staPos <= mLayout.getLineStart(i)) {lineNumber = i;break;}}}return lineNumber;}

难度不大,主要是先关API的使用。

3.屏幕常亮

一个实用的功能,也在这里记录一下。

首先需要声明权限

  <!--屏幕常亮--><uses-permission android:name="android.permission.WAKE_LOCK" />

在onCreate()的时候获取

     PowerManager.WakeLock mWakeLock;PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");

接着和生命周期方法联合使用

@Overrideprotected void onResume() {super.onResume();mWakeLock.acquire();}@Overrideprotected void onPause() {super.onPause();mWakeLock.release();}

源码

很多细节的处理还是要看源码的

zhuguohui/PageReader

总结

研究这个功能,让我又一次对自己的聪明才智充满了信心。

实现智能读报(逐字朗读+自动滚屏)相关推荐

  1. echart 时间滚动_基于 ECharts 封装甘特图并实现自动滚屏

    项目中需要用到甘特图组件,之前的图表一直基于 EChart 开发,但 EChart 本身没有甘特图组件,需要自行封装 经过一番鏖战,终于完成了... 我在工程中参考 v-chart 封装了一套图表组件 ...

  2. VC编辑框(EDIT)的自动换行、自动滚屏 、到指定行数自动清空

    From: http://getyoureyes.blog.163.com/blog/static/101716622201083081914305/ 经过多次测试,总结出VC编辑框(EDIT)的自动 ...

  3. Qt4小技巧——QTextEdit自动滚屏

    CuteQt Blog » Blog Archive » Qt4小技巧--QTextEdit自动滚屏 Qt4小技巧--QTextEdit自动滚屏   本站所有文章由本站和原作者保留一切权力,仅在保留本 ...

  4. 帆软报表调用python脚本_帆软报表(finereport)实现自动滚屏效果

    例如Demo:IOS平台年度数据报表. 展示内容丰富,一个页面中存在多个图表.内容,超出了浏览器窗口的大小导致内容展示不全. 为了能够预览这个报表的全部内容,可以使用JS滚屏效果来实现. 操作步骤: ...

  5. jQuery实现 自动滚屏操作

    实现自动滚屏思路: 1.滚屏即:文本的往上移动一段距离: 2.那么我们使文本每过一段时间就往上移动一段固定距离,就可实现滚屏: 3.直到文本底部出现在浏览器窗口中,专业点就是 文本移动的距离 + 浏览 ...

  6. Android拖动实现(一个流畅的拖动排序DragSortGridView,自动滚屏)

    https://github.com/huxq17/HandyGridView 先上效果 流畅效果超越了网易新闻和UC浏览器的栏目收藏.gif图和实际效果有差距 1.拖拽可以移动item,并且其他it ...

  7. 大屏html自动滚动,Automatic Scrolling Tabs - 网页自动滚屏

    Automatic Scrolling Tabs插件背景简介 最近有网友在网上提问求一个脱离鼠标仍可网页自动向下滚动,然后鼠标去操作其他东西的插件,最好能设置滚动速度的.之前我们会推荐一些鼠标手势插件 ...

  8. MFC EDIT的使用———自动滚屏

    在自动换行设置的时候,要在EDIT控件的属性中选中"multiline"的属性和Auto_HScroll.Vertical scroll. 经过多次测试,总结出VC编辑框(EDIT)的自动换 ...

  9. 基于qt的udp聊天发送信息 有四处要设置自动滚屏 --------------- 续上

    上次写博客,我说有两处设置自动滚屏,但经过实际和板子通讯发现,还应有两处自动滚屏.总结如下: 第一处滚屏:receive()槽函数的开始,也就是显示text之前,要自动滚屏下.防止用户在上一时刻将鼠标 ...

最新文章

  1. 2.3.2 Batch Norm介绍
  2. [Python图像处理] 二十.图像量化处理和采样处理及局部马赛克特效
  3. linux克隆后重新封装,克隆后立即在OSX上修改Linux内核源代码
  4. C++新特性探究(十一):enum class(struct)
  5. android消息,android消息机制
  6. HDU 1087 Super Jumping! Jumping! Jumping!(DP)
  7. 再谈“颠覆”冯.诺依曼计算机体系结构 —— 计算机的未来发展方向:去内存化...
  8. Servlet请求和响应
  9. linux 5.8 设备的mac地址与预想的不符 已忽略,解决“eth0设备的MAC 址与预想的不符,忽略”...
  10. 大数据基础概念思维导图
  11. 微信小程序云开发--数据库使用
  12. resource failed to call close
  13. 官方|TensorFlow微信公众号发布
  14. vc 键盘按键KeyValue值
  15. 【黄啊码】百万级别订单量,如何生成唯一订单ID(雪花算法)
  16. 校园网自动登录,断线重连
  17. zabbix 表结构: triggers中expression 对应的信息
  18. 设计模式之依赖倒置设计原则
  19. 影像科dsa为什么必须买维修保险_为什么大家对影像科了解得那么少呢?
  20. Hive常用日期格式转换

热门文章

  1. 瑞萨单片机之开发环境的搭建(一)
  2. 组态王 6.55 启停plc_基于OMAC的设备标准化(PLC)应用(2)
  3. 1.get_dms_camera_coord_by_cheeseboard
  4. python期末考试重点_python语言基础与应用期末考试OJ
  5. 建设一个网站需要多长时间?
  6. 跟明汯投资学习适合散户的量化投资策略和方法!
  7. ArcGIS基础实验操作100例--实验68注记符号化表达
  8. uni-app知识点整理(5)- 网络请求、数据缓存、图片上传和预览
  9. CAD编辑指南11:CAD转PDF以及批量打印
  10. doxygen教程-4-快速上手配置文件