Benutzerdefinierte Dokumente drucken

Einige Anwendungen wie Apps zum Zeichnen, Seitenlayout-Apps und andere Apps, die sich auf grafische Ausgabe. Das Erstellen ansprechender gedruckter Seiten ist eine wichtige Funktion. In diesem Fall reicht es nicht aus, um ein Bild oder HTML-Dokument zu drucken. Die Druckausgabe für diese Arten von Anwendungen muss Kontrolle über alles, was auf einer Seite erscheint, einschließlich Schriftarten, Textfluss, Seitenumbrüchen Kopf-, Fußzeilen und Grafikelementen.

Das Erstellen von Druckausgaben, die vollständig an Ihre Anwendung angepasst sind, erfordert mehr als die zuvor erläuterten Ansätze. Sie müssen Komponenten erstellen, Kommunikation mit dem Print-Framework, Anpassen der Druckereinstellungen, Zeichnen von Seitenelementen mehrere Seiten zu drucken.

In dieser Lektion erfahren Sie, wie Sie eine Verbindung zum Druckmanager herstellen, einen Druckadapter erstellen und Druckinhalte erstellen können.

Wenn Ihre Anwendung den Druckvorgang direkt verwaltet, ist der erste Schritt nach Erhalt eines Druckanfrage von Ihrem Nutzer besteht darin, eine Verbindung zum Android Print Framework herzustellen und eine Instanz der Klasse PrintManager. Mit dieser Klasse können Sie einen Druckauftrag initialisieren und beginnen den Druckvorgang. Das folgende Codebeispiel zeigt, wie Sie den Druckmanager abrufen. und starten Sie den Druckvorgang.

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); //
}

Der Beispielcode oben zeigt, wie ein Druckauftrag benannt und eine Instanz der PrintDocumentAdapter-Klasse festgelegt wird, die die Schritte des Drucklebenszyklus verarbeitet. Die Implementierung der Klasse der Druckadapter wird im nächsten Abschnitt erläutert.

Hinweis: Der letzte Parameter im Feld print() verwendet ein PrintAttributes-Objekt. Mit diesem Parameter können Sie bieten Hinweise auf das Druckframework und voreingestellte Optionen basierend auf dem vorherigen Druckzyklus, und so die User Experience verbessert. Sie können mit diesem Parameter auch Optionen festlegen, besser zum Druckinhalt passen, indem Sie z. B. die Ausrichtung auf Querformat festlegen wenn Sie ein Foto in dieser Ausrichtung drucken.

Ein Druckadapter interagiert mit dem Android Print-Framework und verarbeitet die Schritte der Druckverfahren. Bei diesem Vorgang müssen Nutzer vor dem Erstellen Drucker und Druckoptionen auswählen ein Dokument zum Drucken. Diese Auswahl kann sich auf die endgültige Ausgabe auswirken, wenn die Nutzenden sie auswählen. Drucker mit unterschiedlichen Ausgabefunktionen, unterschiedlichen Seitengrößen oder Seitenausrichtungen. Während dieser Auswahl fordert das Print-Framework Ihren Adapter auf, ein Layout zu erstellen und ein Druckdokument zur Vorbereitung auf die endgültige Ausgabe. Sobald Nutzende auf die Schaltfläche „Drucken“ tippen, nimmt das fertige Druckdokument und übergibt es zur Ausgabe an einen Anbieter für Druck. Während des Druckens kann der Nutzer die Druckaktion abbrechen. Daher muss Ihr Druckadapter auch und auf Kündigungsanfragen reagieren.

