Android实战之小说阅读器,带有水平翻页,记录上次读取页码数_安卓怎么计算小说章节页码-程序员宅基地

技术标签: android应用  阅读器  SQLite  小说  Android  

实现的效果图:


实现的步骤:

一,建立布局文件:3个布局xml

1,activity_main.xml:显示sdcard卡中所有的文件夹及文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/title_text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:text="当前位置: /mnt/sdcard"
        android:textSize="14sp" />
    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"
        android:cacheColorHint="#00000000" >
    </ListView>
</LinearLayout>

2.file_line.xml主要填充list中的文本数据

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >
    <TextView
        android:id="@+id/file_img"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1" />
    <TextView
        android:id="@+id/file_name"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="4"
        android:textSize="14sp" />
</LinearLayout>

3.activity_detail.xml:这是小说显示的显示界面

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/txt_title"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" /> 

//这里自定义一个MyView用以显示小说的文本内容
    <com.example.ebook.view.MyView
        android:id="@+id/txt_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"
       />
<TextView
        android:id="@+id/txt_page"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center"
        android:textColor="#000000"
        android:textSize="14sp" />
</LinearLayout>

二、java代码部分:现在com.example.ebook.dbc包中创建DataBaseConnection类中建两个数据库:txt.db  和  page.db

package com.example.ebook.dbc;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DataBaseConnection extends SQLiteOpenHelper {
public DataBaseConnection(Context ctx){
super(ctx,"txt.db",null,1);
}
public DataBaseConnection(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
//初始化文本表
String sql = "CREATE TABLE txt (" +
"id integerprimarykey," +
"full_path text ," +
"now_page integer ," +
"over_flag integer " +
")";
db.execSQL(sql);
//初始化页码表
sql ="CREATE TABLE page(" +
"id integerprimary key," +
"txt_id integer ," +
"page_num integer ," +
"content text " +
")";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}
}

2.在util包中建类Globals:

package com.example.ebook.util;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import com.example.ebook.R;
import com.example.ebook.dbc.DataBaseConnection;
public class Globals {
public static int SCREEN_WIDTH;
public static int SCREEN_HEIGHT;

//建立一个map集合,里面封装了所有的扩展名对应的图片,以便进行文件图片的显示
public static Map<String,Integer> allIconImgs = new HashMap<String,Integer>();
public static DataBaseConnection dbc;
//每行显示的字数
public static int LINE_CHAR_COUNT = 20;
//字符间隔
public static int CHAR_SEP = 2;
//页边距
public static int PAGE_SEP = 20;
//每个字的大小
public static int CHAR_SIZE;
//行间隔
public static int LINE_SEP;
//计算行数
public static int LINE_COUNT;
//每一行文本之间的间隔文字
public static String TXT_SEP_FLAG = "LINE_SEP_FLAG_BU_NENG_CHONG_FU";

public static void init(Activity a){
SCREEN_WIDTH = a.getWindowManager().getDefaultDisplay().getWidth();
SCREEN_HEIGHT = a.getWindowManager().getDefaultDisplay().getHeight();

//初始化数据库
dbc = new DataBaseConnection(a);
dbc.getWritableDatabase();

//初始化所有扩展名和图片的对应的关系
allIconImgs.put("txt", R.drawable.txt_file);
allIconImgs.put("mp3", R.drawable.mp3_file);
allIconImgs.put("mp4", R.drawable.mp4_file);
allIconImgs.put("bmp", R.drawable.image_file);
allIconImgs.put("gif", R.drawable.image_file);
allIconImgs.put("png", R.drawable.image_file);
allIconImgs.put("jpg", R.drawable.image_file);
allIconImgs.put("dir_open", R.drawable.open_dir);
allIconImgs.put("dir_close", R.drawable.close_dir);

//计算数字= 屏幕宽度“字”总宽度/行字数 
//屏幕宽度“字”总宽度 = 屏幕宽度 - 页边距 * 2 - 字符间隔宽度
//字符间隔宽度 = (字符数-1)* 字符间距
CHAR_SIZE =(SCREEN_WIDTH - (PAGE_SEP * 2) - (LINE_CHAR_COUNT - 1)
* CHAR_SEP)/LINE_CHAR_COUNT;
//只使用屏幕高度的4/5来显示文字内容,计算所显示出的行数
//行数 = (屏幕*4/5)/(字的高度+行间距)
LINE_COUNT = SCREEN_HEIGHT * 4 / 5 / (CHAR_SIZE + LINE_SEP);
}

}

