安卓小项目【小说阅读器】

1,布局设置,LiearLayout 上中下三部分,其中小说主题内容部分为自定义view,比例是1:8:1

2,创建数据库表  两张表txt表和page表,两张表进行主外键关联

public class DBHelper extends SQLiteOpenHelper {

    public DBHelper(Context context) {
        super(context, "read.db", null, 1);
    }
    public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table txt(id Integer primry key,full_path text,now_page integer,over_flag integer)");
        db.execSQL("create table page(id integer primary key,txt_id integer,page_num integer,content text)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}
文本表
<span style="font-size:12px;">public class TxtDB {
    public static void insertData(String fullPath) {
        String sql = "select id from txt where full_path=?";
        Cursor c = Globals.helper.getReadableDatabase().rawQuery(sql, new String[]{fullPath});
        if (!c.moveToFirst()) {//如果不存在数据则插入数据
            sql = "insert into txt(id,full_path,now_page,over_flag) values(?,?,?,?)";
            Globals.helper.getWritableDatabase().execSQL(sql, new Object[]{1,fullPath,1,0});
        }
        c.close();
    }
    public static Map<String, Object> loadData(String fullPath) {
        Map<String, Object> map = new HashMap<>();
        String sql = "select id,now_page,over_flag from txt where full_path=?";
        Cursor c = Globals.helper.getReadableDatabase().rawQuery(sql, new String[]{fullPath});
        while (c.moveToNext()) {
            map.put("txtId", c.getInt(0));
            map.put("nowPage", c.getInt(1));
            map.put("overFlag", c.getInt(2));
        }
        c.close();
        return map;
    }

    public static void updateOverFlag(String fullPath) {
        String sql = "update txt set over_flag=1 where full_path=?";
        Globals.helper.getWritableDatabase().execSQL(sql, new Object[]{fullPath});
    }
}
</span>

分页表

<span style="font-size:12px;">public class PageDB {
    private static int pageNum;

    public static void insertDate(Map<String, Object> map) {
        String sql = "insert into page(txt_id,page_num,content) values(?,?,?)";
        Globals.helper.getWritableDatabase().execSQL(sql, new Object[]{map.get("txtId"), map.get("pageNum"), map.get("content")});
    }
    public static String findContent(int txtId, int pageNum) {
        String content = null;
        String sql = "select content from page where txt_id=? and page_num=?";
        Cursor c = Globals.helper.getReadableDatabase().rawQuery(sql, new String[]{txtId + "", pageNum + ""});
        while (c.moveToNext()) {
            content = c.getString(0);
        }
        c.close();
        return content;
    }
    public static int findPageNum(int txtId){
        String sql="select count(*) from page where txt_id=?";
        Cursor c= Globals.helper.getReadableDatabase().rawQuery(sql,new String[]{txtId+""});
        while(c.moveToNext()){
            pageNum=c.getInt(0);
        }
        c.close();
        return pageNum;
    }
}</span>

3,定义一个全局可以使用的类Globals ,在里面设置页面内容,每行显示的字数,字间距,行间距,页边距,获得安卓设备的屏幕宽度和高度

public class Globals {
    public static int  SCREEN_WIDTH;
    public static int  SCREEN_HEIGHT;
    public static  int LINE_CHAR_COUNT=20;//每行显示20个字
    public static  int CHAR_SEP=2;//字间距
    public static  int PAGE_SEP=5;//页边距
    public static int LINE_SEP=2;//行间距
    public static  int CHAR_SIZE;//字符大小,需计算,保证能根据屏幕大小自适应
    public static int LINE_COUNT;//每页显示的行数,需计算。

 public static  void init(Activity a){

        SCREEN_WIDTH=a.getWindowManager().getDefaultDisplay().getWidth();//获得屏幕宽度
        SCREEN_HEIGHT=a.getWindowManager().getDefaultDisplay().getHeight();//获得屏幕高度

        CHAR_SIZE=(SCREEN_WIDTH-PAGE_SEP*2-(LINE_CHAR_COUNT-1)*CHAR_SEP)/LINE_CHAR_COUNT;//计算出字符大小
        LINE_COUNT=SCREEN_HEIGHT*4/5/(CHAR_SIZE+LINE_SEP);//计算每页显示的行数,并跟布局相适应,占屏幕的五分之四

    }

}
4,自定义MyView,滑动翻页
<pre name="code" class="java">public class MyView extends View {
    private String content;
    private MainActivity a;

    private float startX;
    private float nowX;

    private String preContent;//准备上一页和下一页的内容
    private String nextContent;

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        a = (MainActivity) context;
        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();//重新调用onDraw方法
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    if (Math.abs(nowX - startX) >= 50) {//nowX和startX的绝对值大于50 的话才执行翻页
                        if (nowX > startX) {//上一页
                            if (a.getNowPageNum() > 1) {
                                a.setNowPageNum(a.getNowPageNum() - 1);//翻到上一页
                                changeData();
                            }
                        }
                        if (nowX < startX) {//下一页
                            if (a.getNowPageNum() < a.getPageNum()) {
                                a.setNowPageNum(a.getNowPageNum() + 1);//翻到下一页
                                changeData();
                            }
                        }
                    }
                    startX = 0;
                    nowX = 0;
                }
                return false;
            }
        });
    }

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

    public void changeData() {
        content = PageDB.findContent((int) a.getTxtMap().get("txtId"),a.getNowPageNum());
        if (a.getNowPageNum() > 1) {//当前页不是第一页的时候
            preContent = PageDB.findContent((int) a.getTxtMap().get("txtId"), a.getNowPageNum() - 1);
        } else {
            preContent = null;
        }
        if (a.getNowPageNum() < a.getPageNum()) {//当前页不是最后一页
            nextContent = PageDB.findContent((int) a.getTxtMap().get("txtId"),a.getNowPageNum()+ 1);
        } else {
            nextContent = null;
        }
        a.getHandler().sendEmptyMessage(1);
        super.postInvalidate();//调用OnDraw方法;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (content != null) {
            String[] allValues = content.split(Globals.END_FLAG);
            for (int i = 0; i < allValues.length; i++) {
                for (int j = 0; j < allValues[i].length(); j++) {
                    if (j + i * Globals.char_line_conut < content.length()) {
                        Paint paint = new Paint();
                        paint.setColor(Color.BLACK);
                        paint.setTextSize(Globals.char_size);

                        canvas.drawText(String.valueOf(allValues[i].charAt(j)),
                                Globals.page_sep + j * (Globals.char_size + Globals.char_sep) + (nowX-startX),
                                i * (Globals.char_size + Globals.line_sep), paint);
                    }
                }
            }
        }
        if (nowX > startX && preContent != null) {
            String[] allValues = preContent.split(Globals.END_FLAG);
            for (int i = 0; i < allValues.length; i++) {
                for (int j = 0; j < allValues[i].length(); j++) {
                    if (j + i * Globals.char_line_conut < content.length()) {

                        Paint paint = new Paint();
                        paint.setColor(Color.BLACK);
                        paint.setTextSize(Globals.char_size);

                        canvas.drawText(String.valueOf(allValues[i].charAt(j)),
                                Globals.page_sep + j * (Globals.char_size + Globals.char_sep) +(nowX-startX) -Globals.screen_width,
                                i * (Globals.char_size + Globals.line_sep), paint);
                    }
                }

            }
        }
        if (nowX < startX && nextContent != null) {
            String[] allValues = nextContent.split(Globals.END_FLAG);
            for (int i = 0; i < allValues.length; i++) {
                for (int j = 0; j < allValues[i].length(); j++) {
                    if (j + i * Globals.char_line_conut < content.length()) {

                        Paint paint = new Paint();
                        paint.setColor(Color.BLACK);
                        paint.setTextSize(Globals.char_size);

                        canvas.drawText(String.valueOf(allValues[i].charAt(j)),
                                Globals.page_sep + j * (Globals.char_size + Globals.char_sep) +(nowX-startX) +Globals.screen_width,
                                i * (Globals.char_size + Globals.line_sep), paint);
                    }
                }

            }
        }

    }
}


