You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android(4.4-API 19)生成PDF后无预览网络打印实现求助

别着急!你的需求完全可以实现,我来一步步给你讲清楚怎么做,尤其是针对新手的友好方案~

先搞定最容易实现的:带预览打印

带预览打印是Android官方支持的标准流程,非常适合新手上手,而且能让用户选择网络打印机(只要打印机和设备在同一局域网)。

你已经能生成PDF文件了,接下来只需要调用系统的打印服务,把生成好的PDF传递给它就行。所有代码都放在你那个有表单和Print按钮的Activity里,直接写在按钮的点击事件中:

步骤1:添加权限(如果需要)

如果你的PDF文件存在公共存储目录(比如你现在的/storage/emulated/0/client pqr/),需要在AndroidManifest.xml中添加存储权限:

<!-- Android 10及以下 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 11+ 如需访问公共目录,可添加这个权限(需要用户手动授权) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

注意:Android 10+推荐把文件放在应用私有目录,这样不需要额外权限,但你如果坚持用公共目录,要记得在运行时请求权限。

步骤2:按钮点击事件中的打印代码

直接在Print按钮的点击监听里写这些代码:

printButton.setOnClickListener(v -> {
    // 指向你生成好的PDF文件
    File pdfFile = new File("/storage/emulated/0/client pqr/pqr.pdf");
    
    // 先检查文件是否存在
    if (!pdfFile.exists()) {
        Toast.makeText(this, "PDF文件找不到啦!", Toast.LENGTH_SHORT).show();
        return;
    }

    // 获取系统打印管理器
    PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
    // 给打印任务起个名字,会显示在打印队列里
    String printJobName = getString(R.string.app_name) + " - PQR表单打印";

    // 适配Android 10+的简单方案:直接用FilePrintDocumentAdapter
    PrintDocumentAdapter printAdapter = new FilePrintDocumentAdapter(pdfFile);

    // 触发打印预览界面
    printManager.print(printJobName, printAdapter, null);
});

这段代码会弹出系统自带的打印预览界面,用户可以选择已发现的网络打印机,调整打印设置后直接打印,完全符合你的“至少带预览”的需求。

如果你的设备系统版本低于Android 10,FilePrintDocumentAdapter可能不存在,那可以自己写一个简单的PDF打印适配器(代码如下),替换上面的printAdapter即可:

// 在你的Activity内部定义这个内部类
private class CustomPdfPrintAdapter extends PrintDocumentAdapter {
    private final File pdfFile;

    public CustomPdfPrintAdapter(File pdfFile) {
        this.pdfFile = pdfFile;
    }

    @Override
    public void onLayout(PrintAttributes oldAttrs, PrintAttributes newAttrs, CancellationSignal cancelSignal, LayoutResultCallback callback, Bundle extras) {
        if (cancelSignal.isCanceled()) {
            callback.onLayoutCancelled();
            return;
        }

        // 描述打印文档的信息
        PrintDocumentInfo docInfo = new PrintDocumentInfo.Builder("pqr.pdf")
                .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN) // 不知道页数就设为未知
                .build();

        callback.onLayoutFinished(docInfo, !oldAttrs.equals(newAttrs));
    }

    @Override
    public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancelSignal, WriteResultCallback callback) {
        InputStream input = null;
        OutputStream output = null;

        try {
            input = new FileInputStream(pdfFile);
            output = new FileOutputStream(destination.getFileDescriptor());

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = input.read(buffer)) != -1 && !cancelSignal.isCanceled()) {
                output.write(buffer, 0, bytesRead);
            }

            if (cancelSignal.isCanceled()) {
                callback.onWriteCancelled();
            } else {
                callback.onWriteFinished(new PageRange[]{PageRange.ALL_PAGES});
            }
        } catch (IOException e) {
            callback.onWriteFailed(e.getMessage());
            e.printStackTrace();
        } finally {
            try {
                if (input != null) input.close();
                if (output != null) output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

使用时替换成:

PrintDocumentAdapter printAdapter = new CustomPdfPrintAdapter(pdfFile);
进阶需求:无预览直接打印(难度较高)

无预览直接发送到网络打印机是可行的,但需要跳过系统打印服务,直接通过网络协议(比如IPP,大部分网络打印机都支持)发送PDF文件。这对新手来说门槛较高,因为需要处理:

  • 网络打印机的IP地址、端口(通常是631)
  • IPP协议的请求构造
  • 可能的打印机认证
  • 局域网内的打印机发现(可选)

如果你一定要做,可以尝试用第三方的IPP客户端库,或者自己实现简单的IPP请求,但这个过程涉及较多网络编程知识,建议先把带预览的版本跑通,再慢慢研究这部分。

代码到底该放在哪里?

所有打印相关的代码都直接放在你的表单Activity里

  • 按钮的点击监听就在这个Activity里,所以打印逻辑写在监听内部最直接。
  • 如果是自定义的PrintDocumentAdapter,可以作为Activity的内部类定义,这样方便访问Activity的上下文和资源。
额外注意事项
  • 测试时确保你的Android设备和网络打印机在同一个Wi-Fi局域网内,系统才能发现打印机。
  • 如果生成PDF的过程比较耗时,建议放在子线程里执行,避免阻塞UI线程,点击按钮时可以先显示加载提示,生成完成后再触发打印。

内容的提问来源于stack exchange,提问作者Sam

火山引擎 最新活动