3.在com.example.ebook.util中添加TxtDAOUtil类

package com.example.ebook.util;
import java.util.HashMap;
import java.util.Map;
import android.database.Cursor;
public class TxtDAOUtil {
public static void insertData(String fullPath){
//先判断之前是否添加过
String sql = "SELECT id FROM txt WHERE full_path =?";
Cursor c = Globals.dbc.getReadableDatabase().rawQuery(sql,
new String[]{fullPath});

if(!c.moveToFirst()){
//数据库中没有这条数据,就需要添加,默认设置用户打开的页面是第一页, 同时还没有完成分页的操作
sql = "INSERT INTO txt (full_path,now_page,over_flag) VALUES (?,1,0)";

Globals.dbc.getWritableDatabase().execSQL(sql,new Object[]{fullPath});
}
c.close();
}

public static void updateOverFlag(int id){
String sql = "UPDATE txt SET over_flag =1 WHERE id =?";
Globals.dbc.getWritableDatabase().execSQL(sql, new Object[]{id});
}

public static void updateNowPage(int id,int nowPage){
String sql = "UPDATE txt SET now_page =? WHERE id =?";
Globals.dbc.getWritableDatabase().execSQL(sql, new Object[]{nowPage,id});
}

public static Map<String,Object> getTxtDataByFullPath(String fullPath){
Map<String,Object> map = new HashMap<String,Object>();
String sql = "SELECT id,now_page,over_flag FROM txt WHERE full_path = ?";
Cursor c = Globals.dbc.getReadableDatabase().rawQuery(sql,
new String[]{fullPath});
c.moveToFirst();
map.put("id", c.getInt(0));
map.put("nowPage", c.getInt(1));
map.put("overFlag", c.getInt(2));

return map;
}
}

4在com.example.ebook.util中建立PageDAOUtil

package com.example.ebook.util;
import java.util.Map;
import android.database.Cursor;
public class PageDAOUtils {
public static void insertData(Map<String, Object> map) {
String sql = "INSERT INTO page (txt_id,page_num,content) VALUES (?,?,?)";
Globals.dbc.getWritableDatabase().execSQL(
sql,
new Object[] { map.get("txtId"), map.get("pageNum"),
map.get("content") });
}
public static int getAllCount(int txtId) {
String sql = "SELECT COUNT(*) FROM page WHERE txt_id = ?";
Cursor c = Globals.dbc.getReadableDatabase().rawQuery(sql,
new String[] { String.valueOf(txtId) });
c.moveToFirst();
int count = c.getInt(0);
c.close();
return count;
}

public static void deletePageData(int txtId) {
String sql = "DELETE FROM page WHERE txt_id =?";
Globals.dbc.getWritableDatabase().execSQL(sql, new Object[] { txtId });
}

public static String getPageContent(int txtId, int pageNum) {
String sql = "SELECT content FROM page WHERE txt_id = ? AND page_num = ?";
Cursor c = Globals.dbc.getReadableDatabase()
.rawQuery(
sql,
new String[] { String.valueOf(txtId),
String.valueOf(pageNum) });

c.moveToFirst();
String content = c.getString(0);

c.close();
return content;
}
}

5. com.example.ebook.adapter中建立FileAdapter,这是解决sdcard卡显示布局的

