Per alcune applicazioni, come le app di disegno, le app di impaginazione e altre app incentrate su l'output grafico, la creazione di stupende pagine stampate è una caratteristica fondamentale. In questo caso, non è sufficiente per stampare un'immagine o un documento HTML. L'output di stampa per questi tipi di applicazioni richiede controllo preciso di tutto ciò che compare in una pagina, inclusi caratteri, flusso del testo, interruzioni di pagina intestazioni, piè di pagina ed elementi grafici.
La creazione di output di stampa completamente personalizzati per la tua applicazione richiede investimenti nella programmazione rispetto agli approcci discussi in precedenza. Devi creare componenti che comunicare con il framework di stampa, adattare le impostazioni della stampante, disegnare elementi di pagina e gestire la stampa su più pagine.
Questa lezione mostra come collegare il gestore di stampa, creare un adattatore per la stampa e creare contenuti per la stampa.
Collegati a Gestione stampa
Quando la tua applicazione gestisce direttamente il processo di stampa, il primo passaggio dopo aver ricevuto un
una richiesta di stampa da parte dell'utente è quella di connettersi al framework di stampa Android per ottenere un'istanza
della classe PrintManager
. Questa classe consente di inizializzare un processo di stampa
e iniziare il ciclo di vita della stampa. Il seguente esempio di codice mostra come recuperare il gestore di stampa
e avviare il processo di stampa.
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); // }
Il codice di esempio riportato sopra mostra come assegnare un nome a un processo di stampa e impostare un'istanza della classe PrintDocumentAdapter
che gestisce i passaggi del ciclo di vita di stampa. La
Nella sezione successiva verrà trattata l'implementazione della classe dell'adattatore di stampa.
Nota: l'ultimo parametro della colonna print()
prende un oggetto PrintAttributes
. Puoi utilizzare questo parametro per
forniscono suggerimenti sul framework di stampa e sulle opzioni preimpostate in base al ciclo di stampa precedente,
migliorando così l'esperienza utente. Puoi utilizzare questo parametro anche per impostare opzioni che
più appropriato per i contenuti da stampare, ad esempio impostando l'orientamento su orizzontale
quando stampi una foto con quell'orientamento.
Crea un adattatore di stampa
Un adattatore di stampa interagisce con il framework di stampa di Android e gestisce i passaggi del processo di stampa. Questo processo richiede che gli utenti selezionino stampanti e opzioni di stampa prima della creazione un documento da stampare. Queste selezioni possono influenzare l'output finale quando l'utente sceglie stampanti con capacità di output, dimensioni di pagina o orientamenti diversi. Man mano che vengono effettuate queste selezioni, il framework di stampa chiede all'adattatore di creare un layout e generare e stamparlo, in preparazione all'output finale. Quando un utente tocca il pulsante Stampa, il framework prende il documento finale stampato e lo passa a un fornitore di stampa per l'output. Durante la stampa di stampa, gli utenti possono scegliere di annullare l'azione di stampa, quindi l'adattatore di stampa deve rilevare e reagire a una richiesta di annullamento.
La classe astratta PrintDocumentAdapter
è progettata per gestire
ciclo di vita della stampa, che ha quattro metodi principali di callback. Devi implementare questi metodi
nell'adattatore di stampa per interagire correttamente con il framework di stampa:
onStart()
- Ricevuto una sola chiamata al l'inizio del processo di stampa. Se la tua applicazione prevede attività di preparazione una tantum per eseguire, ad esempio ottenere un'istantanea dei dati da stampare, eseguirle qui. Implementazione questo metodo nell'adattatore non è obbligatorio.onLayout()
- Richiamato ogni volta l'utente cambia un'impostazione di stampa che influisce sull'output, ad esempio dimensioni della pagina diverse, o l'orientamento della pagina, offrendo all'applicazione l'opportunità di calcolare il layout del pagine da stampare. Questo metodo deve restituire come minimo il numero di pagine previste nel documento stampato.onWrite()
- Chiamata per eseguire il rendering stampato pagine in un file da stampare. Questo metodo può essere chiamato una o più volte dopo ogni ChiamataonLayout()
.onFinish()
- Chiamata una volta alla fine del processo di stampa. Se l'applicazione deve eseguire attività di rimozione una tantum, eseguili qui. Non è necessario implementare questo metodo nell'adattatore.
Le seguenti sezioni descrivono come implementare i metodi di layout e scrittura, che sono fondamentale per il funzionamento di un adattatore di stampa.
Nota:questi metodi dell'adattatore vengono richiamati sul thread principale della tua applicazione. Se
prevedi che l'esecuzione di questi metodi nella tua implementazione richieda una quantità significativa di
nel tempo, implementarle in modo che vengano eseguite in un thread separato. Ad esempio, puoi incapsulare
creare layout o stampare documenti di scrittura in oggetti AsyncTask
separati.
Informazioni su documenti stampati Compute
Nell'ambito di un'implementazione della classe PrintDocumentAdapter
,
l'applicazione deve essere in grado di specificare il tipo di documento che sta creando e calcolare il totale
numero di pagine per il processo di stampa, fornite informazioni sulle dimensioni della pagina stampata.
L'implementazione del metodo onLayout()
in
l'adattatore esegue questi calcoli e fornisce informazioni sull'output previsto
un processo di stampa in una classe PrintDocumentInfo
, che include il numero di pagine e
tipo di contenuti. Il seguente esempio di codice mostra un'implementazione di base del metodo onLayout()
per 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'esecuzione del metodo onLayout()
può
avere tre risultati: completamento, annullamento o fallimento, nel caso in cui il calcolo
Impossibile completare il layout. Devi indicare uno di questi risultati richiamando il
dell'oggetto PrintDocumentAdapter.LayoutResultCallback
.
Nota: il parametro booleano del
onLayoutFinished()
indica se i contenuti del layout sono stati effettivamente modificati o meno
dall'ultima richiesta. L'impostazione corretta di questo parametro consente al framework di stampa di evitare
chiamando inutilmente il metodo onWrite()
,
memorizzando nella cache il documento di stampa scritto in precedenza e migliorando le prestazioni.
Il lavoro principale di onLayout()
è
calcolare il numero di pagine previste come output in base agli attributi della stampante.
Il modo in cui viene calcolato questo numero dipende molto da come la tua applicazione presenta le pagine per:
stampa. Il codice di esempio riportato di seguito mostra un'implementazione in cui il numero di pagine è
determinato dall'orientamento di stampa:
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); }
Scrivi un file di documento di stampa
Quando è il momento di scrivere l'output di stampa in un file, il framework di stampa di Android chiama il metodo onWrite()
della classe PrintDocumentAdapter
dell'applicazione. I parametri del metodo specificano le pagine da
e il file di output da utilizzare. L'implementazione di questo metodo deve quindi visualizzare
pagina di contenuti richiesta in un file di documento PDF di più pagine. Al termine della procedura,
chiama il metodo onWriteFinished()
dell'oggetto callback.
Nota: il framework di stampa di Android potrebbe chiamare il metodo onWrite()
una o più volte per ogni
chiamata a onLayout()
. Per questo motivo,
è importante impostare il parametro booleano
onLayoutFinished()
al metodo false
quando il layout dei contenuti di stampa non è cambiato,
per evitare inutili riscritture del documento cartaceo.
Nota: il parametro booleano del
onLayoutFinished()
indica se i contenuti del layout sono stati effettivamente modificati o meno
dall'ultima richiesta. L'impostazione corretta di questo parametro consente al framework di stampa di evitare
chiamando inutilmente il metodo onLayout()
,
memorizzando nella cache il documento di stampa scritto in precedenza e migliorando le prestazioni.
L'esempio seguente illustra i meccanismi di base di questo processo utilizzando la classe PrintedPdfDocument
per creare un 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); ... }
Questo esempio delega il rendering dei contenuti delle pagine PDF a drawPage()
di cui parleremo nella prossima sezione.
Come per il layout, l'esecuzione di onWrite()
può avere tre risultati: completamento, annullamento o fallimento nel caso in cui
non possono essere scritti. Devi indicare uno di questi risultati richiamando il metodo
metodo appropriato dell'oggetto PrintDocumentAdapter.WriteResultCallback
.
Nota:il rendering di un documento per la stampa può essere un'operazione che richiede molte risorse. Nella
per evitare di bloccare il thread dell'interfaccia utente principale della tua applicazione, dovresti prendere in considerazione
eseguire le operazioni di rendering e scrittura della pagina su un thread separato, ad esempio
in un AsyncTask
.
Per ulteriori informazioni sull'utilizzo di thread di esecuzione come le attività asincrone,
consulta Processi
e Thread.
Disegno dei contenuti della pagina PDF
Quando l'applicazione viene stampata, devi generare un documento PDF e passarlo a
il framework di stampa Android. Per questo, puoi utilizzare qualsiasi libreria di generazione PDF
che non ha uno scopo specifico. Questa lezione mostra come utilizzare il corso PrintedPdfDocument
per generare pagine PDF dai tuoi contenuti.
Il corso PrintedPdfDocument
utilizza un Canvas
per disegnare elementi su una pagina PDF, come un disegno su un layout di attività. Puoi disegnare
elementi sulla pagina stampata utilizzando i metodi di disegno Canvas
. Le seguenti
il codice di esempio mostra come disegnare alcuni semplici elementi sulla pagina di un documento PDF utilizzando questi
metodo:
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); }
Quando utilizzi Canvas
per disegnare su una pagina PDF, gli elementi vengono specificati in
che è 1/72 di pollice. Assicurati di utilizzare questa unità di misura per specificare la dimensione
di elementi della pagina. Per il posizionamento degli elementi disegnati, il sistema di coordinate inizia da 0,0
per l'angolo in alto a sinistra della pagina.
Suggerimento: l'oggetto Canvas
ti consente di posizionare la stampa
elementi sul bordo di un documento PDF, molte stampanti non sono in grado di stampare sul bordo di un
foglio di carta fisica. Assicurati di tenere conto dei bordi non stampabili della pagina quando
puoi creare un documento cartaceo con questo corso.