Pour certaines applications, telles que les applications de dessin, les applications de mise en page et d'autres applications axées sur graphique, la création de belles pages imprimées est une caractéristique clé. Dans ce cas, il ne suffit pas pour imprimer une image ou un document HTML. Pour ces types d'applications, la sortie à imprimer un contrôle précis de tous les éléments d'une page, y compris les polices, le flux de texte, les sauts de page, les en-têtes, les pieds de page et les éléments graphiques.
Créer une sortie d'impression entièrement personnalisée pour votre application nécessite davantage que les approches mentionnées précédemment. Vous devez créer des composants communiquer avec le cadre d'impression, s'adapter aux paramètres de l'imprimante, dessiner les éléments de la page et gérer l'impression sur plusieurs pages.
Cette leçon vous explique comment vous connecter au gestionnaire d'impression, créer un adaptateur d'impression créer du contenu pour l'impression.
Se connecter au gestionnaire d'impression
Lorsque votre application gère directement le processus d'impression, la première étape suivant la réception d'une
d'impression de votre utilisateur consiste à se connecter au framework d'impression Android et à obtenir une instance
de la classe PrintManager
. Cette classe vous permet d'initialiser une tâche d'impression
et commencer le cycle de vie de l'impression. L'exemple de code suivant montre comment récupérer le gestionnaire d'impression
et lancer le processus d'impression.
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); // }
L'exemple de code ci-dessus montre comment nommer une tâche d'impression et définir une instance de la classe PrintDocumentAdapter
qui gère les étapes du cycle de vie de l'impression. La
l'implémentation de la classe de l'adaptateur d'impression est abordée dans la section suivante.
Remarque:Le dernier paramètre de print()
utilise un objet PrintAttributes
. Vous pouvez utiliser ce paramètre pour
Fournir des conseils sur le cadre d'impression et les options prédéfinies en fonction du cycle d'impression précédent
et ainsi améliorer
l'expérience utilisateur. Vous pouvez également utiliser ce paramètre pour définir des options
sont plus adaptées au contenu imprimé (par exemple, en optant pour le mode paysage) ;
lorsque vous imprimez une photo
dans cette orientation.
Créer un adaptateur d'impression
Un adaptateur d'impression interagit avec le framework d'impression Android et gère les étapes de la du processus d'impression. Ce processus oblige les utilisateurs à sélectionner les imprimantes et les options d'impression avant de créer un document à imprimer. Ces sélections peuvent influencer le résultat final choisi par l'utilisateur des imprimantes avec différentes capacités de sortie, différentes tailles de page ou différentes orientations de page. Au fur et à mesure que ces sélections sont effectuées, le framework d'impression demande à votre adaptateur de mettre en page et de générer un imprimer le document, en préparation de la sortie finale. Une fois qu'un utilisateur appuie sur le bouton d'impression, le cadre prend le document final imprimé et le transmet à un prestataire d'impression pour sortie. Pendant l'impression les utilisateurs peuvent choisir d'annuler l'action d'impression. Par conséquent, votre adaptateur d'impression doit également écouter et réagir aux demandes d'annulation.
La classe abstraite PrintDocumentAdapter
est conçue pour gérer le
cycle de vie d'impression, qui comporte quatre méthodes de rappel principales. Vous devez implémenter ces méthodes
dans votre adaptateur d'impression afin d'interagir correctement avec l'environnement d'impression:
onStart()
: appelé une fois au au début du processus d'impression. Si votre candidature comporte des tâches de préparation ponctuelles comme l'obtention d'un instantané des données à imprimer, exécutez-les ici. Implémentation cette méthode dans votre adaptateur n'est pas nécessaire.onLayout()
: appelé chaque fois que l'utilisateur modifie un paramètre d'impression qui a un impact sur le résultat, comme une taille de page différente, ou l'orientation de la page, ce qui permet à votre application de calculer la mise en page pages à imprimer. Au minimum, cette méthode doit renvoyer le nombre de pages attendu dans le document imprimé.onWrite()
: appelé pour afficher l'impression pages en un fichier à imprimer. Cette méthode peut être appelée une ou plusieurs fois après chaqueonLayout()
appel.onFinish()
: appelé une fois à la fin du processus d'impression. Si votre application a des tâches de suppression ponctuelles à effectuer, les exécuter ici. Il n'est pas nécessaire d'implémenter cette méthode dans votre adaptateur.
Les sections suivantes décrivent comment implémenter les méthodes de mise en page et d'écriture, qui sont essentiel au fonctionnement d'un adaptateur d'impression.
Remarque:Ces méthodes d'adaptateur sont appelées sur le thread principal de votre application. Si
vous vous attendez à ce que l'exécution de ces méthodes dans votre implémentation demande beaucoup
implémentez-les pour qu'elles s'exécutent dans un thread distinct. Par exemple, vous pouvez encapsuler le
mettre en page ou imprimer des documents dans des objets AsyncTask
distincts.
Informations sur les documents d'impression Compute
Dans une implémentation de la classe PrintDocumentAdapter
, votre
application doit être en mesure de spécifier le type de document qu'elle crée et de calculer le total
nombre de pages pour la tâche d'impression, en fonction du format de la page imprimée.
L'implémentation de la méthode onLayout()
dans
l'adaptateur effectue ces calculs et fournit des informations sur la sortie attendue du
d'une tâche d'impression dans une classe PrintDocumentInfo
, y compris le nombre de pages et
type de contenu. L'exemple de code suivant montre une implémentation de base de la méthode onLayout()
pour un 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."); } }
L'exécution de la méthode onLayout()
peut
ont trois résultats: l'achèvement, l'annulation ou l'échec dans le cas où le calcul du
Impossible de finaliser la mise en page. Vous devez indiquer l'un de ces résultats en appelant la méthode
de l'objet PrintDocumentAdapter.LayoutResultCallback
.
Remarque:Le paramètre booléen de la méthode
La méthode onLayoutFinished()
indique si le contenu de la mise en page a été modifié ou non.
depuis la dernière demande. La définition de ce paramètre correctement permet au framework d'impression d'éviter
appeler inutilement la méthode onWrite()
,
essentiellement mettre en cache le document imprimé
précédemment écrit et améliorer les performances.
L'action principale de onLayout()
est
calculer le nombre de pages attendues comme sortie en fonction des attributs de l'imprimante.
La façon dont vous calculez ce nombre dépend fortement de la façon dont votre application présente les pages pour
impression. L'exemple de code suivant illustre une implémentation dans laquelle le nombre de pages est
déterminée par l'orientation d'impression:
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); }
Écrire un fichier de document d'impression
Au moment d'écrire la sortie d'impression dans un fichier, le framework d'impression Android appelle la méthode onWrite()
de la classe PrintDocumentAdapter
de votre application. Les paramètres de la méthode spécifient les pages
écrit et le fichier de sortie à utiliser. Votre implémentation de cette méthode doit ensuite afficher chaque
la page de contenu demandée en fichier PDF de plusieurs pages. Une fois ce processus terminé,
Appelez la méthode onWriteFinished()
de l'objet de rappel.
Remarque:Le framework d'impression Android peut appeler la méthode onWrite()
une ou plusieurs fois pour chaque
à onLayout()
. Pour cette raison, il est
il est important de définir le paramètre booléen
Méthode onLayoutFinished()
sur false
lorsque la mise en page du contenu imprimé n'a pas changé
pour éviter les réécritures inutiles du document imprimé.
Remarque:Le paramètre booléen de la méthode
La méthode onLayoutFinished()
indique si le contenu de la mise en page a été modifié ou non.
depuis la dernière demande. La définition de ce paramètre correctement permet au framework d'impression d'éviter
appeler inutilement la méthode onLayout()
,
essentiellement mettre en cache le document imprimé
précédemment écrit et améliorer les performances.
L'exemple suivant illustre les mécanismes de base de ce processus à l'aide de la classe PrintedPdfDocument
pour créer un fichier 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); ... }
Cet exemple délègue le rendu du contenu des pages PDF à drawPage()
qui est abordée dans la section suivante.
Comme pour la mise en page, l'exécution de onWrite()
peut avoir trois résultats: l'achèvement, l'annulation ou l'échec dans le cas où le
le contenu ne peut pas être écrit. Vous devez indiquer l'un de ces résultats en appelant la méthode
la méthode appropriée de l'objet PrintDocumentAdapter.WriteResultCallback
.
Remarque:Le rendu d'un document pour impression peut nécessiter beaucoup de ressources. Dans
pour éviter de bloquer le thread principal de l'interface utilisateur de votre application, vous devez envisager
les opérations de rendu et d'écriture de la page sur un thread distinct, par exemple
dans un AsyncTask
.
Pour en savoir plus sur l'utilisation des threads d'exécution comme les tâches asynchrones,
consultez la section Processus
et les threads.
Dessiner le contenu d'une page PDF
Lors de l'impression, votre candidature doit générer un document PDF et le transmettre à
le framework d'impression Android pour l'impression. Vous pouvez utiliser n'importe quelle bibliothèque de génération de PDF
l'objectif. Cette leçon explique comment utiliser la classe PrintedPdfDocument
.
pour générer des pages PDF à partir de votre contenu.
La classe PrintedPdfDocument
utilise un élément Canvas
.
permettant de dessiner des éléments sur une page PDF, de la même manière que dans une mise en page d'activité. Vous pouvez dessiner
sur la page imprimée à l'aide des méthodes de dessin Canvas
. Les éléments suivants :
exemple de code montre comment dessiner des éléments simples sur une page de document PDF en utilisant ces
méthodes:
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); }
Lorsque vous utilisez Canvas
pour dessiner sur une page PDF, les éléments sont spécifiés dans
soit 1/72 de pouce. Veillez à utiliser cette unité de mesure pour indiquer la taille.
d'éléments
sur la page. Pour positionner les éléments dessinés, le système de coordonnées commence à 0,0
pour l'angle supérieur gauche de la page.
Conseil:Bien que l'objet Canvas
vous permette de placer une impression
sur le bord d'un document PDF, de nombreuses imprimantes ne parviennent pas à imprimer jusqu'au bord d'un
une feuille de papier physique. Assurez-vous de tenir compte des bords non imprimables de la page lorsque
vous créerez un document imprimé avec cette classe.