美文网首页记录集
【记录】Android关于WebView内容转PDF文件打印及其

【记录】Android关于WebView内容转PDF文件打印及其

作者: Guxxxd | 来源:发表于2021-06-23 18:45 被阅读0次

    需求描述:将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
    如果各位大佬有更好的方法,还请评论区赐教。

    相关文章

      网友评论

        本文标题:【记录】Android关于WebView内容转PDF文件打印及其

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