Die abstrakte Klasse PrintDocumentAdapter dient der Verarbeitung von mit vier Callback-Methoden. Sie müssen diese Methoden implementieren in Ihrem Druckeradapter verwenden, damit Sie ordnungsgemäß mit dem Print-Framework interagieren können:

  • onStart(): 1 Aufruf am des Druckvorgangs beginnen. Wenn Ihre Anwendung einmalige Vorbereitungsaufgaben umfasst, ausgeführt werden sollen, z. B. um einen Snapshot der zu druckenden Daten zu erhalten, führen Sie sie hier aus. Implementieren Diese Methode in Ihrem Adapter ist nicht erforderlich.
  • onLayout() – Wird jedes Mal aufgerufen, wenn ein der Nutzer eine Druckeinstellung ändert, die sich auf die Ausgabe auswirkt, z. B. eine andere Seitengröße, oder Seitenausrichtung. So kann Ihre Anwendung das Layout der Seite zu druckenden Seiten. Diese Methode muss mindestens zurückgeben, wie viele Seiten erwartet werden im gedruckten Dokument.
  • onWrite() – Zum Rendern gedruckt aufgerufen Seiten in eine zu druckende Datei. Diese Methode kann nach jeder Methode einmal oder mehrmals aufgerufen werden. onLayout() Anruf.
  • onFinish() – Wird am Ende einmal aufgerufen des Druckvorgangs. Wenn für Ihre Anwendung einmalige Löschaufgaben ausgeführt werden müssen, führen Sie sie hier aus. Die Implementierung dieser Methode in Ihrem Adapter ist nicht erforderlich.

In den folgenden Abschnitten wird beschrieben, wie die Layout- und Schreibmethoden implementiert werden. entscheidend für die Funktion eines Printadapters ist.

Hinweis:Diese Adaptermethoden werden im Hauptthread Ihrer Anwendung aufgerufen. Wenn erwarten Sie, dass die Ausführung dieser Methoden in Ihrer Implementierung erheblich implementieren Sie sie so, dass sie in einem separaten Thread ausgeführt werden. Sie können beispielsweise die das Schreiben von Dokumenten in separaten AsyncTask-Objekten.

Informationen zu Druckdokumenten berechnen

Innerhalb einer Implementierung der PrintDocumentAdapter-Klasse enthält Ihr Anwendung muss den erstellten Dokumenttyp angeben und die Gesamtsumme berechnen können. Anzahl der Seiten für den Druckauftrag unter Angabe der Größe der gedruckten Seite. Die Implementierung der Methode onLayout() in führt der Adapter diese Berechnungen durch und liefert Informationen zur erwarteten Ausgabe des die Anzahl der Druckaufträge in einem PrintDocumentInfo-Kurs, einschließlich der Anzahl der Seiten und Inhaltstyp auswählen. Das folgende Codebeispiel zeigt eine einfache Implementierung der onLayout()-Methode für ein PrintDocumentAdapter-Element:

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.");
    }
}

Die Ausführung der Methode onLayout() kann drei Ergebnisse haben: Abschluss, Stornierung oder Misserfolg, falls die Berechnung des Layout nicht fertiggestellt werden kann. Sie müssen eines dieser Ergebnisse angeben, indem Sie das entsprechende des PrintDocumentAdapter.LayoutResultCallback-Objekts.

Hinweis:Der boolesche Parameter der Die Methode onLayoutFinished() gibt an, ob sich der Layoutinhalt tatsächlich geändert hat. seit der letzten Anfrage. Wenn Sie diesen Parameter korrekt festlegen, kann das Print-Framework die Methode onWrite() unnötigerweise aufrufen, Das zuvor geschriebene Druckdokument wird im Prinzip im Cache gespeichert, um die Leistung zu verbessern.

Die Hauptaufgabe von onLayout() ist Berechnung der Anzahl der Seiten, die aufgrund der Druckerattribute als Ausgabe erwartet werden. Wie Sie diese Zahl berechnen, hängt stark davon ab, wie die Seiten Ihrer App Drucken. Das folgende Codebeispiel zeigt eine Implementierung, bei der die Anzahl der Seiten Druckausrichtung festgelegt:

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);
}

Druckdokumentdatei schreiben

