【Android应用开发技术:媒体开发】打印

原创 2015年08月05日 11:42:09

作者:郭孝星
微博:郭孝星的新浪微博
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
Github:https://github.com/AllenWells

【Android应用开发技术:媒体开发】章节列表

在Android 4.4以及更高版本的系统中,提供了直接从Android应用程序打印图片和文字的服务,包括打印图片、HTML页面以及创建自定义打印文档等。

一 打印图片

Android Support Library中的PrintHelper提供了一种打印图片的简单方法,该类有一个单一的布局选项:setScaleMode,它允许提供以下选项:

  • SCALE_MODE_FIT:该选项会调整图片大小,整个图片就会在打印有效区域内全部显示出来。
  • SCALE_MODE_FILL:该选项会等比例地调整图片的大小使得图片可以充满整个打印有效区域,这种模式下,图片的一部分可能无法打印出来,如果不设置图片的打印布局选项,该模式将是默认的图片拉伸方式。
private void doPhotoPrint() {
    PrintHelper photoPrinter = new PrintHelper(getActivity());
    photoPrinter.setScaleMode(PrintHelper.SCALE_MODE_FIT);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
            R.drawable.droids);
    photoPrinter.printBitmap("droids.jpg - test print", bitmap);
}

上述方法被调用之后,Android的打印界面就会出现,允许用户选择一个打印机和它的打印机选项,用户可以选择打印图片和取消操作,当打印开始后,系统的通知栏里会显示一个打印通知提醒。

二 打印HTML文档

Android 4.4以后提供了WebView类,该类可以加载一个本地HTML资源或者从网上下载一个页面,然后创建打印任务,并把它交给Android打印服务。

2.1 加载打印文档

WebView对象一般作为Activity布局的一部分,如果应用当前没有使用WebView,我们可以创建一个该类的实例,以便进行打印,步骤如下:

  1. 在HTML资源加载完毕后,创建一个WebViewClient用来启动一个打印任务。
  2. 加载HTML资源到WebView对象中。

举例

打印HTML构建的字符串

private WebView mWebView;
private void doWebViewPrint() {
    // Create a WebView object specifically for printing
    WebView webView = new WebView(getActivity());
    webView.setWebViewClient(new WebViewClient() {
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                return false;
            }
            @Override
            public void onPageFinished(WebView view, String url) {
                Log.i(TAG, "page finished loading " + url);
                //在WebViewClient中的onPageFinished()方法内调用创建打印任务的方法,
                //以便页面加载完毕后再进行打印,否则会导致打印输出不完整或空白。
                createWebPrintJob(view);
                mWebView = null;
            }
    });
    // Generate an HTML document on the fly:
    String htmlDocument = "<html><body><h1>Test Content</h1><p>Testing, " +
            "testing, testing...</p></body></html>";
    webView.loadDataWithBaseURL(null, htmlDocument, "text/HTML", "UTF-8", null);

    //保留了一个WebView对象实例的引用,这样可以保证它不会在打印任务创建之前被垃圾回收器所回收。
    mWebView = webView;
}

如果我们想打印图像,而这个图像文件在工程的assets目录下,方法如下所示:

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

如果我们想加载并打印一个网页,方法如下所示:

webView.loadUrl("http://blog.csdn.net/allenwells");

注意:当使用WebView打印文档时,有以下限制:

  • 不能为文档添加页眉和页脚,包括页号。
  • HTML文档的打印选项不包含选择打印的页数范围,例如:对于一个10页的HTMl文档,只打印2到4页是不可以
    的。
  • 一个WebView的实例只能在同一时间处理一个打印任务。
  • 若一个HTML文档包含CSS打印属性,比如一个landscape属性,这是不被支持的。
  • 不能通过一个HTML文档中的JavaScript脚本来激活打印。

2.2 创建打印任务

当HTML页面加载完毕后,我们就可以创建打印任务了,如下所示:

