Çizim uygulamaları, sayfa düzeni uygulamaları ve grafik çıktısı odaklı diğer uygulamalar gibi bazı uygulamalarda, göz alıcı basılı sayfalar oluşturmak temel özelliklerden biridir. Bu durumda resim veya HTML belgesi yazdırmak yeterli olmaz. Bu tür uygulamaların yazdırma çıktısı, yazı tipleri, metin akışı, sayfa sonları, üstbilgiler, altbilgiler ve grafik öğeler dahil olmak üzere bir sayfaya gelen her şeyin hassas bir şekilde kontrol edilmesini gerektirir.
Uygulamanız için tamamen özelleştirilmiş bir baskı çıktısı oluşturmak, daha önce tartışılan yaklaşımlara kıyasla daha fazla programlama yatırımı gerektirir. Yazdırma çerçevesiyle iletişim kuran bileşenler oluşturmanız, yazıcı ayarlarına göre ayarlama yapmanız, sayfa öğeleri çizmeniz ve birden fazla sayfada yazdırmayı yönetmeniz gerekir.
Bu derste, yazıcı yöneticisiyle nasıl bağlantı kuracağınızı, yazdırma bağdaştırıcısını nasıl oluşturacağınızı ve yazdırma için nasıl içerik oluşturacağınızı öğreneceksiniz.
Yazdırma yöneticisine bağlanma
Uygulamanız yazdırma işlemini doğrudan yönettiğinde, kullanıcınızdan yazdırma isteği aldıktan sonraki ilk adım Android yazdırma çerçevesine bağlanmak ve PrintManager
sınıfının bir örneğini almaktır. Bu sınıf, bir yazdırma işini başlatmanıza ve yazdırma yaşam döngüsünü başlatmanıza olanak tanır. Aşağıdaki kod örneğinde, yazıcı yöneticisini alacağınız ve yazdırma işlemini nasıl başlatacağınız gösterilmektedir.
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); // }
Yukarıdaki örnek kodda, bir yazdırma işini nasıl adlandıracağınız ve yazdırma yaşam döngüsünün adımlarını uygulayan PrintDocumentAdapter
sınıfının bir örneğinin nasıl ayarlanacağı gösterilmektedir. Yazdırma bağdaştırıcısı sınıfının uygulanması bir sonraki bölümde ele alınmaktadır.
Not: print()
yöntemindeki son parametre bir PrintAttributes
nesnesi alır. Yazdırma çerçevesiyle ilgili ipuçları sağlamak ve önceki yazdırma döngüsüne göre önceden ayarlanmış seçenekler sağlamak için bu parametreyi kullanabilir, böylece kullanıcı deneyimini iyileştirebilirsiniz. Bu parametreyi, yazdırılan içerik için daha uygun olan seçenekleri (söz konusu yönde bir fotoğrafı yazdırırken yönü yatay olarak ayarlama gibi) belirlemek için de kullanabilirsiniz.
Yazdırma bağdaştırıcısı oluşturma
Yazdırma bağdaştırıcısı, Android yazdırma çerçevesiyle etkileşime girer ve yazdırma işleminin adımlarını işler. Bu işlem, kullanıcıların yazdırmak için bir doküman oluşturmadan önce yazıcıları ve yazdırma seçeneklerini belirlemesini gerektirir. Kullanıcı farklı çıkış özelliklerine, farklı sayfa boyutlarına veya farklı sayfa yönlerine sahip yazıcıları seçtiği için bu seçimler nihai sonucu etkileyebilir. Bu seçimler yapıldığında baskı çerçevesi, nihai çıktıya hazırlanmak için adaptörünüzden bir baskı dokümanı hazırlamasını ve oluşturmasını ister. Bir kullanıcı yazdır düğmesine dokunduktan sonra, çerçeve son yazdırma dokümanını alır ve çıktısı için bir yazdırma sağlayıcısına iletir. Yazdırma işlemi sırasında kullanıcılar yazdırma işlemini iptal etmeyi seçebilir. Bu nedenle yazdırma adaptörünüzün de iptal isteklerini dinlemesi ve bunlara yanıt vermesi gerekir.
PrintDocumentAdapter
soyut sınıfı, dört ana geri çağırma yöntemi içeren yazdırma yaşam döngüsünü işlemek için tasarlanmıştır. Yazdırma çerçevesiyle doğru şekilde etkileşim kurmak için yazdırma bağdaştırıcınızda aşağıdaki yöntemleri uygulamanız gerekir:
onStart()
: Yazdırma işleminin başında bir kez çağrıldı. Uygulamanızın, yazdırılacak verilerin anlık görüntüsünü alma gibi tek seferlik hazırlama görevleri varsa bunları burada yürütün. Bağdaştırıcınızda bu yöntemi uygulamanız gerekmez.onLayout()
- Kullanıcılar çıktıyı etkileyen bir yazdırma ayarını (ör. farklı sayfa boyutu veya sayfa yönü) her değiştirdiğinde çağrılır; bu da uygulamanıza, yazdırılacak sayfaların düzenini hesaplama fırsatı verir. En azından, bu yöntem yazdırılan dokümanda kaç sayfanın beklendiğini döndürmelidir.onWrite()
: Yazdırılan sayfaları yazdırılacak bir dosya halinde oluşturmak için çağrılır. Bu yöntem, heronLayout()
çağrısından sonra bir veya daha fazla kez çağrılabilir.onFinish()
: Yazdırma işleminin sonunda bir defa çağrıldı. Uygulamanızın gerçekleştirmesi gereken tek seferlik sökme görevleri varsa bunları burada yürütün. Bağdaştırıcınızda bu yöntemi uygulamanız gerekmez.
Aşağıdaki bölümlerde, yazdırma adaptörünün çalışması için kritik öneme sahip olan düzen ve yazma yöntemlerinin nasıl uygulanacağı açıklanmaktadır.
Not: Bu bağdaştırıcı yöntemleri uygulamanızın ana iş parçacığında çağrılır. Uygulamanızda bu yöntemlerin yürütülmesinin çok zaman alacağını düşünüyorsanız bunları ayrı bir iş parçacığında yürütülecek şekilde uygulayın. Örneğin, düzeni kapsülleyebilir veya belge yazma çalışmasını ayrı AsyncTask
nesnelerine yazdırabilirsiniz.
Basılı belge bilgilerini hesapla
PrintDocumentAdapter
sınıfının uygulamasında, uygulamanızın oluşturduğu belge türünü belirtmesi ve yazdırılan sayfa boyutu hakkında bilgi verildiğinde yazdırma işi için toplam sayfa sayısını hesaplayabilmesi gerekir.
Bağdaştırıcıda onLayout()
yönteminin uygulanması bu hesaplamaları yapar ve sayfa sayısı ile içerik türü de dahil olmak üzere PrintDocumentInfo
sınıfındaki yazdırma işinin beklenen çıkışı hakkında bilgi sağlar. Aşağıdaki kod örneğinde, PrintDocumentAdapter
için onLayout()
yönteminin temel bir uygulaması gösterilmektedir:
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."); } }
onLayout()
yönteminin çalıştırılmasının üç sonucu olabilir: tamamlanma, iptal etme veya düzen hesaplamasının tamamlanamadığı durumlarda başarısız olma. PrintDocumentAdapter.LayoutResultCallback
nesnesinin uygun yöntemini çağırarak bu sonuçlardan birini belirtmeniz gerekir.
Not: onLayoutFinished()
yönteminin boole parametresi, düzen içeriğinin son istekten bu yana gerçekten değişip değişmediğini ifade eder. Bu parametrenin doğru şekilde ayarlanması, yazdırma çerçevesinin onWrite()
yöntemini gereksiz yere çağırmayı önlemesine olanak tanır. Bu işlem, temelde önceden yazılmış yazdırma dokümanını önbelleğe alır ve performansı artırır.
onLayout()
işlevinin ana işi, yazıcının özellikleri dikkate alınarak çıktı olarak beklenen sayfa sayısını hesaplamaktır.
Bu sayıyı nasıl hesaplayacağınız, büyük ölçüde uygulamanızın sayfaları baskı için nasıl hazırladığına bağlıdır. Aşağıdaki kod örneğinde, sayfa sayısının yazdırma yönüne göre belirlendiği bir uygulama gösterilmektedir:
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); }
Yazdırılacak belge dosyası yazın
Bir dosyaya yazdırma çıkışı yazma zamanı geldiğinde, Android yazdırma çerçevesi, uygulamanızın PrintDocumentAdapter
sınıfının onWrite()
yöntemini çağırır. Yöntemin parametreleri, hangi sayfaların yazılması gerektiğini ve kullanılacak çıkış dosyasını belirtir. Bu yöntemi uygulamanız, istenen her içerik sayfasını çok sayfalı bir PDF belge dosyası olarak oluşturmalıdır. Bu işlem tamamlandığında, geri çağırma nesnesinin onWriteFinished()
yöntemini çağırırsınız.
Not: Android yazdırma çerçevesi, onWrite()
yöntemini her onLayout()
çağrısı için bir veya daha fazla kez çağırabilir. Bu nedenle, yazdırma dokümanının gereksiz yere yeniden yazılmasını önlemek için, yazdırma içeriği düzeni değişmediğinde onLayoutFinished()
yönteminin boole parametresinin false
olarak ayarlanması önemlidir.
Not: onLayoutFinished()
yönteminin boole parametresi, düzen içeriğinin son istekten bu yana gerçekten değişip değişmediğini ifade eder. Bu parametrenin doğru şekilde ayarlanması, yazdırma çerçevesinin onLayout()
yöntemini gereksiz yere çağırmayı önlemesine olanak tanır. Bu işlem, temelde önceden yazılmış yazdırma dokümanını önbelleğe alır ve performansı artırır.
Aşağıdaki örnekte, bir PDF dosyası oluşturmak için PrintedPdfDocument
sınıfının kullanıldığı bu işlemin temel mekanizması gösterilmektedir:
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); ... }
Bu örnekte, PDF sayfası içeriğinin oluşturulması için drawPage()
yöntemi yetki verilir. Bu yöntem bir sonraki bölümde ele alınacaktır.
Düzende olduğu gibi, onWrite()
yönteminin çalıştırılması üç sonuç olabilir: tamamlama, iptal veya içeriğin yazılamadığı durumda başarısızlık. PrintDocumentAdapter.WriteResultCallback
nesnesinin uygun yöntemini çağırarak bu sonuçlardan birini belirtmeniz gerekir.
Not: Bir dokümanı yazdırmak için oluşturmak, yoğun kaynak kullanan bir işlem olabilir. Uygulamanızın ana kullanıcı arayüzü iş parçacığını engellememek için sayfa oluşturma ve yazma işlemlerini ayrı bir iş parçacığında (örneğin, AsyncTask
) gerçekleştirmeyi düşünmeniz gerekir.
Eşzamansız görevler gibi yürütme iş parçacıklarıyla çalışma hakkında daha fazla bilgi için İşlemler ve İş Parçacıkları bölümüne bakın.
Çizim PDF sayfası içeriği
Uygulamanız yazdırıldığında uygulamanız bir PDF dokümanı oluşturmalı ve bu dokümanı baskı için Android yazdırma çerçevesine iletmelidir. Bu amaçla dilediğiniz PDF oluşturma kitaplığını kullanabilirsiniz. Bu derste, içeriğinizden PDF sayfaları oluşturmak için PrintedPdfDocument
sınıfının nasıl kullanılacağı gösterilmektedir.
PrintedPdfDocument
sınıfı, PDF sayfasında öğe çizmek için etkinlik düzeni üzerinde yapılan çizime benzer şekilde bir Canvas
nesnesi kullanır. Canvas
çizim yöntemlerini kullanarak yazdırılan sayfada öğeler çizebilirsiniz. Aşağıdaki örnek kod, PDF belgesi sayfasında bazı basit öğelerin aşağıdaki yöntemleri kullanarak nasıl çizileceğini gösterir:
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); }
Bir PDF sayfasında çizim yapmak için Canvas
kullanılırken öğeler noktalar cinsinden belirtilir. Bu, bir inçin 1/72'sidir. Sayfadaki öğelerin boyutunu belirtmek için bu ölçü birimini kullandığınızdan emin olun. Çizilen öğelerin konumlandırması için koordinat sistemi, sayfanın sol üst köşesi için 0,0'dan başlar.
İpucu: Canvas
nesnesi, yazdırma öğelerini PDF dokümanlarının kenarına yerleştirmenize olanak sağlasa da, birçok yazıcı fiziksel bir kağıdın kenarına yazdıramaz. Bu sınıfla bir yazdırma dokümanı oluştururken sayfanın yazdırılamayan kenarlarını hesaba kattığınızdan emin olun.