package com.example.ebook.adapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.example.ebook.R;
import com.example.ebook.util.Globals;
import android.content.ContentValues;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class FileAdapter extends BaseAdapter {
private Context ctx;
private List<Map<String,Object>> allValues = new ArrayList<Map<String,Object>>();
public FileAdapter(Context ctx, List<Map<String, Object>> allValues) {
this.ctx = ctx;
this.allValues = allValues;
}

@Override
public int getCount() {
return allValues.size();
}

@Override
public Object getItem(int position) {
return allValues.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null){
convertView = LayoutInflater.from(ctx).inflate(R.layout.file_line, 
null);

//设置高度
convertView.setLayoutParams(new LayoutParams(
LayoutParams.MATCH_PARENT, Globals.SCREEN_HEIGHT /9));
}

//取得图片组件
TextView fileImg = (TextView) convertView.findViewById(R.id.file_img);
fileImg.getLayoutParams().height = Globals.SCREEN_HEIGHT/9;
TextView fileName = (TextView) convertView.findViewById(R.id.file_name);
//取得数据,设置到组件里
Map<String,Object> map = allValues.get(position);
//设置内容,文字
fileName.setText(map.get("fileName").toString());
//图片要根据扩展名取得
String extName = map.get("extName").toString();
//取得图片的id
int imgId = Globals.allIconImgs.get(extName);
//设置图片
fileImg.setBackgroundResource(imgId);
return convertView;
}
}

6.在package com.example.ebook中建立一个MainActivity类

package com.example.ebook;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.widget.TextView;
import com.example.ebook.adapter.FileAdapter;
import com.example.ebook.util.Globals;
import com.example.ebook.util.TxtDAOUtil;

public class MainActivity extends Activity {
private TextView titleText;
private ListView list;
private FileAdapter adapter;
private List<Map<String,Object>> allValues = new ArrayList<Map<String,Object>>();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Globals.init(this);
setContentView(R.layout.activity_main);
//取得组件
titleText = (TextView) findViewById(R.id.title_text);
list = (ListView) findViewById(R.id.list);

//准备数据
//取得SD卡根目录
File root = Environment.getExternalStorageDirectory();
loadFileData(root);

//建立Adapter
adapter = new FileAdapter(this, allValues);
list.setAdapter(adapter);
//加入监听事件
list.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
//取得当前操作的数据
Map<String,Object> map = allValues.get(arg2);
//判断所有点的是文件还是文件夹
boolean dirFlag = (Boolean) map.get("dirFlag");
if(dirFlag){
//文件夹
//建立该文件的File对象
//取得绝对路劲
String fullPath = (String) map.get("fullPath");
//建立File
File dir = new File(fullPath);

//先清空原有的数据
allValues.clear();

if(!Environment.getExternalStorageDirectory()
.getAbsolutePath().equals(fullPath)){

//加入返回上一级的操作数
Map<String,Object> parent = new HashMap<String,Object>();
parent.put("fileName", "返回上一级");
parent.put("extName", "dir_open");
parent.put("dirFlag", true);
parent.put("fullPath", dir.getParent());

//保存一个标志
parent.put("flag", "TRUE");
//将这一行加入到数据集合中
allValues.add(parent);
}

//加入新数据
loadFileData(dir);
//使用Adapter通知界面ListView,数据已经被修改了,你也要一起改
adapter.notifyDataSetChanged();
}else{
//将该文件保存到文本表中
TxtDAOUtil.insertData(map.get("fullPath").toString());

//切换界面
Intent in = new Intent(MainActivity.this,DetailActivity.class);
in.putExtra("fullPath", map.get("fullPath").toString());
startActivity(in);
}
}
});
list.setOnItemLongClickListener(new OnItemLongClickListener() {

@Override
public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
final int arg2, long arg3) {
//取得数据
Map<String,Object> map = allValues.get(arg2);
final File f = new File(map.get("fullPath").toString());

if(f.isFile()){
//弹出确认框
Builder builder = new Builder(MainActivity.this);
builder.setTitle("提示");
builder.setMessage("确认要删除该文件(" + f.getName() + ")吗?");
builder.setPositiveButton("确定",new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
//将SD卡中的文件删除
if(f.exists()){
f.delete();
}

//将列表中的数据删除
allValues.remove(arg2);
//通知
adapter.notifyDataSetChanged();
}
});
builder.setNegativeButton("取消", new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {

}
});
builder.create().show();
}
return false;
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//根据keyCode判断用户按下了那个键
if(keyCode == KeyEvent.KEYCODE_BACK){
//判断当前是否在SD卡根目录下。
//取得第一行数据
Map<String,Object> map = allValues.get(0);
if("TRUE".equals(map.get("flag"))){
//里面,需要返回上一级
list.performItemClick(list.getChildAt(0),
0, list.getChildAt(0).getId());
}else{
//弹出提示框
Builder builder = new Builder(MainActivity.this);
builder.setTitle("提示");
builder.setMessage("亲,真的要离开我吗?");
builder.setPositiveButton("真的", new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.setNegativeButton("取消", new OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {

}
});
builder.create().show();
}
return false;
}