private void createWebPrintJob(WebView webView) {
    // Get a PrintManager instance
    PrintManager printManager = (PrintManager) getActivity()
            .getSystemService(Context.PRINT_SERVICE);
    // Get a print adapter instance
    PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter();
    // Create a print job with name and adapter instance
    String jobName = getString(R.string.app_name) + " Document";

    //保存了一个PrintJob对象的实例,这种高做法不是必须的,当我们监控应用中打印任务是否完成,
    //失败或者被用户取消,我们可以采用这种做法。
    PrintJob printJob = printManager.print(jobName, printAdapter,
            new PrintAttributes.Builder().build());
    // Save the job object for later status checking
    mPrintJobs.add(printJob);
}

三 打印自定义文档

对于有些应用,比如绘图应用,页面布局应用和其它一些关注于图像输出的应用,创造出美丽的打印页面将是它的核心功能。在这种情况下,仅仅打印一幅图片或一个HTML文档就不够了。这类应用的打印输出需要精确地控制每一个会在页面中显示的对象,包括字体,文本流,分页符,页眉,页脚和一些图像元素等等。

为了能够创建自定义打印文档,我们需要构建和打印框架可以相互通信的组件,调整打印参数,绘制页面元素并管理多个页面的打印。

3.1 连接打印管理器

连接Android打印框架,首先要获取PrintManager类的实例,该类会初始化一个打印任务并开始打印任务的生命周期。

举例

获取打印管理器,开始打印进程。

private void doPrint() {
    // Get a PrintManager instance
    PrintManager printManager = (PrintManager) getActivity()
            .getSystemService(Context.PRINT_SERVICE);
    // Set job name, which will be displayed in the print queue
    String jobName = getActivity().getString(R.string.app_name) + " Document";

    //传递PrintDocumentAdapter,开始打印任务。该print()方法最后一个参数接收的是
    //一个PrintAttributes对象,我们可以使用这个参数向打印框架进行一些打印设置,比
    //如基于前一个打印周期的预设,从而改善用户体验。
    printManager.print(jobName, new MyPrintDocumentAdapter(getActivity()),
            null); 
}

3.2 创建打印适配器

打印适配器负责与Android打印框架交互并处理打印过程的每一步。这个过程需要用户在创建打印文档前选择打印机和打印选项。由于用户可以选择不同性能的打印机,不同的页面尺寸或不同的页面方向,因此这些选项可能会影响最终的打印效果。当这些选项配置好之后,打印框架会寻求适配器进行布局并生成一个打印文档,以此作为打印的前期准备。一旦用户点击了打印按钮,框架会将最终的打印文档传递给一个打印提供程序(Print Provider)供打印输出。在打印过程中,用户可以选择取消打印,所以打印适配器必须监听并响应取消打印的请求。

PrintDocumentAdapter抽象类负责处理打印的生命周期,它的回调方法如下所示:

  • onStart():一旦打印进程开始,该方法就将被调用。如果应用有任何一次性的准备任务要执行,比如获取一个要打印数据的快照,那么让它们在此处执行。在适配器中,这个回调方法不是必须实现的。
  • onLayout():每当用户改变了影响打印输出的设置时,例如改变了页面的尺寸,或者页面的方向,该函数将会被调用,以此给应用一个机会去重新计算打印页面的布局。另外,该方法必须返回打印文档包含多少页面。
  • onWrite():该方法调用后,会将打印页面渲染成一个待打印的文件。该方法可以在onLayout()方法被调用后调用一次或多次。
  • onFinish():一旦打印进程结束后,该方法将会被调用。如果应用有任何一次性销毁任务要执行,让这些任务在该方法内执行。这个回调方法不是必须实现的。

注意:这些适配器的回调方法会在应用的主线程上被调用。如果这些方法的实现在执行时可能需要花费大量的时间,那么我们可以把它们放到另一个线程里执行。例如:我们可以将布局或者写入打印文档的操作封装在一个AsyncTask对象中。

3.3 计算打印文档信息

在实现PrintDocumentAdapter类时,应用必须能够指定创建文档的类型,计算打印任务所需要打印的总页数,并提供打印页面的尺寸信息。

举例

onLayout()方法的实现

