Web - lydGit/Notes GitHub Wiki

Web界面显示转PDF文件

功能要求:把H5的网页报表转成PDF文件 参考:Android Webview生成和导出PDF解决方案探索

1:PdfDocument,PrintedPdfDocument

实现的功能差不多,都是把View中显示的内容转成PDF的格式,主要不同的是PrintedPdfDocument可以设置打印参数,如纸张大小,像素等!但是WebView中他只会转换界面显示的内容,不显示的部分做转换!(PdfDocument|PrintedPdfDocument)

2:PrintManager

调用系统打印方法,弹出一个系统窗口对打印参数进行设置或打印效果进行预览。

PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
 
// Get a print adapter instance
// MyCreatePrintDocumentAdapter为继承PrintDocumentAdapter抽象类的子类
PrintDocumentAdapter printAdapter = new MyCreatePrintDocumentAdapter();

// Create a print job with name and adapter instance
String jobName = context.getString(com.sumxiang.noteapp.R.string.app_name) + " Document";

PrintAttributes attributes = new PrintAttributes.Builder()
        .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
        .setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 300, 300))
        .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
        .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
        .build();

// 这里第三个参数可以传自定义attributes,也可以直接传null,此时将使用默认配置。
printManager.print(jobName, printAdapter, attributes);

3:WebView>>Bitmap>>PDF(未验证方法,记录个思路)

大概流程:先将WebView转成Bitmap格式,然后根据设置好的的打印样式的大小,将Bitmap裁剪为多张Bitmap。然后再转成pdf格式!实现起来十分困难!

4:PrintDocumentAdapter+DexMaker

通过DexMaker中的ProxyBuilder.forClass()动态生成的代理类来替代真正的实体类来截取PrintDocumentAdapter中的方法。

public void savePDFFile(File saveFile) {
    //手机系统4.4以下的手机不能使用该功能
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
        CommonUtil.toast(R.string.tv_common_stystem_version_low);
        return;
    }
    try {
        ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(saveFile, ParcelFileDescriptor.MODE_READ_WRITE);
        // 设置打印参数
        PrintAttributes attributes = new PrintAttributes.Builder()
                .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
                .setResolution(new PrintAttributes.Resolution("id", Context.PRINT_SERVICE, 300, 300))
                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
                .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
                .build();
        PrintDocumentAdapter documentAdapter = webView.createPrintDocumentAdapter();
        documentAdapter.onStart();

        //文件暂存路径
        File dexCacheFile = webView.getContext().getDir("dex", 0);
        if (!dexCacheFile.exists()) {
            dexCacheFile.mkdir();
        }
        //设置打印页数,但经过测试,暂无发现效果
        PageRange ranges[] = new PageRange[]{new PageRange(1, 2)};
        //监听文件是否打印
        documentAdapter.onLayout(attributes, attributes, new CancellationSignal(), getLayoutResultCallback(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("onLayoutFinished")) {
                    // 监听到内部调用了onLayoutFinished()方法,即打印成功
                    onLayoutSuccess(documentAdapter, dexCacheFile, ranges, descriptor);
                } else {
                    // 监听到打印失败或者取消了打印
                    documentAdapter.onFinish();
                }
                return null;
            }
        }, dexCacheFile.getAbsoluteFile()), new Bundle());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void onLayoutSuccess(PrintDocumentAdapter adapter, File dexCacheFile, PageRange[] ranges, ParcelFileDescriptor descriptor) throws IOException {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        PrintDocumentAdapter.WriteResultCallback callback = getWriteResultCallback((o, method, objects) -> {
            if (method.getName().equals("onWriteFinished")) {
                // PDF文件写入本地完成,导出成功
            } else {
                // 导出失败
            }
            adapter.onFinish();
            return null;
        }, dexCacheFile.getAbsoluteFile());
        adapter.onWrite(ranges, descriptor, new CancellationSignal(), callback);
    }
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static PrintDocumentAdapter.LayoutResultCallback getLayoutResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException {
    //创建一个动态生成的代理类来替代真正的实体类
    return ProxyBuilder.forClass(PrintDocumentAdapter.LayoutResultCallback.class)
            .dexCache(dexCacheDir)
            .handler(invocationHandler)
            .build();
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static PrintDocumentAdapter.WriteResultCallback getWriteResultCallback(InvocationHandler invocationHandler, File dexCacheDir) throws IOException {
    //创建一个动态生成的代理类来替代真正的实体类
    return ProxyBuilder.forClass(PrintDocumentAdapter.WriteResultCallback.class)
            .dexCache(dexCacheDir)
            .handler(invocationHandler)
            .build();
}

WebView加载https时界面或图片空白

当WebView使用loadUrl方法加载通过ssl加密的https页面时,如果这个网站的安全证书在Android无法得到认证,WebView就会变成一个空白页,而不会像自带的浏览器一样弹出提示。(部分系统可能出现的是图片空白) 参考:Webview 访问https页面 SslError 处理

WebView webview = (WebView) findViewById(R.id.webview);
webview.setWebViewClient(new WebViewClient() {
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        //handler.cancel(); 默认的处理方式,WebView变成空白页
        //handler.process();接受证书
        //handleMessage(Message msg); 其他处理
    }
});

如果当重载函数里面是process()时,系统就会忽略证书的错误继续Load页面内容,不会显示空白页面。