return super.onKeyDown(keyCode, event);
}

private void loadFileData(File dir) {
//列出该目录下的所有文件
File[] allFiles = dir.listFiles();
//设置当前位置的提示信息
titleText.setText("当前位置:"+ dir.getAbsolutePath());

//判断
if(allFiles != null){
//循环
for(int i=0;i<allFiles.length;i++){
File f = allFiles[i];
Map<String,Object> map = new HashMap<String,Object>();
map.put("fileName", f.getName());
//多保存一个文件的绝对路径,方便在进行点击时使用
map.put("fullPath", f.getAbsolutePath());
//判断是文件夹还是文件
if(f.isDirectory()){
//是文件夹
map.put("extName", "dir_close");
map.put("dirFlag", true);
}else{
//是文件,截取出扩展名
String extName = f.getName()
.substring(f.getName().lastIndexOf(".")+1)
.toLowerCase();
map.put("extName", extName);
map.put("dirFlag", false);
}
//只有文件夹或是文本文件才要显示
if(f.isDirectory() || "txt".equals(map.get("extName"))){
allValues.add(map);
}
}
}
}
}


7.在 com.example.ebook.view中建立MyView类

package com.example.ebook.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import com.example.ebook.DetailActivity;
import com.example.ebook.R;
import com.example.ebook.util.Globals;
import com.example.ebook.util.PageDAOUtils;
public class MyView extends View {
private int pageNum;
private int txtId;

// 多查询上一页和下一页的文本,作为缓存保存下来,一边翻页时显示使用
private String preContent;
private String content;
private String nextContent;

// 加入监听,可以让当前的内容,跟着手指一起移动
// 开始时按下的位置
private float startX = 0;
// 移动后的位置
private float nowX = 0;
// 准备好TxtPage
private TextView txtPage;
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// 为自己加入监听,这里的点击事件不需要实现,但是要写
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
}
});
this.setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
// 判断当前操作的状态
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// 按下是,记录横坐标的位置
startX = event.getX();
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
nowX = event.getX();
// 平移界面中绘制的内容。
postInvalidate();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
// 翻页操作
// 判断手指的位置
nowX = event.getX();
if (Math.abs(nowX - startX) < 50) {
// 不变
} else if (nowX > startX) {
// 翻上一页
if (pageNum > 1) {
pageNum--;
// 重新查询当前页的数据
changePage();
}
} else {
// 翻下一页
if (pageNum < PageDAOUtils.getAllCount(txtId)) {
pageNum++;
changePage();
}
}
nowX = 0;
startX = 0;
}
return false;
}

});
}

public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