Wenn es an der Zeit ist, die Druckausgabe in eine Datei zu schreiben, ruft das Android Print Framework die Methode onWrite() der PrintDocumentAdapter-Klasse Ihrer App auf. Die Parameter der Methode legen fest, welche Seiten und die zu verwendende Ausgabedatei. Ihre Implementierung dieser Methode muss dann jedes die angeforderte Inhaltsseite in eine mehrseitige PDF-Dokumentdatei umwandeln. Nach Abschluss dieses Vorgangs onWriteFinished()-Methode des Callback-Objekts aufrufen

Hinweis: Das Android Print-Framework kann die onWrite()-Methode einmal oder mehrmals für jeden onLayout(). Aus diesem Grund ist es wichtig, den booleschen Parameter onLayoutFinished() auf false, wenn das Layout der Druckinhalte nicht geändert wurde um unnötiges Umschreiben des Druckdokuments zu vermeiden.

Hinweis:Der boolesche Parameter der Die Methode onLayoutFinished() gibt an, ob sich der Layoutinhalt tatsächlich geändert hat. seit der letzten Anfrage. Wenn Sie diesen Parameter korrekt festlegen, kann das Print-Framework die Methode onLayout() unnötigerweise aufrufen, Das zuvor geschriebene Druckdokument wird im Prinzip im Cache gespeichert, um die Leistung zu verbessern.

Im folgenden Beispiel werden die grundlegenden Mechanismen dieses Prozesses anhand der Klasse PrintedPdfDocument zum Erstellen einer PDF-Datei veranschaulicht:

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);

    ...
}

In diesem Beispiel wird das Rendern von PDF-Seiteninhalten an drawPage() delegiert. , die im nächsten Abschnitt erläutert wird.

Wie beim Layout wird die Ausführung von onWrite() kann drei Ergebnisse haben: Abschluss, Abbruch oder Fehler, falls der kann der Inhalt nicht geschrieben werden. Sie müssen eines dieser Ergebnisse angeben, indem Sie die Methode Methode des PrintDocumentAdapter.WriteResultCallback-Objekts.

Hinweis:Das Rendern eines Dokuments zum Drucken kann ein ressourcenintensiver Vorgang sein. In Damit der Hauptthread der Benutzeroberfläche Ihrer Anwendung nicht blockiert wird, sollten Sie das Rendern der Seite und die Schreibvorgänge in einem separaten Thread ausführen, z. B. in einem AsyncTask. Weitere Informationen zur Arbeit mit Ausführungs-Threads wie asynchronen Aufgaben Siehe Prozesse und Threads.

Zeichnung (PDF) Seiteninhalt

Wenn Ihre Anwendung gedruckt wird, muss sie ein PDF-Dokument generieren und an das Druck-Framework von Android. Sie können dafür eine beliebige Bibliothek zur PDF-Erstellung verwenden zu verstehen. In dieser Lektion erfahren Sie, wie Sie den PrintedPdfDocument-Kurs verwenden. um aus Ihren Inhalten PDF-Seiten zu generieren.

Für die Klasse PrintedPdfDocument wird ein Canvas verwendet. -Objekt, um Elemente auf einer PDF-Seite zu zeichnen, ähnlich wie beim Zeichnen in einem Aktivitätslayout. Sie können zeichnen, Elemente auf der gedruckten Seite mithilfe der Canvas-Zeichnungsmethoden. Die folgenden Beispielcode zeigt, wie Sie mit diesen Funktionen einfache Elemente auf einer PDF-Dokumentseite zeichnen. Methoden:

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);
}

Wenn Sie mit Canvas auf einer PDF-Seite zeichnen, werden Elemente in was 1/72 Zoll entspricht. Achten Sie darauf, diese Maßeinheit für die Angabe der Größe zu verwenden. von Elementen auf der Seite. Für die Positionierung gezeichneter Elemente beginnt das Koordinatensystem bei 0,0. für die obere linke Ecke der Seite.

Tipp:Mit dem Canvas-Objekt können Sie zwar drucken, Elemente am Rand eines PDF-Dokuments können viele Drucker nicht normales Blatt Papier. Achten Sie darauf, dass Sie die nicht druckbaren Ränder der Seite berücksichtigen, wenn Sie erstellen Sie in diesem Kurs ein Druckdokument.