需要图片集和源码请点赞关注收藏后评论区留言~~~

一、需求描述

在手机上浏览电子书的浏览体验跟阅读纸质书差不多,翻页过程仍旧呈现纸张翻转的视觉特效,让读者看起来赏心悦目。总结一下,手机阅读无非是要具有两大功能点:其一为书架管理,主要是书籍的增删改查,其二为浏览操作,主要是翻页过程的处理

二、功能分析

电子书有几个问题,一方面是电子书格式多样,另一方面是Android没有现成的控件可以统一显示这些电子书,格式各异的电子书要在手机屏幕的方寸之间展示,十分困难。

对于前一个问题。可将电子书统一成少数几种公共格式以便降低编码难度,对于后一个问题,可将电子书的每个页面都转成图片文件,然后利用图像视图浏览电子书。

接下来的实战项目中,暂且只支持两种格式的电子书,分别是PDF,一种与平台无关的电子文件和DJVU。

接下来分析电子书阅读器中用到的一些技术

资产管理器AssetManager 初始的五本演示电子书

数据库框架Room 每本电子书的图书名称 作者 页数统一保存到数据库中

PDF文件渲染器 把PDF文件解析为一组图片

贝塞尔曲线 在浏览电子书的翻页过程中 利用它实现翻页特效

JNI接口

图片文件处理

输入对话框

下面简单介绍一下主要代码模块之间关系

EbookReaderActivity 电子书阅读器的书籍列表页面

PdfRenderActiviity  PDF电子书的阅读页面

PdfSlideActivity PDF电子书的平滑翻页

PdfCurveActivity 贝塞尔曲线翻页

PdfOpenglActivity 卷曲翻页

DvjuRenderActivity DJVU电子书的阅读页面

ImageFragment 每页电子书图片的展示碎片

三、效果展示

效果展示如下 既有普通的平滑翻页 也有贝塞尔曲线和卷曲翻页 看起来更加逼真和赏心悦目

四、代码

Ebook类

