【问题标题】:Android print framework - Silent print custom pdf to predetermined printerAndroid打印框架 - 将自定义pdf静默打印到预定打印机
【发布时间】:2024-01-21 02:10:01
【问题描述】:

我正在为 android 开发一个 POS 应用程序。 我需要做的是在您单击按钮时打印账单。 打印需要在预定的打印机上静默进行,无需与应用程序进行任何交互。将找到打印机,并在代码中设置其 IP 地址。 连接应该发生在真正的 wifi 或蓝牙(可能是 wifi)。

到目前为止,我尝试的是使用 android 打印框架(在 Android 4.4(API 级别 19)中),但它似乎被卡在了 gui 上(同样我不想要任何用于打印的 gui,它只需要发生)。找不到设置打印机IP真码的选项。

我按照一个简单的教程来创建我现在拥有的代码。 这是它的链接: http://www.techotopia.com/index.php/An_Android_Custom_Document_Printing_Tutorial

所以回顾一下,我需要我的自定义 Android POS 应用程序来打印账单(或只是初学者的任何东西)到我在代码中设置的预定打印机。

好像容大pos打印机有android sdk。 我想其他制造商也必须为他们的打印机配备一台。

【问题讨论】:

  • Android 的打印框架专为在传统的 Android 场景中使用而设计。在那里,用户需要 UI 来确认和配置打印作业。我知道没有办法绕过它。将来,Android Things 可能会允许直接打印选项,并且有可能进入基础 Android,但现在不存在 AFAIK。您更有可能采取的行动是与打印机制造商交谈,了解如何直接与打印机交谈(例如,直接套接字连接)并推出您自己的打印系统。
  • 感谢您的快速回复。
  • 知道他们是如何做到这一点的吗?youtube.com/watch?v=M_inOqDzqjg 这是一个克罗地亚视频的链接,他们在其中展示了他们的 POS 应用程序,这正是我想要的,它们与很多兼容打印机。
  • 大概,他们与那些打印机的制造商进行了交谈。我假设有一些数据连接、打印机命令和发送数据的相关标准或规范。因此,一旦您有几台打印机以“艰难的方式”实施,其他打印机主要是测试问题。此外,可能存在用于直接打印到打印机类的现有库,绕过打印框架。
  • 好的,谢谢,我设法找到了 Rongta pos 打印机的文档,他们似乎有一个 android sdk。所以我想这就是我的做法。

标签: java android pdf printing bluetooth


【解决方案1】:

AFAIK 您无法在 Android 中静默打印文档。 AOSP 问题跟踪器上有一个未解决的问题。 https://code.google.com/p/android/issues/detail?id=160908

【讨论】:

    【解决方案2】:

    可以通过调用打印适配器生命周期方法将 printint 转换为 PDF。但是,由于回调是非公共抽象类,并且如果提供 null,系统会抛出段错误,因此您需要使用 DexMaker 来实现它们。我已经为 webView 适配器实现了这样的:

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        printAdapter = webView.createPrintDocumentAdapter();
    }
    
    @Override
    protected Void doInBackground(Void... voids) {
    
        File file = new File(pdfPath);
        if (file.exists()) {
            file.delete();
        }
        try {
            file.createNewFile();
    
            // get file descriptor
            descriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
    
            // create print attributes
            PrintAttributes attributes = new PrintAttributes.Builder()
                    .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
                    .setResolution(new PrintAttributes.Resolution("id", PRINT_SERVICE, 300, 300))
                    .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
                    .setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0))
                    .build();
            ranges = new PageRange[]{new PageRange(1, numberPages)};
    
            // dexmaker cache folder
            cacheFolder =  new File(context.getFilesDir() +"/etemp/");
    
            printAdapter.onStart();
    
            printAdapter.onLayout(attributes, attributes, new CancellationSignal(), getLayoutResultCallback(new InvocationHandler() {
                @Override
                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
    
                    if (method.getName().equals("onLayoutFinished")) {
                        onLayoutSuccess();
                    } else {
                        Log.e(TAG, "Layout failed");
                        pdfCallback.onPdfFailed();
                    }
                    return null;
                }
            }, cacheFolder), new Bundle());
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, e != null ? e.getMessage() : "PrintPdfTask unknown error");
        }
        return null;
    }
    
    private void onLayoutSuccess() throws IOException {
        PrintDocumentAdapter.WriteResultCallback callback = getWriteResultCallback(new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                if (method.getName().equals("onWriteFinished")) {
                    pdfCallback.onPdfCreated();
                } else {
                    Log.e(TAG, "Layout failed");
                    pdfCallback.onPdfFailed();
                }
                return null;
            }
        }, cacheFolder);
        printAdapter.onWrite(ranges, descriptor, new CancellationSignal(), callback);
    }
    
    
    /**
     * Implementation of non public abstract class LayoutResultCallback obtained via DexMaker
     * @param invocationHandler
     * @param dexCacheDir
     * @return LayoutResultCallback
     * @throws IOException
     */
    public static PrintDocumentAdapter.LayoutResultCallback getLayoutResultCallback(InvocationHandler invocationHandler,
                                                                                    File dexCacheDir) throws IOException {
        return ProxyBuilder.forClass(PrintDocumentAdapter.LayoutResultCallback.class)
                .dexCache(dexCacheDir)
                .handler(invocationHandler)
                .build();
    }
    
    /**
     * Implementation of non public abstract class WriteResultCallback obtained via DexMaker
     * @param invocationHandler
     * @param dexCacheDir
     * @return LayoutResultCallback
     * @throws IOException
     */
    public static PrintDocumentAdapter.WriteResultCallback getWriteResultCallback(InvocationHandler invocationHandler,
                                                                                  File dexCacheDir) throws IOException {
        return ProxyBuilder.forClass(PrintDocumentAdapter.WriteResultCallback.class)
                .dexCache(dexCacheDir)
                .handler(invocationHandler)
                .build();
    }
    

    【讨论】:

    • 请解释这是如何回答问题的。