【记录】Android关于WebView内容转PDF文件打印及其遇到的问题解决方案

需求描述:将WebView渲染出来的内容转化为PDF文件打印

关于内容转为文件打印,google一下,文章还是有很多的,这里简单的贴一下

  1. app/build.gradle 添加

implementation “com.linkedin.dexmaker:dexmaker:2.28.1”
implementation “com.linkedin.dexmaker:dexmaker-mockito:2.28.1”

  1. 实现类
 public class H52PdfTask {

    ParcelFileDescriptor descriptor;
    PageRange[] ranges;
    PrintDocumentAdapter printAdapter;

    public void webViewToPdf (WebView webView, String pdfFilePath) {
        try {
            File pdfFile = new File(pdfFilePath);
            if (pdfFile.exists()) {
                pdfFile.delete();
            }
            pdfFile.createNewFile();
            descriptor = ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_WRITE);
            // 设置打印参数
            PrintAttributes.MediaSize isoA4 = PrintAttributes.MediaSize.ISO_A4;
            PrintAttributes attributes = new PrintAttributes.Builder()
                    .setMediaSize(isoA4)
                    .setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 240, 240))
                    .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
                    .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
                    .build();
            // 计算webview打印需要的页数
            int numberOfPages = ((webView.getContentHeight() * 240 / (isoA4.getHeightMils())) );
            ranges = new PageRange[]{new PageRange(0, numberOfPages)};
            // 创建pdf文件缓存目录
            // 获取需要打印的webview适配器
            printAdapter = webView.createPrintDocumentAdapter();
            // 开始打印
            printAdapter.onStart();
            printAdapter.onLayout(attributes, attributes, new CancellationSignal(),
                    getLayoutResultCallback((proxy, method, args) -> {
                        if (method.getName().equals("onLayoutFinished")) {
                            L.i("H52PdfTask onLayoutFinished thread=" + Thread.currentThread().getName());
                            // 监听到内部调用了onLayoutFinished()方法,即打印成功
                            onLayoutSuccess();
                        } else {
                            // 监听到打印失败或者取消了打印
                            L.i("H52PdfTask onLayout fail");
                        }
                        return null;
                    }), new Bundle());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void onLayoutSuccess () throws IOException {
        PrintDocumentAdapter.WriteResultCallback callback = getWriteResultCallback(new InvocationHandler() {
            @Override
            public Object invoke (Object o, Method method, Object[] objects) {
                if (method.getName().equals("onWriteFinished")) {
                    L.i("H52PdfTask onLayoutSuccess onWriteFinished thread=" + Thread.currentThread().getName());
                } else {
                    L.i("H52PdfTask onLayoutSuccess fail");
                }
                return null;
            }
        });
        printAdapter.onWrite(ranges, descriptor, new CancellationSignal(), callback);
    }

    public static PrintDocumentAdapter.LayoutResultCallback getLayoutResultCallback (InvocationHandler invocationHandler) throws IOException {
        return ProxyBuilder.forClass(PrintDocumentAdapter.LayoutResultCallback.class)
                .handler(invocationHandler)
                .build();
    }

    public static PrintDocumentAdapter.WriteResultCallback getWriteResultCallback (InvocationHandler invocationHandler) throws IOException {
        return ProxyBuilder.forClass(PrintDocumentAdapter.WriteResultCallback.class)
                .handler(invocationHandler)
                .build();
    }
}

!!!遇到的问题

大家都知道,WebView加载Url时,其中的图片是以懒加载的方式即可见加载,问题也就随之出来了,当我们没有浏览完整个网页时,打印出来的文档,没有加载完成的图片并不能显示出来,可能是个菊花,可能是个灰底的未加载的图。
怎么才能让WebView一次性将整个网页的图片全部加载出来呢,搜索了国内外的文章,发现更多的都是阻塞图片加载让网页更快速的显示出来,并没有这种反人类需求的解答(也可能是我的搜索引擎不强),又去看官网WebView相关的api,也没有收获(也可能是我英文阅读能力不强)。
就在一筹莫展之际,发现WebView可以被解析成长截屏( webView.capturePicture()),写个demo试下(WebView加载完成之后,点个Button,把生成的长截屏解析成bitmap设置到ImageView上),先改个布局

    <ScrollView>
           <androidx.appcompat.widget.LinearLayoutCompat>

                <androidx.appcompat.widget.AppCompatButton .../>

                <androidx.appcompat.widget.AppCompatImageView .../>

                <WebView ... />
           
          <androidx.appcompat.widget.LinearLayoutCompat>
    </ScrollView>

跑完demo测试发现,长截屏的方式真的可以,而且更神奇的是,WebView不可见时(并没有上滑WebView使图片加载)的图片竟然也在长截屏上正常的显示出来了,这…这些不可见时的图片,是t(爱)m(你)什么时候被加载的,合着调了个截屏的方法就加载了??????
说是迟那时快,忽然提壶灌顶,改了下布局

    <ScrollView>
           <androidx.appcompat.widget.LinearLayoutCompat>

                <WebView ... />
           
          <androidx.appcompat.widget.LinearLayoutCompat>
    </ScrollView>

run,我艹,成了,没问题了,可以了,好起来了,其中全部的图片都加载了。

具体的原理大家自己搜索下,这里就不贴链接了

  1. ScrollView高度测试原理
  2. WebView高度计算

关于ScrollView嵌套WebView的滑动冲突问题,那才值几个钱,呼呼就解决。

注:
ScrollView单嵌套WebView并没有发现冲突(测试机型:华为,红米,小米),可能是我加载的网页比较简单。

其实还有其他实现思路,还没来得及尝试就解决了,你说气不气

  • 2个WebView,前台WebView加载与用户交互,后台WebView自动缓慢滑动至底部,对于打印的操作直接操作后台的WebView就好了,不滑动到底部不允许打印!!!
  • 1个WebView,不滑动到底部不允许打印!!!

Ending
如果各位大佬有更好的方法,还请评论区赐教。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值