public void initPage(int txtId, int pageNum) {
this.txtId = txtId;
this.pageNum = pageNum;
changePage();
}
private void changePage() {
// 查询当前页的文本内容,并刷新显示到界面上
content = PageDAOUtils.getPageContent(txtId, pageNum);
// 同时查询出上一页和下一页的数据
if (pageNum > 1) {
// 有上一页
preContent = PageDAOUtils.getPageContent(txtId, pageNum - 1);
} else {
preContent = null;
}
if (pageNum < PageDAOUtils.getAllCount(txtId)) {
// 有下一页
nextContent = PageDAOUtils.getPageContent(txtId, pageNum + 1);
} else {
nextContent = null;
}

DetailActivity a = (DetailActivity) getContext();

if (txtPage == null) {
txtPage = (TextView) a.findViewById(R.id.txt_page);
}
// 改变底部的显示文字
txtPage.setText("分页完成,当前:" + pageNum + "/"
+ PageDAOUtils.getAllCount(txtId));

// 修改Activity中的nowPage
a.setNowPage(pageNum);
// 刷新显示内容
super.postInvalidate();
}
// 绘制组件中的具体内容
@Override
protected void onDraw(Canvas canvas) {
// 先清空之前绘制的所有内容
super.onDraw(canvas);
if (content != null) {
// 拆分每页的文本,变成每行的文本形式
String[] allStr = content.split(Globals.TXT_SEP_FLAG);

// 建立绘制参数对象
Paint paint = new Paint();
// 通过该对象设置绘制的颜色,文字大小,填充状态等信息
paint.setTextSize(Globals.CHAR_SIZE);
paint.setColor(Color.BLACK);
// 循环绘制 当前页的文字
for (int i = 0; i < allStr.length; i++) {
for (int j = 0; j < allStr[i].length(); j++) {
// 文字需要根据手指的移动位置有一个偏移
canvas.drawText(String.valueOf(allStr[i].charAt(j)),
Globals.PAGE_SEP + j
* (Globals.CHAR_SIZE + Globals.CHAR_SEP)
+ (nowX - startX), (i + 1)
* (Globals.CHAR_SIZE + Globals.LINE_SEP),
paint);
}
}

// 绘制上一页或下一页的文字
if (nowX < startX && nextContent != null) {
allStr = nextContent.split(Globals.TXT_SEP_FLAG);

// 正在翻向下一页,需要将下一页的内容显示出来
for (int i = 0; i < allStr.length; i++) {
for (int j = 0; j < allStr[i].length(); j++) {
// 文字需要根据手指的移动位置有一个偏移
canvas.drawText(
String.valueOf(allStr[i].charAt(j)),
Globals.PAGE_SEP
+ j
* (Globals.CHAR_SIZE + Globals.CHAR_SEP)
+ (nowX - startX)
+ Globals.SCREEN_WIDTH,
(i + 1)
* (Globals.CHAR_SIZE + Globals.LINE_SEP),
paint);
}
}
}
if (nowX > startX && preContent != null) {
allStr = preContent.split(Globals.TXT_SEP_FLAG);
// 正在翻向上一頁,需要將上一頁的內容显示出来
for (int i = 0; i < allStr.length; i++) {
for (int j = 0; j < allStr[i].length(); j++) {
// 文字需要根据手指的移动位置有一个偏移
canvas.drawText(
String.valueOf(allStr[i].charAt(j)),
Globals.PAGE_SEP
+ j
* (Globals.CHAR_SIZE + Globals.CHAR_SEP)
+ (nowX - startX)
- Globals.SCREEN_WIDTH,
(i + 1)
* (Globals.CHAR_SIZE + Globals.LINE_SEP),
paint);
}
}
}
}
}
}

8。最后建立DetailActivity

package com.example.ebook;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import android.R.integer;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
import com.example.ebook.util.Globals;
import com.example.ebook.util.PageDAOUtils;
import com.example.ebook.util.TxtDAOUtil;
import com.example.ebook.view.MyView;
public class DetailActivity extends Activity {

//声明这个handler类
private Handler handler;
//文本信息
private Map<String,Object> txtMap;
private Map<String,Object> pageMap = new HashMap<String,Object>();

private int lineCount;
private int pageNum;
private StringBuilder builder;
private String fullPath;
//取得底部显示分页的信息
private TextView txtPage;
//显示文本内容的标志
private boolean showedFlag = false;
//当前的页数
private int nowPage = 1;
public int getNowPage() {
return nowPage;
}

public void setNowPage(int nowPage) {
this.nowPage = nowPage;
}
private MyView txtContent;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
txtPage = (TextView) findViewById(R.id.txt_page);
txtContent = (MyView) findViewById(R.id.txt_content);

//接受传入的文本,查询数据库中该文件是否完成了分页
fullPath = getIntent().getStringExtra("fullPath");
txtMap  = TxtDAOUtil.getTxtDataByFullPath(fullPath);
//建立Handler
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == 0){
//显示已经处理好的页面
txtPage.setText("已经处理好了:"+(pageNum - 1)+"页的数据");
}else{
txtPage.setText("分页完成,当前:1/"+(pageNum-1));
}
}
};

