Untuk beberapa aplikasi, seperti aplikasi menggambar, aplikasi tata letak halaman, dan aplikasi lain yang fokus pada output grafis, menciptakan halaman cetak yang indah adalah fitur utamanya. Dalam hal ini, tidak cukup untuk mencetak gambar atau dokumen HTML. Output cetak untuk jenis aplikasi ini memerlukan mengontrol secara akurat semua hal yang masuk ke dalam laman, termasuk {i>font<i}, alur teks, batas laman, {i>header<i}, {i>footer<i}, dan elemen grafis.
Membuat output cetak yang sepenuhnya disesuaikan untuk aplikasi Anda memerlukan lebih banyak lebih banyak daripada pendekatan yang telah dibahas sebelumnya. Anda harus membangun komponen yang berkomunikasi dengan kerangka kerja cetak, menyesuaikan pengaturan {i>printer<i}, menggambar elemen halaman dan mengelola pencetakan di beberapa halaman.
Pelajaran ini menunjukkan kepada Anda cara terhubung dengan pengelola cetak, membuat adaptor cetak, dan membuat konten untuk dicetak.
Menghubungkan ke pengelola cetak
Saat aplikasi Anda mengelola proses pencetakan secara langsung, langkah pertama setelah menerima
cetak dari pengguna adalah terhubung ke framework cetak Android dan mendapatkan instance
dari class PrintManager
. Class ini memungkinkan Anda melakukan inisialisasi tugas pencetakan
dan memulai siklus proses pencetakan. Contoh kode berikut menunjukkan cara mendapatkan pengelola pencetakan
dan memulai proses pencetakan.
Kotlin
private fun doPrint() { activity?.also { context -> // Get a PrintManager instance val printManager = context.getSystemService(Context.PRINT_SERVICE) as PrintManager // Set job name, which will be displayed in the print queue val jobName = "${context.getString(R.string.app_name)} Document" // Start a print job, passing in a PrintDocumentAdapter implementation // to handle the generation of a print document printManager.print(jobName, MyPrintDocumentAdapter(context), null) } }
Java
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"; // Start a print job, passing in a PrintDocumentAdapter implementation // to handle the generation of a print document printManager.print(jobName, new MyPrintDocumentAdapter(getActivity()), null); // }
Kode contoh di atas menunjukkan cara memberi nama pekerjaan cetak dan menetapkan instance class PrintDocumentAdapter
yang menangani langkah-langkah siklus proses pencetakan. Tujuan
implementasi class adaptor cetak dibahas di bagian berikutnya.
Catatan: Parameter terakhir di print()
mengambil objek PrintAttributes
. Anda dapat menggunakan parameter ini untuk
memberikan petunjuk ke framework pencetakan dan opsi preset berdasarkan siklus pencetakan sebelumnya,
sehingga meningkatkan
pengalaman pengguna. Anda juga dapat menggunakan parameter ini
untuk mengatur opsi yang
lebih sesuai untuk konten yang dicetak, seperti menyetel orientasi ke lanskap
saat mencetak foto dengan orientasi tersebut.
Membuat adaptor cetak
Adaptor cetak berinteraksi dengan framework cetak Android dan menangani langkah-langkah proses pencetakan. Proses ini mengharuskan pengguna memilih printer dan opsi cetak sebelum membuat dokumen untuk dicetak. Pilihan ini dapat mempengaruhi {i>output<i} akhir saat pengguna memilih {i>printer<i} dengan kemampuan output yang berbeda, ukuran halaman yang berbeda, atau orientasi halaman yang berbeda. Setelah pilihan ini dibuat, framework cetak akan meminta adaptor untuk menyusun dan membuat cetak dokumen, sebagai persiapan untuk {i>output<i} akhir. Setelah pengguna mengetuk tombol cetak, framework mengambil dokumen cetak akhir dan meneruskannya ke penyedia cetak untuk output. Selama pencetakan , pengguna dapat memilih membatalkan tindakan cetak, sehingga adaptor cetak Anda juga harus memproses dan menanggapi permintaan pembatalan.
Class abstrak PrintDocumentAdapter
dirancang untuk menangani
siklus proses pencetakan, yang memiliki empat metode callback utama. Anda harus mengimplementasikan metode ini
di adaptor cetak agar dapat berinteraksi dengan benar dengan framework cetak:
onStart()
- Menelepon sekali pada awal proses cetak. Jika aplikasi Anda memiliki tugas persiapan satu kali untuk lakukan, seperti mendapatkan {i>snapshot<i} dari data yang akan dicetak, jalankan di sini. Menerapkan metode ini dalam adaptor Anda tidak diperlukan.onLayout()
- Dipanggil setiap kali pengguna mengubah pengaturan cetak yang berdampak pada {i>output<i}, seperti ukuran halaman yang berbeda, atau orientasi halaman, yang memberi aplikasi Anda peluang untuk menghitung tata letak halaman yang akan dicetak. Minimal, metode ini harus menampilkan jumlah halaman yang diharapkan di dokumen yang dicetak.onWrite()
- Dipanggil untuk merender hasil cetak halaman menjadi file yang akan dicetak. Metode ini dapat dipanggil satu atau beberapa kali setelah masing-masing PanggilanonLayout()
.onFinish()
- Menelepon sekali di akhir dari proses cetak. Jika aplikasi Anda memiliki tugas pembongkaran satu kali untuk dilakukan, jalankan di sini. Menerapkan metode ini pada adaptor tidak diperlukan.
Bagian berikut ini menjelaskan cara mengimplementasikan tata letak dan metode penulisan, yang yang sangat penting untuk fungsi{i> <i}adaptor cetak.
Catatan: Metode adaptor ini dipanggil di thread utama aplikasi Anda. Jika
Anda mengharapkan eksekusi metode ini dalam implementasi
memerlukan banyak waktu
waktu, menerapkannya untuk dieksekusi dalam thread terpisah. Misalnya, Anda dapat melakukan enkapsulasi
menata letak atau mencetak tugas penulisan dokumen dalam objek AsyncTask
terpisah.
Menghitung info dokumen cetak
Dalam implementasi class PrintDocumentAdapter
, metode
aplikasi harus dapat menentukan jenis dokumen yang dibuatnya dan menghitung jumlah total
jumlah halaman untuk tugas pencetakan, berdasarkan informasi tentang ukuran halaman yang dicetak.
Implementasi metode onLayout()
di
adaptor membuat perhitungan ini dan memberikan
informasi tentang {i>output<i} yang diharapkan dari
cetak di class PrintDocumentInfo
, termasuk jumlah halaman dan
jenis konten. Contoh kode berikut menunjukkan implementasi dasar metode onLayout()
untuk PrintDocumentAdapter
:
Kotlin
override fun onLayout( oldAttributes: PrintAttributes?, newAttributes: PrintAttributes, cancellationSignal: CancellationSignal?, callback: LayoutResultCallback, extras: Bundle? ) { // Create a new PdfDocument with the requested page attributes pdfDocument = PrintedPdfDocument(activity, newAttributes) // Respond to cancellation request if (cancellationSignal?.isCanceled == true) { callback.onLayoutCancelled() return } // Compute the expected number of printed pages val pages = computePageCount(newAttributes) if (pages > 0) { // Return print information to print framework PrintDocumentInfo.Builder("print_output.pdf") .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT) .setPageCount(pages) .build() .also { info -> // Content layout reflow is complete callback.onLayoutFinished(info, true) } } else { // Otherwise report an error to the print framework callback.onLayoutFailed("Page count calculation failed.") } }
Java
@Override public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle metadata) { // Create a new PdfDocument with the requested page attributes pdfDocument = new PrintedPdfDocument(getActivity(), newAttributes); // Respond to cancellation request if (cancellationSignal.isCanceled() ) { 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."); } }
Eksekusi metode onLayout()
dapat
memiliki tiga hasil: penyelesaian, pembatalan, atau kegagalan dalam kasus di mana perhitungan
tata letak tidak dapat diselesaikan. Anda harus menunjukkan salah satu dari hasil ini dengan memanggil
objek PrintDocumentAdapter.LayoutResultCallback
.
Catatan: Parameter boolean
Metode onLayoutFinished()
menunjukkan apakah konten tata letak benar-benar telah berubah atau tidak
sejak permintaan terakhir. Menyetel parameter ini dengan benar memungkinkan framework cetak menghindari
memanggil metode onWrite()
yang tidak perlu,
melakukan {i>caching<i} dokumen cetak
yang ditulis sebelumnya dan meningkatkan kinerja.
Pekerjaan utama onLayout()
adalah
menghitung jumlah halaman yang diharapkan sebagai {i>output<i} berdasarkan atribut {i>printer<i}.
Cara Anda menghitung jumlah ini sangat bergantung pada cara aplikasi mengatur tata letak laman untuk
pencetakan. Contoh kode berikut menunjukkan penerapan dengan jumlah halaman
ditentukan oleh orientasi cetak:
Kotlin
private fun computePageCount(printAttributes: PrintAttributes): Int { var itemsPerPage = 4 // default item count for portrait mode val pageSize = printAttributes.mediaSize if (!pageSize.isPortrait) { // Six items per page in landscape orientation itemsPerPage = 6 } // Determine number of print items val printItemCount: Int = getPrintItemCount() return Math.ceil((printItemCount / itemsPerPage.toDouble())).toInt() }
Java
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); }
Menulis file dokumen cetak
Jika tiba saatnya menulis output cetak untuk suatu file, framework cetak Android akan memanggil metode onWrite()
dari class PrintDocumentAdapter
aplikasi Anda. Parameter metode ini menetapkan halaman mana yang harus
ditulis dan file {i>output<i} yang akan digunakan. Implementasi Anda terhadap metode ini kemudian harus merender setiap
halaman konten yang diminta ke file dokumen PDF multi-halaman. Ketika proses ini selesai, Anda
memanggil metode onWriteFinished()
dari objek callback.
Catatan: Framework cetak Android dapat memanggil metode onWrite()
satu atau beberapa kali untuk setiap
panggilan ke onLayout()
. Untuk alasan ini,
penting untuk mengatur
parameter boolean
onLayoutFinished()
ke false
jika tata letak konten cetak tidak berubah,
untuk menghindari penulisan ulang
dokumen cetak yang tidak perlu.
Catatan: Parameter boolean
Metode onLayoutFinished()
menunjukkan apakah konten tata letak benar-benar telah berubah atau tidak
sejak permintaan terakhir. Menyetel parameter ini dengan benar memungkinkan framework cetak menghindari
memanggil metode onLayout()
yang tidak perlu,
melakukan {i>caching<i} dokumen cetak
yang ditulis sebelumnya dan meningkatkan kinerja.
Contoh berikut menunjukkan mekanisme dasar dari proses ini menggunakan class PrintedPdfDocument
untuk membuat file PDF:
Kotlin
override fun onWrite( pageRanges: Array<out PageRange>, destination: ParcelFileDescriptor, cancellationSignal: CancellationSignal?, callback: WriteResultCallback ) { // Iterate over each page of the document, // check if it's in the output range. for (i in 0 until totalPages) { // 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?.startPage(i)?.also { page -> // check for cancellation if (cancellationSignal?.isCanceled == true) { callback.onWriteCancelled() pdfDocument?.close() pdfDocument = null return } // Draw page content for printing drawPage(page) // Rendering is complete, so page can be finalized. pdfDocument?.finishPage(page) } } } // Write PDF document to file try { pdfDocument?.writeTo(FileOutputStream(destination.fileDescriptor)) } catch (e: IOException) { callback.onWriteFailed(e.toString()) return } finally { pdfDocument?.close() pdfDocument = null } val writtenPages = computeWrittenPages() // Signal the print framework the document is complete callback.onWriteFinished(writtenPages) ... }
Java
@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 = pdfDocument.startPage(i); // check for cancellation if (cancellationSignal.isCanceled()) { callback.onWriteCancelled(); pdfDocument.close(); pdfDocument = null; return; } // Draw page content for printing drawPage(page); // Rendering is complete, so page can be finalized. pdfDocument.finishPage(page); } } // Write PDF document to file try { pdfDocument.writeTo(new FileOutputStream( destination.getFileDescriptor())); } catch (IOException e) { callback.onWriteFailed(e.toString()); return; } finally { pdfDocument.close(); pdfDocument = null; } PageRange[] writtenPages = computeWrittenPages(); // Signal the print framework the document is complete callback.onWriteFinished(writtenPages); ... }
Contoh ini mendelegasikan rendering konten halaman PDF ke drawPage()
, yang akan dibahas di bagian berikutnya.
Seperti halnya tata letak, eksekusi onWrite()
dapat memiliki tiga hasil: penyelesaian, pembatalan, atau kegagalan dalam kasus di mana
konten tidak dapat ditulis. Anda harus menunjukkan salah satu dari hasil ini dengan memanggil metode
yang sesuai dari objek PrintDocumentAdapter.WriteResultCallback
.
Catatan: Merender dokumen untuk dicetak dapat menjadi operasi intensif resource. Di beberapa
untuk menghindari pemblokiran thread antarmuka pengguna utama pada aplikasi, sebaiknya pertimbangkan
melakukan operasi rendering dan penulisan halaman di thread terpisah, misalnya
di AsyncTask
.
Untuk informasi selengkapnya tentang bekerja dengan
thread eksekusi seperti tugas asinkron,
lihat Proses
dan Threads.
Menggambar konten halaman PDF
Saat aplikasi Anda mencetak, aplikasi harus menghasilkan dokumen PDF dan meneruskannya ke
framework cetak Android untuk pencetakan. Anda dapat menggunakan library pembuatan PDF apa pun untuk ini
tujuan. Tutorial ini menunjukkan cara menggunakan class PrintedPdfDocument
untuk membuat halaman PDF dari konten Anda.
Class PrintedPdfDocument
menggunakan Canvas
untuk menggambar elemen pada halaman PDF, mirip dengan menggambar di tata letak aktivitas. Anda bisa menggambar
pada halaman yang dicetak menggunakan metode gambar Canvas
. Hal berikut
kode contoh menunjukkan cara menggambar beberapa elemen sederhana pada halaman dokumen PDF menggunakan
metode:
Kotlin
private fun drawPage(page: PdfDocument.Page) { page.canvas.apply { // units are in points (1/72 of an inch) 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) } }
Java
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); }
Saat menggunakan Canvas
untuk menggambar pada halaman PDF, elemen ditentukan dalam
poin, yaitu 1/72 inci. Pastikan Anda menggunakan satuan ukuran ini untuk menentukan ukuran
elemen pada halaman. Untuk penentuan posisi elemen yang digambar, sistem koordinat dimulai dari 0,0
di sudut kiri atas halaman.
Tips: Meskipun objek Canvas
memungkinkan Anda melakukan pencetakan
elemen di tepi dokumen PDF, banyak {i>printer<i} tidak dapat mencetak ke tepi dokumen
sepotong kertas fisik. Pastikan bahwa Anda memperhitungkan tepi laman yang tidak dapat dicetak saat
Anda akan membuat dokumen cetak dengan class ini.