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,可以专门为打印目的创建该类的实例,步骤:
- 创建在加载HTML资源后启动打印任务的WebViewClient
- 将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 文档的边缘放置打印元素,但许多打印机无法打印到实际纸张的边缘。当您使用该类构建打印文档时,确保考虑到页面的不可打印边缘。