//判断当前的文本是否被分页完成,如果没有被分页过,就需要自己进行分页处理
if((Integer)(txtMap.get("overFlag"))==0){
//首先这里必须子进程对内容进行读取,防止主线程读取卡屏
Thread t = new Thread(){
@Override
public void run() {
//进行分页的处理,就是要读取文本
try{
//将之前分页处理的结果删除,防止之前分页到一半,用户退出造成问题
PageDAOUtils.deletePageData((Integer) txtMap.get("id"));

//windows上的文本必须转码 
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(
new File(fullPath)),"GBK"));
String line = null;
builder = new StringBuilder();

//计算已经读取的内容,在手机上显示的行数,当满足一页时,将其保存到数据库里,再重新计算行数,以便保存下一页
lineCount = 0;
//当前加入了的页数
pageNum = 1;

while((line = reader.readLine()) != null){
//对line进行拆分
//通过一个循环来截取内容
while(line.length() > 20){
//截取出一行
String tempStr = line.substring(0, 20);
addPageLineData(tempStr);

//截取后半部分,继续循环
line = line.substring(20);
}
//这里应该剩下一行,对该行也进行相同的处理
addPageLineData(line);
}
//剩下不到一页的内容,也要保存到数据库里
pageMap.put("txtId", txtMap.get("id"));
pageMap.put("pageNum", pageNum++);
pageMap.put("content", builder.toString());

PageDAOUtils.insertData(pageMap);
//如果整个文件都读取完成了,但还没有显示数据,就需要自己进行显示处理
if(!showedFlag){
showedFlag = true;
txtContent.initPage((Integer) txtMap.get("id"), 1);
}
//修改状态
TxtDAOUtil.updateOverFlag((Integer) txtMap.get("id"));
handler.sendEmptyMessage(1);
reader.close();
}catch(Exception e){
e.printStackTrace();
}
}
};
t.start();
}else{
//如果已经初始化好了分页
//需要显示出上次看到的位置
showedFlag = true;
txtContent.initPage((Integer)txtMap.get("id"), (Integer)txtMap.get("nowPage"));
}
}
@Override
protected void onDestroy() {
//退出时,需要记录下当前看到的页数
TxtDAOUtil.updateNowPage((Integer) txtMap.get("id"), nowPage);
super.onDestroy();
}
private void addPageLineData(String lineData) throws InterruptedException {
lineCount++;
builder.append(lineData);
builder.append(Globals.TXT_SEP_FLAG);

if(lineCount == Globals.LINE_COUNT){
//满一页,就需要保存
pageMap.put("txtId", txtMap.get("id"));
pageMap.put("pageNum", pageNum++);
pageMap.put("content", builder.toString());

PageDAOUtils.insertData(pageMap);

handler.sendEmptyMessage(0);
//如果已经够了10页,就可以显示了
if(!showedFlag){
showedFlag =  true;
txtContent.initPage((Integer) txtMap.get("id"), 1);
}

Thread.sleep(5);
//添加后,都需要将一些数据清空
builder = new StringBuilder();
lineCount = 0;
}
}
}

全部代码上传完毕,在翻页效果时,有那种说面的效果必须是需要算法的。这里后期的文章里将尝试解决。需要素材或是源码,评价中说明下



版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014078192/article/details/23605819

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan