android pdf框架-4,分析vudroid源码1

本文从源码角度分析Vudroid PDF阅读器的页面结构,包括页面分割、Page对象、DocumentView、PageTreeNode等。通过理解其解码策略、页面逻辑与物理分割,展示了如何构建一个高效的PDF阅读器。同时,文章讨论了Bitmap缓存及从URI加载PDF的过程,适合Android开发者参考学习。
摘要由CSDN通过智能技术生成

前言

上文基于recyclerview修改,没有自定义layoutmanager,其实并不是一个好的阅读器,缩放功能有限,放大了容易内存溢出.

本文,从修改vudroid源码来完成一个阅读器.

为什么是vudroid?

  • 它是基于view系统的
  • 相对完整的功能
  • 代码没有那么抽象,简洁容易修改
  • 功能没有完善,有修改空间
  • 可以容易做一个sdk对外提供

原作者ebookdroid更好,基于opengl实现的,修改更麻烦,代码非常抽象.已经是一个完整的项目,各项功能都完善了,也没有修改的必要.

先理解它的页面结构

策略模式的应用,让外部的uri在内部不同的策略中选一个显示的activity.

而在解码时又提供了策略模式,针对不同的类型,提供不同的解码实现.

页面分割

它对页面进行了抽象rect(0,0,1.0,1.0);然后对它进行分割.分割的算法按层级.如果当前的缩放度加大了,达到层级需要再放大一级时,就会再分裂.

比如一个页面,分裂成4个,4个页面中的第一块区域,随时缩放级别加大,不满足了,就会继续分裂为4个,层级加一级.

默认的 PageTreeNode里面

private static final int SLICE_SIZE = 256 * 256 * 1;,这里1是旧设备,屏蔽较小,当前的设备都已经1080的了,可以设置为4.

对于page与pagetreenode现在没有什么要修改了.

private void invalidateChildren() {
        boolean isThresholdHit = thresholdHit();
        boolean isVisible = isVisible();
        if (isThresholdHit && children == null && isVisible) {
            final int newThreshold = treeNodeDepthLevel * 2;
            children = new PageTreeNode[]
                    {
                            new PageTreeNode(documentView, new RectF(0, 0, 0.5f, 0.5f), page, newThreshold, this),
                            new PageTreeNode(documentView, new RectF(0.5f, 0, 1.0f, 0.5f), page, newThreshold, this),
                            new PageTreeNode(documentView, new RectF(0, 0.5f, 0.5f, 1.0f), page, newThreshold, this),
                            new PageTreeNode(documentView, new RectF(0.5f, 0.5f, 1.0f, 1.0f), page, newThreshold, this)
                    };
        }
        if (!isThresholdHit && getBitmap() != null || !isVisible) {
            recycleChildren();
        }
    }

当缩放级别大了,需要再分钱时,它会生成4个新的子节点,那么当前的节点就不再有bitmap了.

这是页面的逻辑分割,到具体的物理分割需要一个映射.

getTargetRect(),这个方法是对page.bounds与逻辑大小进行映射,得到一个目标rect.这样达到分块渲染的效果.
Page对象

一个页面包含一个页面树

Page(DocumentView documentView, int index) {
    this.documentView = documentView;
    this.index = index;
    node = new PageTreeNode(documentView, new RectF(0, 0, 1, 1), this, ZOOM_THRESHOLD, null);
}

页与页码都存起来了,子节点在node中,page本身没有画bitmap,它会调用node实现具体的绘图.

它处理长宽比例,页面宽度,背景绘制

invalidate方法,就是与view的同名,刷新视图用的.在外部的view调用.
DocumentView

这是view,处理滚动,绘制,页面加载,缩放等一切与view相关的.

因为它只是普通的view,那么layout后,高宽是固定的,如何实现多页面的滚动,就是scroll实现的,这样就会有偏移量.在滚动绘制时,需要处理每一个page的绘制.但如果每一个page都要绘制,性能是极差的.所以需要判断page是否在可见区.

先看它的流程:

private void init() {
        if (isInitialized) {
            return;
        }
        final int width = decodeService.getEffectivePagesWidth();
        final int height = decodeService.getEffectivePagesHeight();
        for (int i = 0; i < decodeService.getPageCount(); i++) {
            pages.put(i, new Page(this, i));
            pages.get(i).setAspectRatio(width, height);
        }
        System.out.println("DecodeService:" + pages.size() + " pageToGoTo:" + pageToGoTo);
        isInitialized = true;
        currentPageModel.setPageCount(decodeService.getPageCount());
        invalidatePageSizes();
        goToPageImpl(pageToGoTo);
    }

先把所有页面的物理高宽计算出,存起来,然后初始化页面的view显示时的大小.这一步操作比较耗时,可以考虑进入非ui线程,而且页面的高宽是固定的,除非pdf被修改,这是可以缓存在文件中的.

invalidatePageSizes,初始化页面的实际显示的高宽,这个高宽是一个pdf根据dpi=72得到的高宽,显示在当前的view系统上多大,这时就会涉及到缩放了.
float heightAccum = 0;
            int width = getWidth();
            float zoom = zoomModel.getZoom();
            for (int i = 0; i < pages.size(); i++) {
                Page page = pages.get(i);
                float pageHeight = page.getPageHeight(width, zoom);
                page.setBounds(new RectF(0, heightAccum, width * zoom, heightAccum + pageHeight));
                heightAccum += pageHeight;
            }

zoom默认是1,因为还没有进入缩放操作.但是这里的getpageheight却是根据view的宽高与pdf页面的宽高计算的.这个很好理解

float getPageHeight(int mainWidth, float zoom) {
    return mainWidth / getAspectRatio() * zoom;
}

计算出所有页面高宽后,以后每一个页面,滚动到哪里就有数了.

接着它就开始跳转到页面.

上面是构造函数调用时的初始化.在布局后也会进行一次初始化

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    float scrollScaleRatio = getScrollScaleRatio();
    invalidatePageSizes();
    invalidateScroll(scrollScaleRatio);//只是处理滚动的位置
    commitZoom();//这是调用page的invalidate.
}

这个初始化不一样的地方,它计算高宽后,要处理滚动,提交缩放.

滚动起来,如何更新呢?

protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        // bound
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值