Kapitel 1 Drucken von Swing

Werbung
Kapitel 1
Drucken von
Swing-Komponenten.
Wir wollen eine einfache Methode zum Drucken einer Swing-Komponente vorstellen und daran den Mechanismus der Druckereinbindung in JAVA studieren. Anschließen wird eine generische Klasse entwickelt, mit der man beliebige
Komponenten drucken kann. Wir verwenden dazu Klassen aus der Bibliothek
java.awt.print, die man so einbindet.
import java.awt.print.*;
Unser Beispiel Programm soll einen Frame mit einem Menü anzeigen. Das
Menü hat nur einen einzigen Unterpunkt, nämlich Drucken“. In den Frame
”
kleben wir ein Panel, in das ein Rechteck, ein ausgefülltes Oval und ein Text
geschrieben werden. Um sehen zu können, ob bei der Druckerausgabe auch
das ganze Panel druckt wird, zeichnen wir noch ein Rechteck ganz am Rand
des Panels. Beim Anklicken des Menüpunktes Drucken“ soll nur das Panel,
”
nicht der ganze Rahmen gedruckt werden.
Die Bibliothek java.awt.print stellt sehr viele Klassen zur individuellen Eirichtung von Druckeranwendungen beschreit. Wir beschreiben hier eine einfache, aber oft ausreichende Möglichkeit zum Drucken von Dokumenten. Spezielleres findet man in der Dokumentation des JDK-Paketes oder in ´den
Tutorials auf den JAVA-Seiten der Firma SUN.
1
2
1.1
KAPITEL 1. DRUCKEN VON SWING-KOMPONENTEN.
Prinzipielles Vorgehen
Um eine Swing-Komponente (hier ein Panel) druckbar zu machen, muss man
für sie das Interface Printable implementieren. Dazu ist die Implementierung
der Methode print ausreichend. Anschließend muss man einen so genannten
PrinterJob definieren, ihm die Komponente übergeben und ihn starten. Man
kann zusätzlich einen Druckerauswahldialog anzeigen lassen, in dem man den
Drucker, die zu druckenden Seiten und gegebenenfalls noch weiter Optionen
festlegen kann. Um die Aufbereitung der Daten und den Transfer zum Drucker
muss sich der Benutzer nicht kümmern. Eingebettete Unter-Komponenten
einer druckbaren Komponenten werden mit gedruckt.
1.2
Das Interface Printable
Das Interface Printable verlangt die Implementation einer einzigen Methode
public int print(Graphics graphics,
throws PrinterException
PageFormat pageFormat, int pageIndex)
Diese Methode wird vom Laufzeitsystem aufgerufen, wenn gedruckt werden
soll. Das Laufzeitsystem erzeugt auch die notwendigen Parameter graphics,
pageFormat und pageIndex. Die Bedeutung dieser Parameter ist die folgende:
graphics Dieses Objekt stellt die Verbindung zu den betriebssystemabhängigen Druckerroutinen (Druckertreibern) her. Es hat die gleiche Aufgabe beim Drucken wie das Graphics-Objekt, das der paintComponentMethode übergeben bei Zeichen übergeben wird und das die Verbindung
zu den Zeichenroutienen herstellt.
pageFormat Dieses Objekt vom Typ FageFormat wird vom Laufzeitsystem
erstellt. Es enthält Informationen über das von gewählten Drucker verwendete Seitenformat. Man kann ihn die Papiergröße, den bedruckbaren Bereich des Papiers und weitere Parameter mit geeigneten getMethoden entnehmen.
pageIndex Das Laufzeitsystem ruft die Methode print immer wieder mit
neuen Werten von pageIndex auf. Diese Werte sind 0, 1, 2, 3, . . .. Liefert die Methode print die Konstante PAGE EXISTS (definiert im Interface Printable) zurück, so wird tatsächlich gedruckt und anschließend
1.2. DAS INTERFACE PRINTABLE
3
print mit dem nächsten Wert für pageIndex aufgerufen. Liefert die Methode print die Konstante NO SUCH PAGE zurück, so wird nicht gedruckt
und print auch nicht erneut aufgerufen. Mit diesem Mechanismus ist
es möglich, mehrseitige Dokumente zu drucken.
Kommen wir nun zu einer einfachen, aber für unsere Zwecke ausreichenden
Implementierung der des Interfaces Printabel, das heißt der Methode print.
Zunächst fragen schauen wir, ob der übergebenen Wert von pageIndex gleich
Null ist. Unser Panel ist ein einseitiges Dokument“, daher geben wir den
”
Wert NO SUCH PAGE zurück, wenn pageIndex größer oder gleich 1 ist. Nur bei
ersten Aufruf von print (dann ist pageIndex gleich Null) wird gedruckt.
Das Drucken selbst geschieht durch einen Aufruf der paint-Methode des Panels. Man übergibt paint dazu den Graphics-Parameter, den die Methode
print erhalten hat. Dadurch zeichnet“ paint nicht auf den Bildschirm son”
dern auf den Drucker. Das Grobgerüst der Implementation von print sieht
dann so aus.
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0)
{
return(NO_SUCH_PAGE);
}
else
{
this.paint(g);
return(PAGE_EXISTS);
}
}
Es gibt allerdings einige technisch bedingte Vorkehrung, die man für einen
problemlosen Ausdruck noch treffen sollte. Zunächst sollte man vor dem
Drucken die so genannte Doppelpufferung der Grafik ausschalten und anschließend wieder einschalten. Diese sorgt für eine schöne Bildschirm Darstellung beim Neuzeichnen einer Komponente, ist beim Drucken aber störend.
Die folgenden Zeilen zeigen, wie man den paint Befehl einrahmt, um die
Doppelpufferung entsprechend zu steuern.
RepaintManager currentManager = RepaintManager.currentManager(this);
currentManager.setDoubleBufferingEnabled(false);
paint(g);
4
KAPITEL 1. DRUCKEN VON SWING-KOMPONENTEN.
currentManager.setDoubleBufferingEnabled(true);
Ein weiterer Punkt betrifft die Positionierung und Skalierung des Druckbildes.
Der Nullpunkt des Druck-Koordinatensystems ist meistens die linke obere
Ecke des Papiers. Da der Drucker aber einen Rand des Papiers aus technischen
Gründen nicht bedrucken kann hat der liegt die linke obere Ecke des Druckbereichs tiefer und weiter rechts. Das Druck-Koordinatensystems steht wie
das von Swing auf dem Kopf. Die das Graphics-Objekt der print-Methode
identifiziert die beiden Nullpunkte von Grafik und Papier, was dazu führt das
je ein Streifen links und oben in der Grafik in den Rand des Papiers fällt und
nicht gedruckt wird. Abhilfe verschafft eine Verschiebung (Translation) des
Druck-Koordinatensystems. Der Befehl
g.translate(int xNeu, int yNeu)
des verschiebt den Nullpunkt des Koordinatensystems des Graphics-Objektes
g auf den Punkt (xneu , yneu ) (in den Koordinaten vor der Verschiebung).
Dies hat denselben Effekt als würde man zu allen x- beziehungsweise yKoordinaten der Grafik den Wert xneu beziehungsweise yneu addieren. Gilt
xneu > 0 und yneu > 0, so wird die Grafik nach rechts unten verschoben. Da
wir diese Verschiebung auf das Graphics-Objektes des Druckes anwenden,
ändert sich an der Bildschirmdarstellung nichts. Es bleibt noch zu klären, um
wie viele wir Verschieben müssen. Hierzu entnehmen wir dem PageFormatObjekt die Daten über den druckbaren Bereich. Die Befehle
double pageFormat.getImageableX()
double pageFormat.getImageableY()
liefern die x- beziehungsweise y-Koordinaten der linken oberen Ecke des
druckbaren Bereichs. Zur Sicherheit sollte man die Werte aufrunden bevor
man das Koordinatensystem verschiebt. Insgesamt ergibt sich so die folgende
Implementierung des Interfaces Printable
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0)
{
return(NO_SUCH_PAGE);
}
else
1.3. DIE KLASSE PRINTERJOB
5
{
int x = (int)pageFormat.getImageableX() + 1;
int y = (int)pageFormat.getImageableY() + 1;
g.translate(x,y);
RepaintManager currentManager = RepaintManager.currentManager(this);
currentManager.setDoubleBufferingEnabled(false);
this.paint(g);
currentManager.setDoubleBufferingEnabled(true);
return(PAGE_EXISTS);
}
}
1.3
Die Klasse PrinterJob
Die Klasse PrinterJob realisiert in JAVA einen Druckauftrag. Diese Klasse
hat zwar einen Konstruktor,dieser wird aber nicht verwendet. Stattdessen
benutzt man die Methode getPrinterJob. Einen Druckauftrag erzeugt man
also so:
PrinterJob printJob = PrinterJob.getPrinterJob();
Um das Drucken auszulösen ruft man die Methode print für das PrinterJobObjekt auf.
printJob.print();
Diese Methode wirft eine PrinterException und gehört daher in einen trycatch-Block. Man muss dem Druckauftrag mitteilen, welche Komponente
zu gedruckt werden soll, genauer: Man muss ihm mitteilen welches druckbare Objekt (Printable) zu drucken ist. Dies geschieht mit der Methode
setPrintable wie folgt
setPrintable(Printable pintableObject)
Erwähnenswert ist noch die Methode
boolean printDialog();
!
6
KAPITEL 1. DRUCKEN VON SWING-KOMPONENTEN.
Sie zeigt einen Auswahldialog an, der etwa wie in Abbildung 1.1 aussieht.
In ihm kann man für den Druckauftrag einige Optionen auswählen. Dazu
gehören: der Drucker, die zu druckenden Seiten und die Anzahl der Exemplare. Die Methode liefert beim Schließen einen Booleschen Wert zurück. Ist
dieser true, so wurde im Dialog der Druckauftrag bestätigt, ist er false so
wurde der Druckauftrag abgebrochen. Das folgende Programmfragment zeigt
die Anwendung der Klasse PrinterJob.
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(pp);
if (printJob.printDialog())
try {
printJob.print();
} catch(PrinterException pex) {
pex.printStackTrace();
}
Abbildung 1.1: Ein Druckerdialog.
1.4. EINE BEISPIELANWENDUNG
1.4
7
Eine Beispielanwendung
Im folgenden sind drei JAVA-Dateien angegeben, die eine Druckanwendung
darstellen. Die Klasse PrintPanel ist von JPanel abgeleitet. In ein solches Panel werden ein Rechteck, eine gefülltes Oval und ein Text gezeichnet. Zusätzlich wird ein Rechteck am Rand des Panels gezeichnet, um die Umrisse desselben im Ausdruck sichtbar zu machen. Ein PrintPanel ist druckbar, das heißt
es implementiert das Interface Printable wie in Abschnitt 1.2 beschrieben
durch Realisierung der Methode print;
Datei: PrintPanel.java
package eis.Printing;
import java.awt.*;
import javax.swing.*;
import java.awt.print.*;
public class PrintPanel extends JPanel implements Printable {
public PrintPanel() {
setBackground(Color.white);
setPreferredSize(new Dimension(300, 200));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
g.drawRect(20,20,100,50);
g.fillOval(80,80,60,30);
g.drawString("Drucken aus Swing ist einfach",100,150);
g.setColor(Color.red);
g.drawRect(0,0,299,199);
}
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0) {
return(NO_SUCH_PAGE);
} else {
int x = (int)pageFormat.getImageableX() + 1;
int y = (int)pageFormat.getImageableY() + 1;
g.translate(x,y);
RepaintManager currentManager = RepaintManager.currentManager(this);
8
KAPITEL 1. DRUCKEN VON SWING-KOMPONENTEN.
currentManager.setDoubleBufferingEnabled(false);
this.paint(g);
currentManager.setDoubleBufferingEnabled(true);
return(PAGE_EXISTS);
}
}
}
Die Klasse PrintFrame erzeugt einen Rahmen in dessen Content-Pane zentral
eine PrintPanel eingebettet wird. Der Rahmen hat ein Menü, dessen einziger Menüpunkt Drucken“ den Druckvorgang auslöst. Dazu wird ein Druck”
auftrag (PrinterJob) erzeugt, dem man das Panel übergibt. Dann wird ein
Druckdialog angezeigt und bei Bestätigung des Druckauftrags im Dialog wird
der Druckvorgang gestartet.
Datei: PrintFrame.java
package eis.Printing;
import
import
import
import
import
java.awt.*;
javax.swing.*;
java.awt.event.*;
java.awt.print.*;
eis.SimpleFrame.SimpleFrame;
public class PrintFrame extends SimpleFrame
implements ActionListener{
private PrintPanel pp;
public PrintFrame() {
this.setTitle("Drucken aus Swing");
pp = new PrintPanel();
this.getContentPane().add(pp,"Center");
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Datei");
JMenuItem druckItem = new JMenuItem("Drucken");
menuBar.add(menu);
menu.add(druckItem);
druckItem.addActionListener(this);
this.setJMenuBar(menuBar);
1.5. EINE GENERISCHE KLASSE ZUM DRUCKEN
9
pack();
}
public void actionPerformed(ActionEvent evt){
String command = evt.getActionCommand();
if(command.equals("Drucken"))
{
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(pp);
if (printJob.printDialog())
try {
printJob.print();
} catch(PrinterException pe) {
System.out.println("Error printing: " + pe);
}
}
}
}
Die Klasse PrintTest ist die Startklasse.
Datei: PrintTest.java
package eis.Printing;
public class PrintTest
{
public static void main(String[] args)
{
PrintFrame prfr = new PrintFrame();
prfr.showIt();
}
}
1.5
Eine generische Klasse zum Drucken
Oft möchte man viele verschiedene Komponenten einer umfangreichen Anwendung druckbar machen. Dazu muss man für jede von Ihnen das Interface
10
KAPITEL 1. DRUCKEN VON SWING-KOMPONENTEN.
Printable implementieren. Will man zum Beispiel eine JTextArea drucken,
so muss man zunächst eine Klasse von JTextArea ableiten, die Printable
implementiert, aber keine neuen Funktionen zu JTextArea hinzufügt. Um
diesen Aufwand zu vermeiden definieren die Klasse PrintSuit. Sie stellt die
Methode
public static void printComponent(Component comp)
Ihr übergibt man eine (nicht notwendigerweise druckbare) grafische Komponente comp und diese wird dann gedruckt. Der unten angegeben Code vereinigt die Implementation des Interfaces Printable und die Erzeugung der
PrinterJob-Objekts.
Datei: PrintSuit.java
package eis.Printing;
import
import
import
import
java.awt.*;
javax.swing.*;
java.awt.print.*;
java.awt.Graphics2D;
public class PrintSuit implements Printable {
private Component compToPrint;
public static void printComponent(Component comp) {
new PrintSuit(comp).print();
}
private PrintSuit(Component comp) {
this.compToPrint = comp;
}
public void print() {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(this);
if (printJob.printDialog())
try {
printJob.print();
} catch(PrinterException pex) {
pex.printStackTrace();
}
1.5. EINE GENERISCHE KLASSE ZUM DRUCKEN
11
}
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if (pageIndex > 0) {
return(NO_SUCH_PAGE);
} else {
int x = (int)pageFormat.getImageableX() + 1;
int y = (int)pageFormat.getImageableY() + 1;
g.translate(x,y);
RepaintManager currentManager = RepaintManager.currentManager(compToPrint);
currentManager.setDoubleBufferingEnabled(false);
compToPrint.paint(g);
currentManager.setDoubleBufferingEnabled(true);
return(PAGE_EXISTS);
}
}
}
Das Programm PrintSuitTestFrame demonstriert die Verwendung von PrintSuit.
Wir drucken den Code hier nicht ab, das Programm lässt sich aber von der
Web-Seite herunterladen. Gedruckt wird nur das eingebettete Knopf, nicht
der ganze Frame.
Herunterladen