//onLayout()方法执行结果有3种完成、取消或失败,我们需要通过PrintDocumentAdapter.LayoutResultCallBack
//对象中的适当方法来指出这些结构中的一个。
@Override
public void onLayout(PrintAttributes oldAttributes,
                     PrintAttributes newAttributes,
                     CancellationSignal cancellationSignal,
                     LayoutResultCallback callback,
                     Bundle metadata) {
    // Create a new PdfDocument with the requested page attributes
    mPdfDocument = new PrintedPdfDocument(getActivity(), newAttributes);
    // Respond to cancellation request
    if (cancellationSignal.isCancelled() ) {
        callback.onLayoutCancelled();
        return;
    }
    // Compute the expected number of printed pages
    int pages = computePageCount(newAttributes);
    if (pages > 0) {
        // Return print information to print framework
        PrintDocumentInfo info = new PrintDocumentInfo
                .Builder("print_output.pdf")
                .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                .setPageCount(pages);
                .build();
        // Content layout reflow is complete
        callback.onLayoutFinished(info, true);
    } else {
        // Otherwise report an error to the print framework
        callback.onLayoutFailed("Page count calculation failed.");
    }
}
//计算打印文档的页数,并把它作为打印参数交给打印机,打印页数是根据打印方向确定的。
private int computePageCount(PrintAttributes printAttributes) {
    int itemsPerPage = 4; // default item count for portrait mode    MediaSize pageSize = printAttributes.getMediaSize();
    if (!pageSize.isPortrait()) {
        // Six items per page in landscape orientation
        itemsPerPage = 6;
    }
    // Determine number of print items
    int printItemCount = getPrintItemCount();
    return (int) Math.ceil(printItemCount / itemsPerPage);
}

2.4 将打印文档写入文件

当将打印内容输出到一个文件是,Android打印会调用PrintDocumentAdapter类的onWrite()方法,这个方法的参数指定了哪些页面要被写入以及要使用的输出文件,该方法的实现必须将每一个请求页的内容渲染成一个含有多个页面的PDF文件,当这个过程结束以后,我们需要调用callback对象的onWriteFinished()方法。

举例

onWrite()方法的实现。

//onWrite()方法执行结果有3种完成、取消或失败,我们需要通过PrintDocumentAdapter.LayoutResultCallBack
//对象中的适当方法来指出这些结构中的一个。
@Override
public void onWrite(final PageRange[] pageRanges,
                    final ParcelFileDescriptor destination,
                    final CancellationSignal cancellationSignal,
                    final WriteResultCallback callback) {
    // Iterate over each page of the document,
    // check if it's in the output range.
    for (int i = 0; i < totalPages; i++) {
        // Check to see if this page is in the output range.
        if (containsPage(pageRanges, i)) {
            // If so, add it to writtenPagesArray. writtenPagesArray.size()
            // is used to compute the next output page index.
            writtenPagesArray.append(writtenPagesArray.size(), i);
            PdfDocument.Page page = mPdfDocument.startPage(i);
            // check for cancellation
            if (cancellationSignal.isCancelled()) {
                callback.onWriteCancelled();
                mPdfDocument.close();
                mPdfDocument = null;
                return;
            }
            // Draw page content for printing
            drawPage(page);
            // Rendering is complete, so page can be finalized.
            mPdfDocument.finishPage(page);
        }
    }
    // Write PDF document to file
    try {
        mPdfDocument.writeTo(new FileOutputStream(
                destination.getFileDescriptor()));
    } catch (IOException e) {
        callback.onWriteFailed(e.toString());
        return;
    } finally {
        mPdfDocument.close();
        mPdfDocument = null;
    }    PageRange[] writtenPages = computeWrittenPages();
    // Signal the print framework the document is complete
    callback.onWriteFinished(writtenPages);
    ...
}

2.5 绘制PDF页面的内容

当应用进行打印时,我们需要生成一个PDF文档并将它传递给Android打印框架以进行打印,我们可以借助任何PDF生成库来协助完成这个操作。

