android 记录打印文件

android官方提供的打印文件分为打印照片和打印HTML文档和自定义文档

!!转载官方的,详细看官方,只是加了一些细节

官方地址

打印图片

android支持库PrintHelper 类 提供了简单的图片打印方式。该类具有单个版式选项setScaleMode(),
可以通过两种选项之一进行打印:

  • SCALE_MODE_FIT - 此选项用于调整图片大小,使整个图片显示在页面的可打印区域内
  • SCALE_MODE_FILL - 此选项用于缩放图片,使其填充页面的整个可打印区域。选择此设置意味着图片上下或左右边缘的某些部分不会打印出来。如果您未设置缩放模式,则此选项为默认值。
    setScaleMode() 的这两个缩放选项都能保持图片的现有宽高比不变。以下代码示例演示了如何创建 PrintHelper 类的实例、设置缩放选项以及开始打印过程:
    private fun doPhotoPrint(activity:Activity) {
        activity?.also { context ->
            PrintHelper(context).apply {
                scaleMode = PrintHelper.SCALE_MODE_FIT
            }.also { printHelper ->
                val bitmap = BitmapFactory.decodeResource(resources, R.drawable.droids)
                printHelper.printBitmap("这里是你需要打印的图片的名字", bitmap)
            }
        }
    }
    

此方法可以作为菜单项的操作来调用。请注意,对于并非始终受支持的操作(如打印),相应的菜单项应放置在溢出菜单中。如需了解详情,请参阅操作栏设计指南。

调用 printBitmap() 方法后,不需要从您的应用中执行进一步操作。此时会显示 Android 打印界面,允许用户选择打印机和打印选项。然后,用户可以打印图片或取消操作。如果用户选择打印图片,则会创建打印任务并在系统栏中显示打印通知。


这里还有几个字段 对应的含义
PrintHelper下面对应的字段
COLOR_MODE_MONOCHROME-----》 黑白图片
COLOR_MODE_COLOR-----》 彩色图片 (默认就是)
ORIENTATION_LANDSCAPE----》横向打印(默认)
ORIENTATION_PORTRAIT-----》纵向打印

打印HTML文档

在 Android 4.4(API 级别 19)中,WebView 类已更新,可支持打印 HTML 内容。该类使您能够加载本地 HTML 资源或从网络上下载网页,创建打印作业并将其移交给 Android 的打印服务。
加载HTML文档
需使用WebView打印HTML文档,需要加载HTML资源或者以字符串形式构建HTML文档
此视图对象通常用作activity布局中的一部分,如果应用未使用webview,可以专门为打印目的创建该类的实例,步骤:

  1. 创建在加载HTML资源后启动打印任务的WebViewClient
  2. 将HTML资源加载到webview对象中
private var mWebView:WebView?=null

private fun WebPrint(context:Context){
  //  创建一个专门用于打印的 WebView 对象
        val webView = WebView(context)
        webView.webViewClient = object : WebViewClient() {

            override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false

            override fun onPageFinished(view: WebView, url: String) {
                Log.i(TAG, "page finished loading $url")
                createWebPrintJob(view)
                mWebView = null
            }
        }

        // 动态生成 HTML 文档:
        //这里的test 可以用${string}拼接成你需要打印的
        val htmlDocument =
                "<html><body><h1>Test Content</h1><p>Testing, testing, testing...</p></body></html>"
        webView.loadDataWithBaseURL(null, htmlDocument, "text/HTML", "UTF-8", null)

        // 保留对 WebView 对象的引用,直到您通过 PrintDocumentAdapter
        // 到打印管理器
        mWebView = webView

}

注意:请确保用于生成打印任务的调用发生在上一部分中创建的 WebViewClient 的 onPageFinished() 方法中。如果您没有等到页面加载完成,则打印输出可能不完整或空白,也可能完全失败。
注意:以上示例代码保存了 WebView 对象的实例,以便在创建打印作业之前不会对其进行垃圾回收。请确保您在自己的实现中也这样做,否则打印过程可能会失败。

