美文网首页Android-阅读器系列
FBReader的导入及使用

FBReader的导入及使用

作者: 恒泪 | 来源:发表于2019-05-03 17:36 被阅读82次

    前言

    看了整整一个月的FBReader的代码及文章,终于搞懂了一点怎么使用FBReader,现在我向大家分享下怎么使用和修改FBReader,这个是面向小白的,毕竟当初的我看这个阅读器的时候也有点迷茫。第一次写简书,写得不好请见谅。

    在看怎么使用前,先感谢 初见破晓 大佬的 FBReader 源码阅读笔记(一),里面给我们讲解了部分FBReader的源代码,推荐要用这个FBReader的大家先去看一看(虽然说是大佬是讲解了,但是代码还是要自己看的,不要指望不读源代码就能了解怎么改)

    修改的内容

    我的FBReader是基于https://github.com/adolfAn/FBReader_AS修改而来的,做了以下修改
    1、添加了菜单界面
    2、添加了右侧目录界面
    3、添加了txt目录解析
    4、合并了FBReader的库(未精简)

    经测试,支持txt、epub、mobi等格式,至于pdf,大家可以去看看MuPDF或者PDFView,整个FBReader用的还是几年前开源的,听说现在的FBReader已经不开源了,郁闷。

    目录

    1. 为自己的项目导入FBReader
    2. FBReader的常识
    3. 怎么使用FBReaderHelper
    4. 自定义FBReader

    1、怎么为自己的项目导入FBReader

    我的 FBReader GitHub项目地址

    (1)首先,先下载我的FBReader项目,要加FBReader库,肯定要知道项目库到底能不能跑对吧。 我的环境是:Android Studio 3.3.2
    (2)如果能跑了,那就说明你的开发环境是可以的咯,然后把整个fBReader库导入到你自己的项目中,怎么导入呢,这就要大家自己自行百度了。
    (3)添加读写权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    

    (4)接着在自己的主项目中创建Application类文件,并在onCreate中对FBReader进行初始化,并修改static静态域中的字段,填入你的 applicationId。这里的 applicationId 是你的App项目(主项目)build.gradle文件里的 applicationId 字段的值。如果 FBReaderIntents.DEFAULT_PACKAGE 字段不正确,结果会是打开书本后什么都没有,一片空白。

    public class App extends Application {
    
        static {
            //这里需要自己设置自己 build.gradle 里的  applicationId 到DEFAULT_PACKAGE字段
            FBReaderIntents.DEFAULT_PACKAGE = "你应用的applicationId";
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            FBReaderApplication.init(this);
        }
    }
    
    

    (5)初始化工作完成后,接着就是打开书本了,参考提供的 MainActivity.java 文件,复制粘贴 onResumeonPause 函数中的操作作至你的项目Activity中,再赋值onClick事件函数中的语句,该语句为打开FBReader的核心语句。

    
    public class MainActivity extends AppCompatActivity {
    
        private FBReaderHelper fbReaderHelper;
    
        //    private String path = Environment.getExternalStorageDirectory() + "/test.txt";
    //    private String path2 = Environment.getExternalStorageDirectory() + "/test.mobi";
        private String path3 = Environment.getExternalStorageDirectory() + "/test.epub";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            fbReaderHelper = new FBReaderHelper(this);
            findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //必须确保activity有绑定服务才能通过jni获取书本信息
                    fbReaderHelper.bindToService(new Runnable() {
                        @Override
                        public void run() {
                            Book book = fbReaderHelper.getCollection().getBookByFile(path3);
                            FBReader.openBook(MainActivity.this, book, null);
                        }
                    });
                }
            });
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            //对fbreader阅读服务进行绑定
            fbReaderHelper.bindToService(null);
        }
    
        @Override
        protected void onPause() {
            //注销fbreader阅读服务绑定,service只允许绑定一个activity,所以为保证下一个activity能使用阅读服务,必须注销
            fbReaderHelper.unBind();
            super.onPause();
        }
    }
    
    

    (6)运行

    2、FBReader的常识

    在讲使用 FBReaderHelper 前先补充下 FBReader 的常识:
    (1)FBReader在你打开了一本书后,会缓存当前的这本书,尽管你关闭了FBReader,由于静态缓存,书本仍然被保留下来,所以你下次打开书本将会是秒开的,只有你打开另一本书的时候书本缓存才会被覆盖。
    (2)FBReader对书本的读取需要通过JNI交互 ,同时他的数据持久化存储(存进数据库)也通过JNI,所以看不懂书籍解析和数据存储的并且看不懂C++的朋友就有。。。,没关系的,因为,我也看不懂。
    (3)FBReader对图片和其他资源文件的读取都是异步加载的,所以当你要获取图片的时候肯定需要异步回调才能读取到。
    (4)FBReader的中文排版可能有点丑,因为它并不是中国的,所以你看中文的时候可能会觉得有点怪。
    (5)FBReader对文本的读取和显示是以段落划分的,FBReader对字符的定位有3个参数:

        public final int ParagraphIndex;    //段落索引
        public final int ElementIndex;      //词的索引
        public final int CharIndex;         //字母的索引
    

    由于英文单词是由字母组成的,所有会出现字母的索引,但是我们中文是没有的,所以当你使用中文的时候,字母的索引为 0 。FBReader需要这3个参数才能定位到文本的位置。
    (6)FBReader的架构有点老,可能看起来会有点吃力,但是其中也是有可以参考的代码结构的。
    (7)在使用FBReader的时候如果你找不到某个功能在哪个代码里,我可以多用用AS的全局搜索,记得灵活使用。

    3、怎么使用FBReaderHelper

    说了那么多,你只是教了我们怎么导入使用怎么还不给我们解释FBReaderHelper是啥,我™。别急,我现在讲,放下你的板凳。

    FBReaderHelper 类顾名思义,就是方便我们对 FBReader进行操作的帮助类,里面几乎所有方法都是对 FBReader的操作,但其作用不单止在于帮助我们操作,它还能帮助我们理解FBReader的源码,毕竟里面的方法就是从FBReader里面找出来的。

    我把对FBReader的操作理解为3个状态:未读取、预读取、完全读取。
    未读取:顾名思义
    预读取:就是只通过 FBReader获取了书本的Book类和 BookModel类,但是没有打开该书本,没有进入到读书界面。
    完全读取:说多了就是,把整本书打开了,连书本有什么字,有什么图片都显示了出来了。

    (1)像设置字体大小这种阅读配置能在3种状态下进行,因为该设置并不一定需要 BookBookModel

         /**
         * 设置字体大小
         */
        public void setFontSize(int size) {
            myFBReaderApp.ViewOptions.getTextStyleCollection().getBaseStyle().FontSizeOption.setValue(size);
            if (myFBReaderApp.getViewWidget() != null) {
                myFBReaderApp.clearTextCaches();
                myFBReaderApp.getViewWidget().repaint();
            }
        }
    

    (2)但是像获取书本总字数这种就必须先让Activity绑定FBReaderservice才能使用了,因为书本的预读取是需要通过 ServiceJNI交互的,从service 处获取到书本的BookModel 后才能获取书本的总字数和段落数。

        /**
         * 获取书本总字数  应该先确定Collection是否已经绑定服务且已打开书本(完全读取)
         *
         * @return
         */
        public int getSumTextCount() {
            if (myFBReaderApp.Model == null) return 0;
            else {
                return myFBReaderApp.Model.getTextModel().getTextLength(
                        myFBReaderApp.Model.getTextModel().getParagraphsNumber()
                );
            }
        }
        //也可以这样实现(传入一个Book对象进行预读取获取BookModel从而获取字体总数)(预读取)
        public int getSumTextCount(Book book) {
            BookModel bookModel = createBookModel(book);
            return bookModel.getTextModel().getTextLength(
                    bookModel.getTextModel().getParagraphsNumber()
            );
        }
    

    两者的出来的结果都是一样的,区别在于,一个是完全读取书籍,一个是预读取。

    (3)完全读取需要FBReader打开整本书籍,走完打开书本的流程,这样可以获取到书本的所有信息,包括文字图片,例如获取当前页字数就需要读取书籍并显示后才能知道当前页的字数。

        /**
         * 获取当前页的字数量   应该先确定Collection是否已经绑定服务且已打开书本
         *
         * @return
         */
        public int getCurPageWordCount() {
            ZLTextWordCursor stCursor = getStartCursor();
            ZLTextWordCursor edCursor = getEndCursor();
            if (myFBReaderApp.Model == null || edCursor.getParagraphIndex() <= 0)
                return edCursor.getElementIndex();
            return myFBReaderApp.Model.getTextModel().getTextLength(edCursor.getParagraphIndex() - 1) + edCursor.getElementIndex() -
                    myFBReaderApp.Model.getTextModel().getTextLength(stCursor.getParagraphIndex()) + stCursor.getElementIndex();
        }
    

    (4)图片的加载,在FBReader中图片的加载时异步的,所以当你需要获取图片的时候肯定是需要回调的,例如封面图的获取。

        /**
         * 异步加载封面图
         *
         * @param book   FBReader 的 Book 对象
         * @param listener
         */
        public void loadBookCover(@NonNull Book book, @NonNull final OnGetCoverListener listener) {
            PluginCollection pluginCollection = PluginCollection.Instance(Paths.systemInfo(activity));
            BookUtil.getEncoding(book, pluginCollection);
            final ZLImage image = CoverUtil.getCover(book, pluginCollection);
            if (image == null) {
                listener.finish(false, null);
            } else {
                if (image instanceof ZLImageProxy) {
                    ((ZLImageProxy) image).startSynchronization(new AndroidImageSynchronizer(activity), new Runnable() {
                        public void run() {
                            loadCover(image, listener);
                        }
                    });
                } else {
                    loadCover(image, listener);
                }
            }
        }
    

    (5)是否需要预读取或完全读取呢,我已经在 FBReaderHelper 的函数上写上注释了,大家在使用的时候记得区分下,要不然可能会报空指针或者获取到的信息是上一本打开过的书籍的。
    (6)可能FBReaderHelper会有点坑,例如空指针的问题,这个嘛是因为我当时写出来只是为了记录操作方法,如果说所获取到的 myFBReaderApp.Model 为空,你或许需要自己调用 FBReaderHelper.createBookModel()来代替。

    4、自定义FBReader

    1. 修改图片显示的配置
    2. 自定义图片的打开方式
    3. 自定义底部时间栏的显示
    4. 翻页状态的监听
    5. 设置默认配置
    4.1. 修改图片显示的配置

    图片配置的文件在org.geometerplus.fbreader.fbreader.options.ImageOptions.java文件中。

        public ImageOptions() {
            ImageViewBackground =
                    new ZLColorOption("Colors", "ImageViewBackground", new ZLColor(255, 255, 255));
            FitToScreen =
                    new ZLEnumOption<FBView.ImageFitting>("Options", "FitImagesToScreen", FBView.ImageFitting.covers);
            TapAction =
                    new ZLEnumOption<TapActionEnum>("Options", "ImageTappingAction", TapActionEnum.openImageView);
            MatchBackground =
                    new ZLBooleanOption("Colors", "ImageMatchBackground", false);
        }
    

    从上往下数,四个对象分别控制的是图片的背景色,图片的填充缩放类型,图片点击后的行为,图片的遮罩效果。
    主要讲的是第二个对象,如果设置的值是FBView.ImageFitting.covers,则是只有封面图填充整个阅读器可视范围。如果设置的是FBView.ImageFitting.all则是所有图都填充整个阅读器可视范围。
    如果想实现点击图片后不对图片处理或打开可以将TapActionEnum.openImageView换成TapActionEnum.doNothing

    4.2. 自定义图片的打开方式

    图片视图是一个Activity界面,叫org.geometerplus.android.fbreader.image.ImageViewActivity.java,当在阅读界面点击图片时,会调用打开该Activity的代码

         } else if (soul instanceof ZLTextImageRegionSoul) {
                Reader.getTextView().hideOutline();
                Reader.getViewWidget().repaint();
                final String url = ((ZLTextImageRegionSoul) soul).ImageElement.URL;
                if (url != null) {
                    try {
                        final Intent intent = new Intent();
                        intent.setClass(BaseActivity,ImageViewActivity.class);
                        intent.putExtra(ImageViewActivity.URL_KEY, url);
                        intent.putExtra(
                                ImageViewActivity.BACKGROUND_COLOR_KEY,
                                Reader.ImageOptions.ImageViewBackground.getValue().intValue()
                        );
                        OrientationUtil.startActivity(BaseActivity, intent);
                        BaseActivity.overridePendingTransition(R.anim.activity_anim_no, R.anim.activity_anim_no);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } 
    

    只需要修改参考Activity里面的代码编写即可。

    4.3. 自定义底部时间栏的显示

    FBReader默认时间栏和阅读进度显示在底部的,如果需要显示在上方需要修改改动的地方比较多,这里只介绍下如何修改底部栏显示的样式。
    控制底部栏显示的具体代码在 org.geometerplus.fbreader.fbreader.FBView.java文件中,你需要做的是修改其大概位于642行的FooterNewStyle类中的paint()函数。相信看到这个函数内容的人心里或多或少都会有些B数,知道该怎么修改了,在这里我贴下代码。

    private class FooterNewStyle extends Footer {
            public synchronized void paint(ZLPaintContext context) {
    
                final FooterOptions footerOptions = myViewOptions.getFooterOptions();
                final ColorProfile cProfile = myViewOptions.getColorProfile();
                context.clear(cProfile.FooterNGBackgroundOption.getValue());
                context.clear(getBackgroundColor());
    
                final BookModel model = myReader.Model;
                if (model == null) return;
    
                final int left = getLeftMargin();
                final int right = context.getWidth() - getRightMargin();
                final int height = getHeight();
                final int charHeight = setFont(context, height / 2, false);
    
                final PagePosition pagePosition = FBView.this.pagePosition();
    
                context.setTextColor(cProfile.RegularTextOption.getValue());
    
                if (footerOptions.ShowBattery.getValue()) {
                    context.drawString(left, (height + charHeight + 1) / 2, "电池 " + myReader.getBatteryLevel() + "%");
                }
                if (footerOptions.showProgressAsPages()) {
                    String str = pagePosition.Current + "/" + pagePosition.Total;
                    int strWidth = context.getStringWidth(str);
                    context.drawString((right - strWidth+left) / 2 , (height + charHeight + 1) / 2, str);
                }
                if (footerOptions.showProgressAsPercentage() && pagePosition.Total != 0) {
                    String str = pagePosition.Current + "/" + pagePosition.Total + "  " + 100 * pagePosition.Current / pagePosition.Total + "%";
                    int strWidth = context.getStringWidth(str);
                    context.drawString((right - strWidth+left) / 2 , (height + charHeight + 1) / 2, str);
                }
                if (footerOptions.ShowClock.getValue()) {
                    String str = ZLibrary.Instance().getCurrentTimeString();
                    int strWidth = context.getStringWidth(str);
                    context.drawString(right - strWidth, (height + charHeight + 1) / 2, str);
                }
    
            }
        }
    

    实际起效的代码在21行以后,是不是很像我们用过的canvas画板和paint画笔?

    4.4. 翻页状态的监听

    我在org.geometerplus.fbreader.fbreader包下添加了3个Action,分别是PageTurnStartActionPageTurningActionPageTurnEndAction,如果FBReader发生了翻页,那么会分别顺序执行三个Action,如果你想计算翻页后的页面的字体数量,那么可以在PageTurnEndActionrun()函数中编写获取字数的代码。注意:没有翻页动画的时候是不会执行PageTurningAction

    4.5. 设置默认配置

    org.geometerplus.zlibrary.ui.android.library.ZLAndroidLibrary.java中保存着一些应用级别的设置。

    public final ZLBooleanOption ShowStatusBarOption = new ZLBooleanOption("LookNFeel", "ShowStatusBar", false);
        public final ZLBooleanOption OldShowActionBarOption = new ZLBooleanOption("LookNFeel", "ShowActionBar", false);
        public final ZLBooleanOption ShowActionBarOption = new ZLBooleanOption("LookNFeel", "ShowActionBarNew", false);
        public final ZLBooleanOption EnableFullscreenModeOption = new ZLBooleanOption("LookNFeel", "FullscreenMode", true);
        public final ZLBooleanOption DisableButtonLightsOption = new ZLBooleanOption("LookNFeel", "DisableButtonLights", !DeviceType.Instance().hasButtonLightsBug());
        public final ZLIntegerRangeOption BatteryLevelToTurnScreenOffOption = new ZLIntegerRangeOption("LookNFeel", "BatteryLevelToTurnScreenOff", 0, 100, 50);
        public final ZLBooleanOption DontTurnScreenOffDuringChargingOption = new ZLBooleanOption("LookNFeel", "DontTurnScreenOffDuringCharging", true);
        public final ZLIntegerRangeOption ScreenBrightnessLevelOption = new ZLIntegerRangeOption("LookNFeel", "ScreenBrightnessLevel", 0, 100, 0);
    

    这些英文都是比较好看懂的,如果不懂得可以google下。

    先写着这么多吧,其实说多了,如果要改fbreader,真的必须看懂源码,这个文章就当作给写引子吧,灵活一点,多用用ctrl+左键和全局搜索,几乎都能找到你想要找的东西。

    相关文章

      网友评论

        本文标题:FBReader的导入及使用

        本文链接:https://www.haomeiwen.com/subject/vjjznqtx.html