PrintedPdfDocument类使用Canvas对象来在PDF页面上绘制元素。

举例

在PDF页面上绘制以下简单元素。

private void drawPage(PdfDocument.Page page) {
    Canvas canvas = page.getCanvas();
    // units are in points (1/72 of an inch)
    int titleBaseLine = 72;
    int leftMargin = 54;
    Paint paint = new Paint();
    paint.setColor(Color.BLACK);
    paint.setTextSize(36);
    canvas.drawText("Test Title", leftMargin, titleBaseLine, paint);
    paint.setTextSize(11);
    canvas.drawText("Test paragraph", leftMargin, titleBaseLine + 25, paint);
    paint.setColor(Color.BLUE);
    canvas.drawRect(100, 100, 172, 172, paint);

注意:Canvas对象允许将打印元素放置在PDF文档的边缘,但是很多打印机无法再纸张的边缘进行打印,因此我们使用PrintedPdfDocument构建打印文档时,应该保证考虑到了哪些无法打印的边缘区域。

Android打印框架Demo

最近在学习Android API相关知识,才后知后觉地发现Android带有原生的打印功能。参考了 http://www.kuqin.com/shuoit/20140625/340810.html网站...
  • true100
  • true100
  • 2016年04月28日 09:54
  • 4319

Android 打印小结

使用Android客户端实现打印的尝试
  • u010278882
  • u010278882
  • 2015年11月13日 17:30
  • 2768

超好用的Android日志打印框架--Logger

Android提供的打印日志的方法是通过android.util.Log类来打印我们需要的日志信息,当打印的日志很多的时候,我们需要查看是哪一行就比较困难,除非你打上了标记,但是这样会比较麻烦。这个时...
  • chengliang0315
  • chengliang0315
  • 2016年12月27日 14:15
  • 4519

Android官方开发文档Training系列课程中文版:打印内容之自定义文档打印

原文地址:http://android.xsoftlab.net/training/printing/custom-docs.html对于一些应用,比如绘图类APP,版面设计类APP以及其它APP,这...
  • u011064099
  • u011064099
  • 2016年05月04日 12:06
  • 3593

使用Android系统打印功能

一、打印图片 使用PrintHelper类,如: private void doPhotoPrint() { PrintHelper photoPrinter = new PrintHel...
  • ssjj_programmer
  • ssjj_programmer
  • 2016年12月14日 22:24
  • 4270

android连接打印机打印

Android用户经常只在自己的设备上查看内容,但有时显示某人的屏幕不是一种充分的方式来共享信息。 您可以从Android应用程式列印资讯,让使用者可以透过应用程式查看更大版本的内容,或与未使用您应用...
  • dengpeng_
  • dengpeng_
  • 2017年03月08日 11:38
  • 8248

六款值得推荐的android(安卓)开源框架简介

六款值得推荐的android(安卓)开源框架简介
  • u010375364
  • u010375364
  • 2016年07月27日 23:54
  • 846

android开发打印票据或文档的方法:android打印文档的类

最近在做个一个基于android平台的工厂ERP项目,需要用到android平板连接打印机打印文档的功能。经过在网上一顿狂搜之后,发现目前好像打印机对android的直接没有任何标准,再不就是自家封的...
  • sdvch
  • sdvch
  • 2015年04月17日 13:56
  • 5010

Android遇上打印机

打印机其实和Android没有什么大的关系,和linux内核关联才是比较强的。最近调试打印机,有那么一点心得,一点一点记录下来。      最终的结果是要在Android实现驱动打印机,但是一般调试一...
  • kangear
  • kangear
  • 2014年01月11日 16:34
  • 31391

Android通过系统打印功能实现PDF预览打印

一、     简介         Android4.4(KitKat,api-19)系统内置了打印框架,通过安装对应打印机的打印插件,就可以容易实现打印功能;         关于 图片、webvi...
  • tangxl2008008
  • tangxl2008008
  • 2017年11月02日 14:54
  • 778
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【Android应用开发技术:媒体开发】打印
举报原因:
原因补充:

(最多只允许输入30个字)