package com.example.ebook;import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;import com.example.ebook.adapter.BookListAdapter;
import com.example.ebook.dao.BookDao;
import com.example.ebook.entity.BookInfo;
import com.example.ebook.util.AssetsUtil;
import com.example.ebook.widget.InputDialog;import java.util.ArrayList;
import java.util.List;public class EbookReaderActivity extends AppCompatActivity implementsAdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {private static final String TAG = "EbookReaderActivity";private ListView lv_ebook; // 声明一个用于展示书籍列表的列表视图对象private String[] mFileNameArray = {"tangshi.pdf", "android.pdf", "zhugeliang.djvu", "dufu.djvu", "luyou.djvu"};private List<BookInfo> mBookList = new ArrayList<>(); // 书籍信息列表private BookListAdapter mAdapter; // 声明一个书籍列表的适配器对象private BookDao bookDao; // 声明一个书籍的持久化对象@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_ebook_reader);getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 保持屏幕常亮initView(); // 初始化视图new Thread(() -> copyPdfFile()).start(); // 启动演示文件的复制线程}// 初始化视图private void initView() {Toolbar tl_head = findViewById(R.id.tl_head);tl_head.setTitle("电子书架");setSupportActionBar(tl_head); // 替换系统自带的ActionBar// 设置工具栏左侧导航图标的点击监听器tl_head.setNavigationOnClickListener(view -> finish());lv_ebook = findViewById(R.id.lv_ebook);}// 把assets目录下的演示文件复制到存储卡private void copyPdfFile() {// 从App实例中获取唯一的书籍持久化对象bookDao = MainApplication.getInstance().getBookDB().bookDao();mBookList = bookDao.queryAllBook(); // 获取所有书籍记录if (mBookList!=null && mBookList.size()>0) {runOnUiThread(() -> initBookList()); // 初始化书籍列表return;}List<BookInfo> bookList = new ArrayList<>();for (String file_name : mFileNameArray) {String dir = String.format("%s/%s/",getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(),file_name.substring(file_name.lastIndexOf(".")+1));String fileName = file_name.substring(file_name.lastIndexOf("/") + 1);// 把资产目录下的电子书复制到存储卡AssetsUtil.Assets2Sd(this, fileName, dir + fileName);bookList.add(new BookInfo(file_name));}bookDao.insertBookList(bookList); // 把演示用的电子书信息添加到数据库runOnUiThread(() -> initBookList()); // 初始化书籍列表}@Overrideprotected void onRestart() {super.onRestart();initBookList(); // 初始化书籍列表}// 初始化书籍列表private void initBookList() {mBookList = bookDao.queryAllBook(); // 获取所有书籍记录// 下面把书籍列表通过ListView展现出来mAdapter = new BookListAdapter(this, mBookList);lv_ebook.setAdapter(mAdapter);lv_ebook.setOnItemClickListener(this);lv_ebook.setOnItemLongClickListener(this);}// 在点击书籍记录时触发@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {String file_name = mBookList.get(position).getFileName();String title = mBookList.get(position).getTitle();Log.d(TAG, "file_name="+file_name+", title="+title);if (file_name.endsWith(".pdf")) { // PDF格式// 跳转到PDF阅读界面startReader(file_name, title, PdfRenderActivity.class);} else if (file_name.endsWith(".djvu")) { // DJVU格式// 跳转到第三方Vudroid提供的阅读界面startReader(file_name, title, DjvuRenderActivity.class);} else {Toast.makeText(this, "暂不支持该格式的电子书", Toast.LENGTH_SHORT).show();}}// 在长按书籍记录时触发@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {// 以下创建并弹出标题填写对话框InputDialog dialog = new InputDialog(this, mBookList.get(position).getFileName(),position, "请输入书籍名称", (idt, content, seq) -> {BookInfo book = mBookList.get(seq);book.setTitle(content);bookDao.updateBook(book); // 更新数据库中该书籍记录的标题});dialog.show();return true;}// 启动指定的电子书阅读界面private void startReader(String file_name, String title, Class<?> cls) {Intent intent = new Intent(this, cls);intent.putExtra("file_name", file_name);intent.putExtra("title", title);startActivity(intent);}}

PDF类

package com.example.ebook;import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewGroup;import com.example.ebook.util.AssetsUtil;
import com.example.ebook.util.Utils;
import com.example.ebook.widget.CurveView;import java.util.ArrayList;
import java.util.List;public class PdfCurveActivity extends AppCompatActivity {private final static String TAG = "PdfCurveActivity";private CurveView cv_book; // 声明一个卷曲视图对象private List<String> mPathList = new ArrayList<>(); // 图片路径列表private String mFileName = "tangshi.pdf"; // 演示文件的名称@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pdf_curve);initView(); // 初始化视图// 加载pdf会花一点点时间,这里先让整个界面出来,再慢慢渲染pdfnew Handler(Looper.myLooper()).post(() -> renderPDF());}// 初始化视图private void initView() {String title = "";// 从前个页面传来的数据中获取书籍的标题和文件名称if (getIntent().getExtras()!=null && !getIntent().getExtras().isEmpty()) {title = getIntent().getStringExtra("title");mFileName = getIntent().getStringExtra("file_name");}Toolbar tl_head = findViewById(R.id.tl_head);tl_head.setTitle(!TextUtils.isEmpty(title) ? title : mFileName);// 设置工具栏左侧导航图标的点击监听器tl_head.setNavigationOnClickListener(view -> finish());cv_book = findViewById(R.id.cv_book);findViewById(R.id.btn_resume).setOnClickListener(v -> cv_book.reset());}// 开始渲染PDF文件private void renderPDF() {// 把资产文件转换为图片路径列表mPathList = AssetsUtil.getPathListFromPdf(this, mFileName);Log.d(TAG, "mPathList.size="+mPathList.size());Bitmap first = BitmapFactory.decodeFile(mPathList.get(0));int height = (int)(1.0*first.getHeight()/first.getWidth() * Utils.getScreenWidth(this));Log.d(TAG, "height="+height);ViewGroup.LayoutParams params = cv_book.getLayoutParams();params.height = height; // 根据书页图片的尺寸调整卷曲视图的高度cv_book.setLayoutParams(params); // 设置卷曲视图的布局参数cv_book.setFilePath(mPathList); // 设置卷曲视图的文件路径}}

卷曲翻页类

package com.example.ebook;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.pdf.PdfRenderer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewGroup.LayoutParams;import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;import com.example.ebook.util.AssetsUtil;
import com.example.ebook.util.BitmapUtil;
import com.example.ebook.util.Utils;import java.io.File;
import java.util.ArrayList;import fi.harism.curl.CurlPage;
import fi.harism.curl.CurlView;public class PdfOpenglActivity extends AppCompatActivity {private final static String TAG = "PdfOpenglActivity";private CurlView cv_content; // 声明一个卷曲视图对象private ArrayList<String> mImgList = new ArrayList<>(); // 图片路径列表private String mFileName = "tangshi.pdf"; // 演示文件的名称@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pdf_opengl);initView(); // 初始化视图// 加载pdf会花一点点时间,这里先让整个界面出来,再慢慢渲染pdfnew Handler(Looper.myLooper()).post(() -> renderPDF());}// 初始化视图private void initView() {String title = "";// 从前个页面传来的数据中获取书籍的标题和文件名称if (getIntent().getExtras()!=null && !getIntent().getExtras().isEmpty()) {title = getIntent().getStringExtra("title");mFileName = getIntent().getStringExtra("file_name");}Toolbar tl_head = findViewById(R.id.tl_head);tl_head.setTitle(!TextUtils.isEmpty(title) ? title : mFileName);// 设置工具栏左侧导航图标的点击监听器tl_head.setNavigationOnClickListener(view -> finish());cv_content = findViewById(R.id.cv_content);}// 开始渲染PDF文件private void renderPDF() {String dir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/pdf/";String filePath = dir + mFileName;// 无法直接从asset目录读取PDF文件,只能先把PDF文件复制到存储卡,再从存储卡读取PDFAssetsUtil.Assets2Sd(this, mFileName, filePath);try {// 打开存储卡里指定路径的PDF文件ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(filePath), ParcelFileDescriptor.MODE_READ_ONLY);// 创建一个PDF渲染器PdfRenderer pdfRenderer = new PdfRenderer(pfd);Log.d(TAG, "page count=" + pdfRenderer.getPageCount());// 依次处理PDF文件的每个页面for (int i = 0; i < pdfRenderer.getPageCount(); i++) {// 生成该页图片的保存路径String imgPath = String.format("%s/%03d.jpg", dir, i);mImgList.add(imgPath);// 打开序号为i的页面PdfRenderer.Page page = pdfRenderer.openPage(i);// 创建该页面的临时位图Bitmap bitmap = Bitmap.createBitmap(page.getWidth(), page.getHeight(),Bitmap.Config.ARGB_8888);bitmap.eraseColor(Color.WHITE); // 将临时位图洗白// 渲染该PDF页面并写入到临时位图page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);BitmapUtil.saveImage(imgPath, bitmap); // 把位图对象保存为图片文件page.close(); // 关闭该PDF页面}pdfRenderer.close(); // 处理完毕,关闭PDF渲染器} catch (Exception e1) {e1.printStackTrace();}// 从指定路径的图片文件中获取位图数据Bitmap bitmap = BitmapFactory.decodeFile(mImgList.get(0));int iv_height = (int)(1.0*bitmap.getHeight()/bitmap.getWidth() * Utils.getScreenWidth(this));showImage(iv_height); // 在卷曲视图上显示位图图像bitmap.recycle(); // 回收位图对象}// 在卷曲视图上显示位图图像private void showImage(int height) {LayoutParams params = cv_content.getLayoutParams();params.height = height;// 设置卷曲视图的布局参数cv_content.setLayoutParams(params);// 设置卷曲视图的书页提供器cv_content.setPageProvider(new PageProvider(mImgList));// 设置卷曲视图的尺寸变更观察器cv_content.setSizeChangedObserver(new SizeChangedObserver());// 设置卷曲视图默认显示第一页cv_content.setCurrentIndex(0);// 设置卷曲视图的背景颜色cv_content.setBackgroundColor(Color.LTGRAY);}// 定义一个加载图片页面的提供器private class PageProvider implements CurlView.PageProvider {private ArrayList<String> mPathArray = new ArrayList<>();public PageProvider(ArrayList<String> pathArray) {mPathArray = pathArray;}@Overridepublic int getPageCount() {return mPathArray.size();}// 在页面更新时触发public void updatePage(CurlPage page, int width, int height, int index) {// 加载指定页面的位图Bitmap front = BitmapFactory.decodeFile(mPathArray.get(index));// 设置书页的纹理page.setTexture(front, CurlPage.SIDE_BOTH);}}// 定义一个监听卷曲视图发生尺寸变更的观察器private class SizeChangedObserver implements CurlView.SizeChangedObserver {@Overridepublic void onSizeChanged(int w, int h) {// 设置卷曲视图的观看模式cv_content.setViewMode(CurlView.SHOW_ONE_PAGE);// 设置卷曲视图的四周边缘cv_content.setMargins(0f, 0f, 0f, 0f);}}}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/book_bg3"android:orientation="vertical"><androidx.appcompat.widget.Toolbarandroid:id="@+id/tl_head"android:layout_width="match_parent"android:layout_height="50dp"android:background="@color/blue_light"app:navigationIcon="@drawable/icon_back" /><ListViewandroid:id="@+id/lv_ebook"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /></LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

Android App开发实战项目之电子书架的实现(附源码 简单易懂 可直接使用)相关推荐

  1. Android App开发实战项目之模仿美图秀秀的抠图工具(附源码和演示视频 简单易懂 可直接使用)

    需要图片集和源码请点赞关注收藏后评论区留言~~~ 所谓抠图神器,就是从一副图片中扣出用户想要的某块区域 一.需求描述 美图的修图功能如此强大,离不开专业的图片加工技术,抠图便是其中重要的一项功能.在A ...

  2. Android App开发实战项目之购物车(附源码 超详细必看)

    需要源码请点赞关注收藏后评论区留言~~~ 一.需求描述 电商App的购物车可谓是司空见惯了,可以知道购物车除了底部有一个结算行,其余部分主要是已加入购物车的商品列表,然后每个商品左边是商品小图,右边是 ...

  3. Android App开发实战项目之仿喜马拉雅的听说书App实现(超详细 附源码和演示视频)

    需要全部源码请点赞关注收藏后评论区留下QQ~~~ 一.需求分析 用户不仅能在平台上收听音频,还能成为内容创作者,总之长音频分享平台需要满足两种角色的使用:一种是作为内容创作者发布自己的音频,另一种是作 ...

  4. Android App开发实战项目之大头贴App功能实现(附源码和演示 简单易上手)

    需要图片集和源码请点赞关注收藏后评论区留言~~~ 一.需求描述 大头贴App有两个特征,第一个是头要大,拿来一张照片后把人像区域裁剪出来,这样新图片里的人头才会比较大,第二个是在周围贴上装饰物,而且装 ...

  5. Android App开发手机阅读中实现平滑翻书效果和卷曲翻书动画实战(附源码 简单易懂 可直接使用)

    需要图片集和源码请点赞关注收藏后评论区留言~~~ 一.平滑翻书效果 与纸质书籍类似,手机上的电子书也有很多页,逐页浏览可采用翻页视图,然而翻页视图犹如一幅从左到右的绵长画卷,与现实生活中上下层叠的书籍 ...

  6. Android App开发手机阅读中PDF文件渲染器的讲解及使用(附源码 简单易懂)

    需要源码和图片集请点赞关注收藏后评论区留言~~~ 一.PDF文件渲染器 Android集成了PDF的渲染操作,从很大程度上方便了开发者,这个PDF文件渲染器便是PdfRenderer.渲染器允许从存储 ...

  7. Android APP:Preference使用详解和实例(附源码)

    Android APP:Preference使用详解和实例 一.Preference 是Android app中重要的控件之一,Settings 模块大部分都是通过Preference 实现的,这里将 ...

  8. Android App开发实战项目之仿手机QQ动感影集动画播放(附源码和演示视频 可直接使用)

    需要图片集和源码请点赞关注收藏后评论区留言~~~ 动感影集就是只要用户添加一张图片,动感影集就能给每张图片渲染不同的动画效果,让原本静止的图片变得活泼起来,辅以各种精致的动画特效,营造一种赏心悦目的感 ...

  9. Android App开发之自定义图形中位图与图形互转、剪裁图形内部区域、给图形添加部件的讲解及实战(附源码 简单易懂)

    需要图片和源码点赞关注收藏后评论区留言~~~ 一.位图与图形互转 Drawable用于在界面上展示图片,Bitmap用于加工图像数据,所以两者之间的转换非常有必要,位图图形BitmapDrawable ...

  10. (基于安卓app开发的毕业设计)智能手机图片管理.(附源码+论文)

    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦.

最新文章

  1. 解析gui-config.json出差_LUA解析json小demo
  2. 哪种脚本语言最适合你!
  3. 安全研究人员最爱的12款Linux发行版
  4. C++经典面试题(最全,面中率最高)
  5. 28.Node.js 函数和匿名函数
  6. JAVA 获取格林威治时间(GMT)
  7. JAVA配置Tomcat
  8. 龟兔赛跑预测(蓝桥杯)
  9. 天翼网关3.0说明书_天翼网关3.0(tewa-708g)续
  10. 计算机三种不同类型的用户账户,计算机应用基础(第2版)教学课件作者陈绥阳第二章.ppt...
  11. 马斯克宣布:特斯拉Model Y本月14日发布,预估售价折合人民币25.8万
  12. 回旋加速器和同步加速器的区别
  13. 新萝卜家园 Ghost XP SP3 电脑城装机版 2011.06+
  14. OSPF之区域间路由
  15. 全球与中国溴酸钠市场深度研究分析报告
  16. 浅谈缓存-注解驱动的缓存 Spring cache介绍
  17. nagios监控安装配置
  18. Mac mini M1使用简单体验(编程、游戏、深度学习)
  19. pdf转json_pdf转长图工具
  20. 如何取消PPT中的动画效果

热门文章

  1. PNG格式小图标的CSS任意颜色赋色技术
  2. java中lastmodified_Java File lastModified()用法及代码示例
  3. ArcBlock 参加美国华盛顿州 Blockchain Unconference
  4. APM 页面加载耗时校准
  5. Java面试快问快答-Instrument机制
  6. 刨根究底字符编码之九——字符编码方案的演变与字节序
  7. jupyter notebook更换浏览器一直不成功
  8. iOS开发系列--NextPrevious On-Demand Resource Basics
  9. java 富文本 过滤xss_富文本XSS过滤
  10. 2022香港ib成绩公布,实在是太高了