5,将文本存放在sdcard的根目录,以西游记作为示范,获得文本的绝对路径,用字符流进行按行读取

<pre name="code" class="java">public class MainActivity extends AppCompatActivity {
    static MyView contentView;
    static TextView pageView;

    private Map<String, Object> txtMap;
    private Map<String, Object> pageMap = new HashMap<>();

    private StringBuffer sb = new StringBuffer();

    private int lineCount = 0;//每页的行数
    static int pageNum = 1;//总页数
    static int nowPageNum = 1;//当前页数

    private String fullPath;

    static Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0) {
                pageView.setText("正在分页,已经分解:" + pageNum);
            } else if (msg.what == 1) {
                pageView.setText("分页已经完成" + nowPageNum + "/" + pageNum);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Globals.init(this);
        setContentView(R.layout.activity_main);
        contentView = (MyView) findViewById(R.id.content_view);
        pageView = (TextView) findViewById(R.id.page_view);

        fullPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/xiyouji.txt";//获取文件路径
        TxtDB.insertIntoTxt(fullPath);
        txtMap = TxtDB.loadTxtByFullpath(fullPath);

        if ((int) txtMap.get("overFlag") == 0) {
            //还未进行分页
            new Thread() {
                @Override
                public void run() {
                    try {
                        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fullPath), "GBK"));
                        String line = null;
                        while ((line = br.readLine()) != null) {
                            while (line.length() > Globals.char_line_conut) {
                                String str = line.substring(0, Globals.char_line_conut);
                                line = line.substring(Globals.char_line_conut);
                                addLine(str);
                            }
                            addLine(line);//行余下的不足以凑成一行的字数添加成一行
                        }
                        br.close();
                        handler.sendEmptyMessage(1);
                        TxtDB.updateOverFlag(fullPath);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }.start();

        } else {
            //已经分页完成
            pageNum = PageDB.getPageCount((int) txtMap.get("txtId"));
            nowPageNum = (int) txtMap.get("nowPage");
            contentView.changeData();
        }
    }
    @Override
    protected void onDestroy() {
        TxtDB.updateNowPaga((int) txtMap.get("nowPage"), fullPath);
        super.onDestroy();
    }

    public void addLine(String str) {//行凑成页之后存放到数据库中
        sb.append(str);
        sb.append(Globals.END_FLAG);
        lineCount++;
        if (lineCount == Globals.line_count) {
            pageMap.put("txtid", txtMap.get("txtId"));
            pageMap.put("pagenum", pageNum++);
            pageMap.put("content", sb.toString());
            PageDB.insertIntoPageDB(pageMap);

            if (pageNum == 10) {
                contentView.changeData();
            }

            handler.sendEmptyMessage(0);
            sb = new StringBuffer();
            lineCount = 0;//再次初始化供下次循环使用

        }

    }

    public Map<String, Object> getTxtMap() {
        return txtMap;
    }

    public void setTxtMap(Map<String, Object> txtMap) {
        this.txtMap = txtMap;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getNowPageNum() {
        return nowPageNum;
    }

    public void setNowPageNum(int nowPageNum) {
        this.nowPageNum = nowPageNum;
    }

    public Handler getHandler() {
        return handler;
    }

    public void setHandler(Handler handler) {
        this.handler = handler;
    }

    public Map<String, Object> getPageMap() {
        return pageMap;
    }
}
				