如需在页面中添加图形,请将图形文件放在项目的 assets/ 目录中,并在 loadDataWithBaseURL() 方法的第一个参数中指定基准网址,如以下代码示例所示:

    webView.loadDataWithBaseURL(
            "file:///android_asset/images/",
            htmlBody,
            "text/HTML",
            "UTF-8",
            null
    )
    

您还可以将 loadDataWithBaseURL() 方法替换为 loadUrl(),以加载要打印的网页,如下所示。

//loadUrl("") 这里你可以直接替换成你要打印的文件网址,比如你要打印一个票据---那你直接吧票据对应的url放进来就好了
    webView.loadUrl("https://developer.android.com/about/index.html")
   

使用 WebView 创建打印文档时,应注意以下限制:

  • 您不能向文档中添加页眉或页脚,包括页码。
  • HTML 文档的打印选项不包括打印特定范围的页面,例如:不支持打印一个 10 页 HTML 文档中的第 2 到 4 页。
  • WebView 的实例一次只能处理一个打印作业。
  • 不支持包含 CSS 打印属性(例如横向属性)的 HTML 文档。
  • 您不能在 HTML 文档中使用 JavaScript 触发打印作业。

注意:布局中包括的 WebView 对象的内容在其加载文档后也可以打印。

创建打印作业

