上回说到pdf阅读应用的相关东西,这次说的是纯文本阅读器。

纯文本阅读器,啊~,这个咋听起来很简单吗,看纯文本还不简单,但是要实现现在市场上那些个兼容大部分编码,支持大文件,带翻页效果的阅读器还是很不容易的。

首先,文件大小问题,面对一个小文件,1M以下,把文本直接读入内存什么问题也没有,但是一个大文件问题就来了,面对一个上M甚至几十M的文本,一次加载到内存中的话,打开的时候会巨慢无比,并且太大了内存也装不下,oom是肯定的。所以应该使用RandomAccessFile,这个支持随机文件读取,可以前后移动文件指针来读取特定位置的字节。

纯文本听起来简单,但是涉及到编码问题,那就麻烦至极。一个文件可能是以gbk,utf8,ascii,utf16 le,utf 16 be,big5等等(只考虑中文编码)。首先要知道文件的编码,这样才可以在java里面根据相应的编码转成String类型(java中String是UTF8编码的),不然就可能是乱码。而且这里还设计到分页的问题,不同的编码的每个字的编码长度是不一样的,比如gbk中文编码是2个字节,但是ascii码部分是一个字节,utf8的ascii码部分是1个字节,中文字符是3个字节,这样在分页的时候,(由于我们是一个字节一个字节读的),很有可能将文件指针搞到一个字符的全部字节的中间处,这样就产生了乱码,而且utf16还分big endian和little endian,字节序的问题,总之各种麻烦。

为了防止出现解析到字符编码中间的问题,为了照顾大部分编码,我采用了回车换行断句法,就是找到一个字符序列中的回车换行,因为回车换行相对固定且容易一些,而且换行的出现频率很大(对于一行文字达到1024个字,大概2K到3K个字节,这样的相对较少,可以忽略)。对于换行也有三种,分别是Windows下的\n,Linux下的\r\n,Mac下的\r。针对这三种情况,在一段字节序列中找到出现换行符的时候,表示一句话结束,所以这句话肯定是完整的,转换成String是没问题。这里主要针对的是相对比较广泛的gbk,utf8,utf16等做了判断,gbk和utf8的换行符都是一个字符占用一个字节,所以可以归为一类,另一个utf16,是2个字节作为一个字符的,而且还分big endian和little endian,区别是big endian是正的字节序,而little endian是反的字节序。比如\n在big endian中是0x00 0x0a,而在little endian中是0x0a 0x00。

那么还有一个原始的问题,怎么知道一个文本是用什么编码的呢?这是一个大问题,对于Unicode类的编码,有的是有BOM的,byte order mark,字节序,可以较为准确的辨识具体是Unicode里的那种编码,这个检测编码是个复杂的东西,我找来了几个开源的库实现,cpDetector,http://sourceforge.net/projects/cpdetector/,其实检测都是基于统计学规律的,有个概率,不是完全准确,这个库我测试了一些文件也有检测错误的时候,特别是对于没有bom的utf16文件。总的来说,这个库检测编码的时候有个顺序,先检测是否是ascii,然后看bom,然后基于统计。这个目前来说最准确的了,但是加上扩展的几个包,有点大,2m左右。

结合前面的RandomAccessFile。往后找换行还相对好弄一点,往前找空行相对麻烦了一些,但是原理都是一样的。

这样取出来一段供人类阅读的字符串后,怎么绘制到屏幕上去呢,以为要加翻书的那种翻页效果,所以要用canvas的绘制,(这些日子没少和canvas接触,感觉还是很有意思的,对Android的绘图机制也更加了解一点了)。我觉的翻页效果这事,纯属是数学问题,我拿来个笔记本在那模拟翻书的效果,怎么折叠移动,坐标点怎么变化,怎么旋转,怎么算一些点,怎么切割,算的很恶心。

对于前面得到的字符串,先要得知一行可以绘制多少个字,一共可以绘制多少行,这是一个问题,由于中文英文字符宽度是不一样的,所以不能拿字符个数作为换行标准,paint类提供了一个方法,breakText,这个可以得出大体真实的根据宽度和paint的字号大小来一行显示的字数。(貌似也不是100%好用)
绘图方面,这里主要是涉及到Canvas画图,Matrix矩阵变换(幸好之前做图片查看器的是好弄懂了),Clip裁剪,Scroller等。由于电脑图形中的坐标系和数学中学的坐标系有点差别,所以这个也要注意。然后为了逼真,还要加一些阴影,背部投影之类的。主要流程是,处理手势,计算坐标点,裁剪,旋转,更新重绘,自动翻过去。

这里细节还很多,有的可以参看后面的连接,有的就是要真正做才能感受到,总之一步一步自己来还是可以通过自己努力做的出来的。