AndroidのPrintManagerを使って画面を印刷する方法

[mixiグループ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/mixi) 14日目の記事です。
株式会社ミクシィで新規事業の業務向けアプリを書いているakkumaです。
今回はAndroidで印刷をする方法を書きます。

## プリントサービスプラグイン

まずAndroidの印刷API経由で好きな内容を印刷するには、Google play上で提供されているプリンターに対応した**プリントサービスプラグイン**をインストールしてある必要があります。メーカーがプラグインを提供していれば、そのメーカーが出している最近のほとんどの機種で利用できる印象です。

キヤノンの例
http://cweb.canon.jp/apps/plug-in/printservice.html

エプソンの例
http://www.epson.jp/spmd/service_plugin/faq.htm#anchor12

またもちろんですが、プリンターにネットワーク経由でアクセスできる環境にいる必要があります。
メーカーのプリントサービスプラグインの実装によってはプリンターのIPアドレスを直接指定することができず、同一サブネット上からプリンターを検出するのみのタイプがあります。
そこは各社の実装に依存するので、プリンターを購入する前にプリントサービスプラグインの実装を確認することをおすすめします。

## PrintManagerとPrintDocumentAdapter

![PrintManagerとPrintDocumentAdapter.png](https://qiita-image-store.s3.amazonaws.com/0/75828/2f7c2456-80d0-32e6-f392-c1dc18bda778.png)


Androidでの印刷機能へのアクセスは[PrintManager](https://developer.android.com/reference/android/print/PrintManager.html?hl=ja)が担っています。`Context#getSystemService()`で取得することができます。
印刷したい内容を**PDFで書き出す**ことで、プリンターに印刷指示を出すことができます。

そこで、指定の用紙条件で作成されたPDFを`PrintManager`に渡すために[PrintDocumentAdapter](https://developer.android.com/reference/android/print/PrintDocumentAdapter.html?hl=ja)を実装する必要があります。
`PrintDocumentAdapter`を独自実装する場合は[公式の解説](https://developer.android.com/training/printing/custom-docs.html)をご覧ください。
後述の**Bitmap**や**WebView(Html)**であれば`PrintDocumentAdapter`を自分で実装せず簡単に印刷する事ができるため省略します。

`PrintDocumentAdapter`を利用した印刷の場合は`PrintManager#print()`を呼ぶことで印刷することができます。

```java
PrintManager printManager = (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
PrintDocumentAdapter adapter = new TestPrintDocumentAdapter(this);
printManager.print("job_name", adapter, null);
```

## WebViewの印刷

WebViewでは、`createPrintDocumentAdapter()`に呼ぶことで表示内容を印刷するためのPrintAdapterが提供されています。
HTMLを生成すれば良いので、複雑なドキュメントの印刷を要求された場合の妥協点かと思います。

```java
private void printWebView(WebView webView) {
    PrintDocumentAdapter adapter = webView.createPrintDocumentAdapter();
    PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
    printManager.print("job_name", adapter, null);
}
```

## Bitmapの印刷

サポートライブラリsupport-v4の[PrintHelper](https://developer.android.com/reference/android/support/v4/print/PrintHelper.html)がBitmapを印刷するためのヘルパーを提供していて、簡単に実装することができます。

```java
private void printBitmap(Bitmap bitmap) {
    PrintHelper printHelper = new PrintHelper(context);
    printHelper.setColorMode(PrintHelper.COLOR_MODE_COLOR);
    printHelper.setScaleMode(PrintHelper.SCALE_MODE_FIT);
    printHelper.printBitmap("job_name", bitmap);
}
```

PrintAdapterの構造を意識しなくても印刷できます。

## Viewの内容を印刷してみる

`Bitmap`さえ取得できれば簡単に印刷することができるので、Viewを`Bitmap`に変換してから`PrintHelper`を使って印刷してみます。
せっかくなのでAndroidアプリを印刷したことを強調するために`getWindow().getDecorView()`を印刷してみます。

```java
private void printWindow() {
    View view = getWindow().getDecorView();
    Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    view.draw(canvas);

    PrintHelper printHelper = new PrintHelper(this);
    printHelper.setColorMode(PrintHelper.COLOR_MODE_COLOR);
    printHelper.setScaleMode(PrintHelper.SCALE_MODE_FIT);
    printHelper.printBitmap("view", bitmap);
}
```

Activityが表示された状態で`printWindow()`を実行すると

![device-2017-12-14-143812-1.png](https://qiita-image-store.s3.amazonaws.com/0/75828/893eb74c-2e11-8fd0-95e3-bbb0d2b98bc5.png)

Activityの内容がそのまま印刷内容になった印刷設定が出ます。
あとは印刷するだけ

![MVIMG_20171214_144548.jpg](https://qiita-image-store.s3.amazonaws.com/0/75828/3402187b-f7d8-f7f7-eacc-2c2a6d2add0d.jpeg)