创建 WebView 并加载 HTML 内容之后,您的应用即将完成它那部分的打印流程。接下来的步骤是访问 PrintManager,创建打印适配器,最后创建打印作业。以下示例说明了如何执行这些步骤:

  	@SuppressLint("ServiceCast")
    private fun createWebPrintJob(webView: WebView) {

        // 获取 PrintManager 实例
        (getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let { printManager ->

            val jobName = "${getString(R.string.app_name)} Document"

            //获取打印适配器实例
            val printAdapter = webView.createPrintDocumentAdapter(jobName)

            //  使用名称和适配器实例创建打印作业
            printManager.print(
                    jobName,
                    printAdapter,
                    PrintAttributes.Builder().build()
            ).also { printJob ->

                //  保存作业对象以供以后检查状态
                printJobs += printJob
            }
        }
    }
    

此示例保存了 PrintJob 对象的实例供应用使用,这不是必需的。您的应用可能会使用此对象跟踪正在处理的打印作业的进度。当您希望监控应用中打印作业的状态(完成、失败或用户取消)时,此方法很有用。不需要创建应用内通知,因为打印框架会自动为打印任务创建系统通知。

打印自定义文档

对于某些应用,例如绘图应用、页面布局应用以及专注于图形输出的其他应用,创建精美的打印页面是一项重要功能。在这种情况下,打印图片或 HTML 文档是不够的。对于此类应用,打印输出需要精确控制进入页面的所有内容,包括字体、文本流、分页符、页眉、页脚和图形元素。

比起之前讨论的方法,创建完全针对您的应用自定义的打印输出需要投入更多的编程工作。您必须构建与打印框架通信的组件,根据打印机设置做出调整,绘制页面元素以及管理多页打印

连接到打印管理器

当应用直接管理打印过程时,从用户那收到打印请求后的第一步是连接到 Android 打印框架并获取 PrintManager 类的实例。该类使您能够初始化打印作业并开始打印生命周期。以下代码示例演示了如何获取打印管理器并开始打印过程。

//为了区分建议改为activity或者别的命名,其实第一个就是上下文
    private fun doPrint(activity:Activity) {
        activity?.also { context ->
              //获取 PrintManager 实例
            val printManager = context.getSystemService(Context.PRINT_SERVICE) as PrintManager
            // 设置作业名称,该名称将显示在打印队列中
            val jobName = "${context.getString(R.string.app_name)} Document"
             // 开始打印作业,传入 PrintDocumentAdapter 实现
            // 处理打印文档的生成
            printManager.print(jobName, MyPrintDocumentAdapter(context), null)
        }
    }
    

注意:print() 方法中的最后一个参数采用 PrintAttributes 对象。您可以使用此参数向打印框架提供提示,并根据之前的打印周期预设选项,从而改善用户体验。您还可以使用此参数设置更适合所打印内容的选项,例如在打印横向照片时,将方向设置为横向

创建打印适配器

打印适配器与 Android 打印框架互动,并处理打印过程的步骤。此过程要求用户在创建打印文档前先选择打印机和打印选项。当用户选择具有不同输出功能、不同页面大小或不同页面方向的打印机时,这些选择会影响最终输出。在进行这些选择时,打印框架会要求您的适配器设置布局并生成打印文档,为最终输出做准备。当用户点按打印按钮后,框架将接受最终打印文档并将其传递给打印提供器以进行输出。在打印过程中,用户可以选择取消打印操作,因此打印适配器还必须监听和回应取消请求。

PrintDocumentAdapter 抽象类旨在处理打印生命周期,它有四个主要的回调方法。您必须在打印适配器中实现这些方法,才能与打印框架正确交互:

  • onStart()----- 在打印过程开始调用一次,如果引用有任何一次性准备任务要执行,例如要获得打印的数据的快照,在这里执行这些任务,不用在适配器中实现此方法
  • onLayout()----- 每当用户更改影响输出的打印设置(例如不同页面的大小或者页面方向)调用,让应用有机会计算要打印的界面的布局。此方法至少必须返回打印文档预期的页数
  • onWirte()----调用这个方法来将打印的页面呈现为要打印的文件。在每次调用onLayout()后,可能调用此方法一次或者多次。
  • onFinish()—此方法在打印过程结束时调用一次, 如果应用有人和一次性拆解任务要执行,在这里操作你需要执行的任务,不用在适配器中实现此方法

注意:这些适配器方法是在应用的主线程上调用的。如果您预计在实现中执行这些方法要花费大量时间,则将它们实现为在单独的线程内执行。例如,您可以将布局或打印文档写入工作封装在单独的 AsyncTask 对象中。

计算打印文档信息

在 PrintDocumentAdapter 类的实现中,您的应用必须能够指定要创建的文档的类型,并根据打印页面尺寸的相关信息计算打印作业的总页数。在适配器中实现的 onLayout() 方法可完成这些计算,并在 PrintDocumentInfo 类中提供有关打印作业预期输出的信息,包括页数和内容类型。以下代码示例为整体代码,详细看下面的拆分代码:

 class MyPrintDocumentAdapter(var context: Context):PrintDocumentAdapter(){
        var pdfDocument:PdfDocument?=null
        override fun onLayout(
            oldAttributes: PrintAttributes?,
            newAttributes: PrintAttributes?,
            cancellationSignal: CancellationSignal?,
            callback: LayoutResultCallback?,
            extras: Bundle?
        ) {
            // 使用请求的页面属性创建一个新的 PdfDocument
            pdfDocument = PrintedPdfDocument(context, newAttributes!!)
            // 响应取消请求
            if (cancellationSignal?.isCanceled == true) {
                callback?.onLayoutCancelled()
                return
            }
            // 计算预期的打印页数
            val pages = computePageCount(newAttributes)

            if (pages > 0) {
                // 将打印信息返回到打印框架
                PrintDocumentInfo.Builder("print_output.pdf")
                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                    .setPageCount(pages)
                    .build()
                    .also { info ->
                        // 内容布局回流完成
                        callback?.onLayoutFinished(info, true)
                    }
            } else {
                // 否则向打印框架报错
                callback?.onLayoutFailed("Page count calculation failed.")
            }
        }

        override fun onWrite(
            pages: Array<out PageRange>?,
            destination: ParcelFileDescriptor?,
            cancellationSignal: CancellationSignal?,
            callback: WriteResultCallback?
        ) {
            // 遍历文档的每一页,
            // 检查它是否在输出范围内。
            for (i in 0 until 总页数) {
                // 检查此页面是否在输出范围内。
                if (包含页面(页面范围, i)) {
                    //如果是,请将其添加到 writePagesArray。 writePagesArray.size()
                    //用于计算下一个输出页面索引。
                    写入的页面数组.append(写入的页面数组.size(), i)
                    pdfDocument?.startPage(i)?.also { page ->

                        // 检查取消
                        if (cancellationSignal?.isCanceled == true) {
                            callback?.onWriteCancelled()
                            pdfDocument?.close()
                            pdfDocument = null
                            return
                        }

                        //绘制页面内容进行打印
                        drawPage(page)

                        // 渲染已完成,因此可以完成页面。
                        pdfDocument?.finishPage(page)
                    }
                }
            }

            // 将 PDF 文档写入文件
            try {
                pdfDocument?.writeTo(FileOutputStream(destination.fileDescriptor))
            } catch (e: IOException) {
                callback?.onWriteFailed(e.toString())
                return
            } finally {
                pdfDocument?.close()
                pdfDocument = null
            }
            val writtenPages = 计算书面页面()
            // 通知打印框架文档已完成
            callback?.onWriteFinished(writtenPages)
        }

        private fun computePageCount(printAttributes: PrintAttributes): Int {
            var itemsPerPage = 4 // 纵向模式的默认项目数

            val pageSize = printAttributes.mediaSize
            if (!pageSize!!.isPortrait) {
                // 横向每页六个项目
                itemsPerPage = 6
            }

            // 确定打印项目数
            val printItemCount: Int = 获取打印项目计数()

            return Math.ceil((printItemCount / itemsPerPage.toDouble())).toInt()
        }


		private fun drawPage(page: PdfDocument.Page) {
       		 page.canvas.apply {

            // 单位以点为单位(1/72英寸)
            val titleBaseLine = 72f
            val leftMargin = 54f

            val paint = Paint()
            paint.color = Color.BLACK
            paint.textSize = 36f
            drawText("Test Title", leftMargin, titleBaseLine, paint)

            paint.textSize = 11f
            drawText("Test paragraph", leftMargin, titleBaseLine + 25, paint)

            paint.color = Color.BLUE
            drawRect(100f, 100f, 172f, 172f, paint)
        }
    }

    }

代码中的中文 替换成自己的代码—

 override fun onLayout(
            oldAttributes: PrintAttributes?,
            newAttributes: PrintAttributes?,
            cancellationSignal: CancellationSignal?,
            callback: LayoutResultCallback?,
            extras: Bundle?
        ) {
            // 使用请求的页面属性创建一个新的 PdfDocument
            pdfDocument = PrintedPdfDocument(context, newAttributes!!)
            // 响应取消请求
            if (cancellationSignal?.isCanceled == true) {
                callback?.onLayoutCancelled()
                return
            }
            // 计算预期的打印页数
            val pages = computePageCount(newAttributes)

            if (pages > 0) {
                // 将打印信息返回到打印框架
                PrintDocumentInfo.Builder("print_output.pdf")
                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                    .setPageCount(pages)
                    .build()
                    .also { info ->
                        // 内容布局回流完成
                        callback?.onLayoutFinished(info, true)
                    }
            } else {
                // 否则向打印框架报错
                callback?.onLayoutFailed("Page count calculation failed.")
            }
        }

执行 onLayout() 方法可能会产生三种结果:完成、取消或失败(在无法完成布局计算的情况下)。您必须通过调用 PrintDocumentAdapter.LayoutResultCallback 对象的相应方法指明其中一种结果。
注意:onLayoutFinished() 方法的布尔值参数指明布局内容自上次请求后是否实际发生过更改。正确设置此参数可让打印框架避免不必要地调用 onWrite() 方法,实质上缓存先前写入的打印文档并提高性能。
onLayout() 的主要工作是根据打印机的属性计算预计输出的页数。计算此数字的方式在很大程度上取决于应用的打印页面布局,在以下代码示例所示的实现中,页数取决于打印方向:

    private fun computePageCount(printAttributes: PrintAttributes): Int {
            var itemsPerPage = 4 // 纵向模式的默认项目数

            val pageSize = printAttributes.mediaSize
            if (!pageSize!!.isPortrait) {
                // 横向每页六个项目
                itemsPerPage = 6
            }

            // 确定打印项目数
            val printItemCount: Int = 获取打印项目计数()

            return Math.ceil((printItemCount / itemsPerPage.toDouble())).toInt()
        }

写入打印文档文件
到了要将打印输出写入文件时,Android 打印框架会调用应用的 PrintDocumentAdapter 类的 onWrite() 方法。该方法的参数指定应写入的页面以及要使用的输出文件。然后,此方法的实现必须将每个请求的内容页呈现为多页 PDF 文档文件。此过程完成后,您需要调用回调对象的 onWriteFinished() 方法。
注意:每次调用 onLayout() 后,Android 打印框架可能会调用 onWrite() 方法一次或多次。因此,很重要的一点是在打印内容布局未发生更改时将 onLayoutFinished() 方法的布尔值参数设置为 false,以免不必要地重写打印文档。
注意:onLayoutFinished() 方法的布尔值参数指明布局内容自上次请求后是否实际发生过更改。正确设置此参数可让打印框架避免不必要地调用 onLayout() 方法,实质上缓存先前写入的打印文档并提高性能。
以下示例演示了该过程的基本机制,并使用 PrintedPdfDocument 类创建 PDF 文件:

 override fun onWrite(
            pages: Array<out PageRange>?,
            destination: ParcelFileDescriptor?,
            cancellationSignal: CancellationSignal?,
            callback: WriteResultCallback?
        ) {
            // 遍历文档的每一页,
            // 检查它是否在输出范围内。
            for (i in 0 until 总页数) {
                // 检查此页面是否在输出范围内。
                if (包含页面(页面范围, i)) {
                    //如果是,请将其添加到 writePagesArray。 writePagesArray.size()
                    //用于计算下一个输出页面索引。
                    写入的页面数组.append(写入的页面数组.size(), i)
                    pdfDocument?.startPage(i)?.also { page ->

                        // 检查取消
                        if (cancellationSignal?.isCanceled == true) {
                            callback?.onWriteCancelled()
                            pdfDocument?.close()
                            pdfDocument = null
                            return
                        }
                        //绘制页面内容进行打印
                        drawPage(page)

                        // 渲染已完成,因此可以完成页面。
                        pdfDocument?.finishPage(page)
                    }
                }
            }

            // 将 PDF 文档写入文件
            try {
                pdfDocument?.writeTo(FileOutputStream(destination.fileDescriptor))
            } catch (e: IOException) {
                callback?.onWriteFailed(e.toString())
                return
            } finally {
                pdfDocument?.close()
                pdfDocument = null
            }
            val writtenPages = 计算书面页面()
            // 通知打印框架文档已完成
            callback?.onWriteFinished(writtenPages)
        }

绘制 PDF 页面内容
当应用进行打印时,应用必须生成 PDF 文档并将其传递到 Android 打印框架进行打印。您可以将任何 PDF 生成库用于此目的。本节课介绍如何使用 PrintedPdfDocument 类根据您的内容生成 PDF 页面。

PrintedPdfDocument 类使用 Canvas 对象在 PDF 页面上绘制元素,类似于在 Activity 布局上绘制。您可以使用 Canvas 绘制方法在打印页上绘制元素。以下示例代码演示了如何使用以下方法在 PDF 文档页面上绘制一些简单的元素:

    private fun drawPage(page: PdfDocument.Page) {
        page.canvas.apply {

            // 单位以点为单位(1/72英寸)
            val titleBaseLine = 72f
            val leftMargin = 54f

            val paint = Paint()
            paint.color = Color.BLACK
            paint.textSize = 36f
            drawText("Test Title", leftMargin, titleBaseLine, paint)

            paint.textSize = 11f
            drawText("Test paragraph", leftMargin, titleBaseLine + 25, paint)

            paint.color = Color.BLUE
            drawRect(100f, 100f, 172f, 172f, paint)
        }
    }
    

使用 Canvas 在 PDF 页面上绘制时,元素以磅(即 1/72 英寸)为单位进行指定。请确保使用此度量单位指定页面上元素的大小。对于已绘制元素的位置,坐标系从页面左上角的 (0,0) 处开始。

提示:虽然 Canvas 对象允许在 PDF 文档的边缘放置打印元素,但许多打印机无法打印到实际纸张的边缘。当您使用该类构建打印文档时,确保考虑到页面的不可打印边缘。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值