时间: 2024-07-29 16:48:32

安卓小项目【小说阅读器】的相关文章

Java小项目之:小说阅读器

Java小项目之:小说阅读器 今天带来的java项目是一款阅读器,老少皆宜,适合练手. 代码展示: package com; import javax.swing.JOptionPane; public class Scroll { private int n; private int size; private Thread t; private static int def_speed = 1000; private static int up_speed = -500; private s

读取本地HTML的小说阅读器应用源码项目

该源码是一个不错的读取本地HTML的小说阅读器,读取本地HTML的小说阅读器,并且源码也比较简单的,非常适合我们的新手朋友拿来学习,有兴趣的朋友研究下. 源码下载: http://code.662p.com/view/10134.html 详细说明:http://android.662p.com/thread-6191-1-1.html

五款PC端小说阅读器 readbook、非常酷阅读器、iSilo、AlReader、haalireader

        本文主要推荐的是功能强大界面简单电脑(windows系统)单机使用的小说阅读器~以阅读TXT之流的主流类小说文件,只想要会联网会更新可以发评论发微博的朋友可以节省点时间,按ctrl+w吧~觉得想要单机软件的朋友请继续往下看~          这篇博文居然还在有人点击和回复,有点出乎我的意料,当初写这篇文章的本意是因为发现想搜一个简单实用的单机阅读器实在是困难重重,搜索结果总是被各大广告阅读器干扰,千辛万苦终于找到之后觉得应该分享一下以飨同好:虽然时间过去了蛮久,但从回复的朋友来

小说阅读器

小说阅读器 启动模块bin/start.py 1 import os 2 import sys 3 4 BASE_PATH=os.path.dirname(os.path.dirname(__file__)) 5 sys.path.append(BASE_PATH) 6 7 8 from core import src 9 10 if __name__== '__main__': 11 src.run() 核心代码模块core/src.py 1 from db import db_handler

《9秒小说阅读器》

<9秒小说阅读器>采用免费开源且跨平台的移动应用开发引擎CrossApp开发,是一款完全开源.免费.跨平台的手机小说下载阅读器,基于最宽松的MIT开源协议,所以开发者可以完全免费.毫无顾虑的使用<9秒小说阅读器>在任何商业行为中,也可直接改名后上线运营,官方会提供每天8小时的免费在线页面问答式服务,每个问题的反馈速度大概在30分钟左右,回复率90%.(可快速导出android版本.ios版本) <9秒小说阅读器>已制作了广告类,开发者只需要申请一个自己的展示广告,更改代

基于Android小说阅读器滑动效果的一种实现

看过小说都知道小说阅读器翻页有好多种效果,比如仿真翻页,滑动翻页,等等.由于某种原因,突然想写一个简单点的滑动翻页效果.在这里写出来也没有什么意图,希望大家可以根据这个效果举一反三,写出其他的效果.图就不上了. 下面是代码:大家理解onTouch事件即可 package com.example.testscroll.view; import android.content.Context; import android.util.AttributeSet; import android.view

电子书阅读器安卓版推荐 PDF阅读器也可轻松编辑文档了

在电子书的各种格式中,你最青睐哪种呢?是mobi.txt.还是PDF,相信很多人和小编一样喜欢阅读 PDF格式的电子书,这时候选择一款合适的PDF阅读器就十分重要了.那么,电子书阅读软件哪个好呢? 在回答这个问题之前,我们先考虑这样一个问题,我们需要一款什么样的PDF阅读器?是只满足基本的阅读功能就可以了么?想必很多人都不满足于这吧.小编今天推荐的这款轻快PDF阅读器的功能就十分齐全,不信且往下看: 一.我们需要什么样的阅读功能? 1.自动添加手机内的PDF文件:这点应该没什么意见吧,毕竟很多人

PDF小说阅读器软件哪个好之轻快PDF阅读器

无论是传统PC端电子书阅读还是手机移动端电子书阅读,都可以找到不少对应的软件.如何选择最适合阅读电子书的PDF阅读器呢,这也正是本期专题要讨论的地方,我们从数十款PDF阅读器当中选择了轻快系列PDF阅读器,并进行了详细的评测,帮助读者更好地了解为什么要选择这样一款阅读工具. 使用平台兼容性 轻快PDF阅读器提供了安卓.苹果和传统PC端软件,也就是说,无论你是手机用户还是PC用户都可以直接使用轻快PDF阅读器,非常省心.这三端在功能上差别并不大,所以我们本次就以PC端作为演示来点评下功能上的优势.

iOS - 小说阅读器分章节,支持正则分章节和按字数分章节

最近做了一个WIFI传书本地阅读功能,有所收获在这里记录下吧. 用户下载的书籍分为两种,一种是有章节格式的,比如 第一章,001章.等,这种可以用正则来直接分章节,还有绝大多数书籍是没有这种格式的,这种如果整本书来直接解析的话,对CPU要求比较大,可能会卡死闪退,所有手动分章节还是很有必要的,这种情况下我们采用按照两千字来分. 话不多说,开始吧. 1.WIFI传书把书传到APP沙盒里,这里我们采用的是 GCDWebServer ,很方便,这里就不做陈述了. 2.将沙盒里面的 .txt 文件转成