はじめに (対象読者・この記事でわかること)
この記事は、Androidアプリ内でExcel(xls)ファイルを開かずに直接印刷したいJava開発者を対象にしています。
特に、
- 帳票データをExcelで受け取り「そのまま印刷」したい
- サーバ側でPDF変換を挟みたくない
- 印刷設定(用紙、倍率、色モノクロ)をアプリ側で制御したい
という要望を持つ方に最適です。
読み進めることで、jExcelApiを使ったxls読み込み→Bitmapレンダリング→PrintManager経由での印刷まで、実装レベルで全体像が掴めます。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- Android StudioでのJavaプロジェクト作成・ビルド経験
- Android 4.4以降のPrintManager/PrintDocumentAdapterの基礎
- Excelブック=ワークシート=セルの概念
- Gradleでの依存追加方法
なぜ「xlsをそのまま印刷」が面倒なのか
Android標準SDKには、Excelを扱うAPIが存在しません。
PlayStoreにある「Excel Viewer」アプリは、MicrosoftやGoogleのオフィスアプリにIntentを委譲するため、印刷設定を自社アプリ内で完了させることができません。
さらに、xlsx形式ならApache POIが使えますが、レガシーなxls(BIFF8)形式はPOIのHSSFより軽量なjExcelApiの方が、Android上でメモリ不足になりにくいという事情もあります。
本記事では、これらの課題をクリアする「xls→自前レンダリング→印刷」までの一連の実装を解説します。
ステップバイステップで実装する
ステップ1:jExcelApiを導入してxlsを読み込む
-
依存を追加
2025年6月時点でJCenterが廃止されているため、JitPack経由が安定しています。
gradle repositories { maven { url 'https://jitpack.io' } } dependencies { implementation 'com.github.ixion:jexcelapi:2.6.13' } -
読み込みクラスを作成
ワークブックを開き、最初のシートを取得します。
java Workbook workbook = Workbook.getWorkbook(new File(xlsPath)); Sheet sheet = workbook.getSheet(0); int rows = sheet.getRows(); int cols = sheet.getColumns(); -
セル単位でBitmapに描画
セルの内容をCanvasに書き込み、1枚のBitmapとして保持します。
java Bitmap bitmap = Bitmap.createBitmap(cols * CELL_WIDTH, rows * CELL_HEIGHT, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); for (int r = 0; r < rows; r++) { for (int c = 0; c < cols; c++) { String txt = sheet.getCell(c, r).getContents(); canvas.drawText(txt, c * CELL_WIDTH, (r + 1) * CELL_HEIGHT, paint); } }
ステップ2:PrintDocumentAdapterを実装する
-
印刷ジョブを開始
PrintManagerに対して、独自のPrintDocumentAdapterを登録します。
java PrintManager mgr = (PrintManager) context.getSystemService(PRINT_SERVICE); mgr.print("xls-report-job", new XlsPrintAdapter(bitmap), null); -
onLayoutでページ数を確定
用紙サイズ(A4)と倍率を考慮し、縦に何枚に分かれるか計算します。
java @Override public void onLayout(PrintAttributes old, PrintAttributes newAttr, CancellationSignal cancel, LayoutResultCallback callback, Bundle extras) { PrintDocumentInfo info = new PrintDocumentInfo .Builder("report.xls") .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) .setPageCount(pageCount) .build(); callback.onLayoutFinished(info, true); } -
onWriteでページごとにBitmapを描画
今回は1シートを1枚のBitmapに展開済みなので、ページ番号に応じてクリップ領域を切り出して出力します。
java @Override public void onWrite(PageRange[] pages, ParcelFileDescriptor dest, CancellationSignal cancel, WriteResultCallback callback) { PrintedPdfDocument pdf = new PrintedPdfDocument(context, newAttr); for (int i = 0; i < pageCount; i++) { PdfDocument.Page page = pdf.startPage(i); // 1ページ分をBitmapとして描画 Matrix m = new Matrix(); m.postScale(scale, scale); page.getCanvas().drawBitmap(bitmap, m, null); pdf.finishPage(page); } try (FileOutputStream os = new FileOutputStream(dest.getFileDescriptor())) { pdf.writeTo(os); } catch (IOException e) { /* エラーハンドリング */ } pdf.close(); callback.onWriteFinished(pages); }
ハマった点・エラー解決
| 現象 | 原因 | 対処 |
|---|---|---|
java.lang.NoClassDefFoundError: jxl.Workbook |
jExcelApiがmultidexに対応していない | android.enableR8.fullMode=falseを一時的に無効化 |
| 印刷プレビューで文字化け | 日本語フォントが存在しない | アセットにNotoSansCJKを配置し、Typeface.createFromAssetで明示的に読み込む |
| OutOfMemoryError | シートが大きすぎてBitmap展開時にメモリ不足 | 1000行を超える場合は、縦方向に複数のBitmapに分割してから印刷ジョブを発行 |
まとめ
本記事では、AndroidでExcel(xls)データを「アプリ内完結」で印刷する手法を解説しました。
- jExcelApiを使った軽量xls読み込み
- Bitmapレンダリング+PrintManager連携
- ページ分割・メモリ対策のポイント
これにより、サーバでPDF変換を用意せず、オフライン環境でも帳票印刷が可能になります。
次回は「xlsx対応」「印刷履歴の永続化」「暗号化xlsの復号&印刷」に挑戦したいと思います。
参考資料
