implementation "com.linkedin.dexmaker:dexmaker:2.28.1" implementation "com.linkedin.dexmaker:dexmaker-mockito:2.28.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,我艹,成了,没问题了,可以了,好起来了,其中全部的图片都加载了。

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

  • ScrollView高度测试原理
  • WebView高度计算
  • 关于ScrollView嵌套WebView的滑动冲突问题,那才值几个钱,呼呼就解决。

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

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

  • 2个WebView,前台WebView加载与用户交互,后台WebView自动缓慢滑动至底部,对于打印的操作直接操作后台的WebView就好了,不滑动到底部不允许打印!!!
  • 1个WebView,不滑动到底部不允许打印!!!
  • Ending 如果各位大佬有更好的方法,还请评论区赐教。

    分类:
    Android
    标签: