这里只是我用java做的小尝试里面存在很多bug还没有处理完;真正的解码的话建议大家使用FBreader,这是一个开源项目,非常的屌。
切入正题,下面是我处理的时候的一些思路。
为进行分页需要计算当前屏幕区域能展示的文字数目,网上搜资料大多都是使用canvas drawText 这样就可以进行读写控制,我这里并不是这么处理的,而是直接使用TextView进行处理,计算TextView每行能展示的文字数目(按照汉字进行计算,因为汉字是方块字设置 textSize 后方便计算,一个汉字是一个英文字母的两倍宽度)和能容纳文字的总行数。
计算方法如下:
textView.postDelayed(new Runnable() {
@Override
public void run() {
float v = textView.getHeight() / textView.getLineHeight();
factory.setLenH((int) v);//行数
factory.setLenW((int) (textView.getWidth() / textView.getTextSize()));//宽能容纳的文字数
}
}, 300);
使用postDelayed,因为只有在页面布局完成之后才能计算view的宽高。
因为txt文件可能存在文件特别大的情况不能全部读入内存,所以使用java的随机读写文件RandomAccessFile进行处理,读取时可以从指定位置进行读取。
读取方法如下,包括读上一页和读下一页,代码中已经注释的很详细了,就不额外添加说明了。
package com.example.MyReader.uitl;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 书本解码处理类
* Created by hangli2 on 2015/4/28.
*/
public class BookPageFactory {
/**
* 当前页能容纳的文字长度
*/
@Deprecated
private int curPageLen = 1026;
/**
* 随机读取文件
*/
private RandomAccessFile randomAccessFile;
/**
* 文件大小
*/
private long length;
/**
* 宽能显示的字数,汉字
*/
private int lenW;
/**
* 高能显示的字数
*/
private int lenH;
/**
* 开始读书本页的位置
*/
private long begin = 0;
/**
* 记录当前页字符数目
*/
private long pageSize = 0;
private boolean FLAG_END = false;
/**逆序读取的时候标记第一个是不是换行*/
private boolean FLAG_BEGIN = false;
/**
* 从指定位置打开书本
*
* @param bookFile 书本文件
* @param begin 档期页开始位置
*/
public void openBook(File bookFile, int begin) throws IOException {
this.begin = begin;
openBook(bookFile);
}
/**
* 从开始位置打开书本
*
* @param bookFile 书本文件
*/
public void openBook(File bookFile) throws IOException {
this.begin = 0;
randomAccessFile = new RandomAccessFile(bookFile, "r");//只读方式打开文件,安装randomAccessFile读取
// randomAccessFile.seek(begin);
}
/**
* 读当前页UTF-8
*
* @return
* @throws java.io.IOException
*/
public String readPageUTF8() {
long sign = begin;//标记当前页读取了几多字符
String result = "";//读取结果字符串
byte[] bytes = new byte[3 * lenW];//汉语占三个字节所以byte最大应该为3倍的lenW
try {
randomAccessFile.seek(begin);
for (int i = 0; i < lenH || FLAG_END; i++) {//读取能显示的最大行数
FLAG_END = false;
bytes = new byte[3 * lenW];
int l = 0;//l用来标记bytes的坐标
for (int k = 0; k < 2 * lenW; k++) {
bytes[++l] = randomAccessFile.readByte();
sign++;
if (bytes[l] == 10) {//遇到换行符换到下一行
if (i == lenH - 1) {
FLAG_END = true;
}
break;
} else if (bytes[l] < 0) {//汉字读取
if (l < (3 * lenW - 2)) {//当前行剩余的空间能够战胜一个汉字
//汉字在utf-8中占三个字节,读取一个字节后再额外读两个字节
bytes[++l] = randomAccessFile.readByte();
sign++;
bytes[++l] = randomAccessFile.readByte();
sign++;
k++;//汉字展示是英文的两倍的宽度,所以占的字符宽度要+1
} else {
bytes[l] = 0;
randomAccessFile.seek(--sign);
break;
}
}
}
result += new String(bytes);
}
} catch (EOFException e){
} catch (IOException e) {
e.printStackTrace();
if (sign-begin>0){
result += new String(bytes);
}
}
// FLAG_END = false;
if (sign-begin>0){
pageSize = sign - begin;
}
return result;
}
/**
* 向前读取一页
* 这里头比较坑爹,向前读取的时候换算换行什么的不好计算,逆序读取可能会出现换行成鬼了
* 如果考虑到是正常的向后翻页之后直接向前翻页就比较好计算了直接逆序读取就行了,现在就是按照这种思路进行处理的
*
* @return
*/
public String readForwardPageUTF8() {
FLAG_BEGIN = true;
long sign = begin;//标记当前页读取了几多字符
String result = "";//读取结果字符串
byte[] bytes = new byte[3 * lenW];//汉语占三个字节所以byte最大应该为3倍的lenW
try {
for (int i = 0; i <lenH; i++) {//读取能显示的最大行数
bytes = new byte[3 * lenW];
int l = 0;//l用来标记bytes的坐标
for (int k = 0; k < 2 * lenW; k++) {//读取行宽,按英文字符计算,应为2倍的lenW
randomAccessFile.seek(--sign);
bytes[++l] = randomAccessFile.readByte();
if (bytes[l] == 10) {//遇到换行符处理,将换行符加到每一行的后面,保证和顺序读取相同
if (!FLAG_END){//这里不进行任何处理继续读取
FLAG_END = true;
// if (randomAccessFile.readByte() == 13) {
// bytes[++l] = 13;
// randomAccessFile.seek(--sign);
// } else {
// randomAccessFile.seek(sign);
// }
}else {//FLAG_END为false的时候呢,不读换行符,但要跳转到下一行,表示一行读完
FLAG_END = false;
bytes[l] = 0;
randomAccessFile.seek(++sign);
if (i == 0&&FLAG_BEGIN){
i--;
FLAG_BEGIN = false;
}
break;
}
// bytes[l] = 0;
// randomAccessFile.seek(++sign);
} else if (bytes[l] < 0) {//汉字读取
if (l < (3 * lenW - 2)) {//当前行剩余的空间能够战胜一个汉字
//汉字在utf-8中占三个字节,读取一个字节后再额外读两个字节
randomAccessFile.seek(--sign);
bytes[++l] = randomAccessFile.readByte();
randomAccessFile.seek(--sign);
bytes[++l] = randomAccessFile.readByte();
k++;//汉字展示是英文的两倍的宽度,所以占的字符宽度要+1
} else {
bytes[l] = 0;
randomAccessFile.seek(sign);
break;
}
}
}
int sizeT = 3 * lenW;
//逆转数组
byte[] bytesT = new byte[sizeT];
for (int m = 0; m < sizeT; m++) {
bytesT[m] = bytes[sizeT - m - 1];
}
result = new String(bytesT) + result;
// FLAG_END = false;
}
} catch (IOException e) {
e.printStackTrace();
if (sign ==-1){
int sizeT = 3 * lenW;
//逆转数组
byte[] bytesT = new byte[sizeT];
for (int m = 0; m < sizeT; m++) {
bytesT[m] = bytes[sizeT - m - 1];
}
result = new String(bytesT) + result;
}
}
try {
randomAccessFile.seek(sign);
} catch (IOException e) {
e.printStackTrace();
}
if(begin-sign>0){
pageSize = begin - sign;
}
begin = sign;
result = readPageUTF8();
return result;
}
/**
* 读取下一页
*/
public String nextPage() {
begin += pageSize;
String result = readPageUTF8();
if (result.equals("")) {
begin -= pageSize;
}
return result;
}
/**
* 读取前一页
*/
public String prePage() {
String result = readForwardPageUTF8();
if (result.equals("")) {
begin += pageSize;
}
return result;
}
public void setLenW(int lenW) {
this.lenW = lenW;
}
public void setLenH(int lenH) {
this.lenH = lenH;
}
@Deprecated
public void setCurPageLen(int curPageLen) {
this.curPageLen = curPageLen;
}
/**
* 获取当前页面所占字节大小
*/
public long getPageSize() {
return pageSize;
}
}