Das Java Codebook - *ISBN 3-8273-2059

Werbung
Multimedia
Core
I/O
84
Wie kann ich einfache Strukturen zeichnen?
Seit Java2 (JDK 1.2) steht das Java2D-API zur Verfügung. Mit dieser Einführung
sind viele erweiterte Grafikmöglichkeiten zur Verfügung gestellt worden, obgleich
die Kompatibilität zu den früheren Funktionen gewahrt wurde. Es stellt viele Funktionen zum Zeichnen von grundlegenden, geometrischen Figuren zur Verfügung.
Dazu gehören Linien, Rechtecke, Kreise bzw. Ellipsen, Kreisbögen und Polygone.
Die entsprechenden Klassen finden sich im Paket java.awt.geom. Sie sind alle Unterklassen der Klasse java.awt.Shape. Damit ist es möglich, sie alle auf die gleiche Art
und Weise in den Zeichenmethoden der Klasse Graphics2D zu behandeln.
In der Klasse SimpleDraw werden einige grundlegende Figuren gezeichnet, die mit
dem Java2D-API sehr einfach zu erstellen sind.
GUI
Multimedia
Datenbank
Netzwerk
XML
RegEx
Daten
Threads
WebServer
Applets
Abbildung 67: Grundlegende Zeichenfunktionen
package javacodebook.media.draw.simple;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class SimpleDraw extends JPanel{
//In Swing immer die Methode paintComponent überschreiben
public void paintComponent(Graphics graphics) {
Listing 139: SimpleDraw
Sonstiges
330
Multimedia
super.paintComponent(graphics);
//Graphics-Objekt ist in Wahrheit ein Graphics2D-Objekt
Graphics2D g = (Graphics2D) graphics;
//Aktuelle Zeichenfarbe setzen
g.setColor(Color.black);
//Eine Linie zeichnen
g.draw(new Line2D.Double(0,100,319,100));
//Ein Rechteck zeichnen
g.draw(new Rectangle2D.Double(10, 10, 80, 60));
//Einen Kreis gefüllt zeichnen
g.draw(new Ellipse2D.Double(130,10,60,60));
//Eine Ellipse mit Farbverlauf gefüllt zeichnen
g.draw(new Ellipse2D.Double(230, 10, 80, 60));
//Ein Rechteck mit abgerundeten Ecken zeichnen
g.draw(new RoundRectangle2D.Double(10, 110, 80, 60, 15, 15));
//Einen Kreisbogen zeichnen
g.draw(new Arc2D.Double(120, 110, 80, 70, 90, 135, Arc2D.OPEN));
//Ein Tortenstück zeichnen
g.draw(new Arc2D.Double(240, 110, 80, 80, 90, 45, Arc2D.PIE));
}
//Größe des Panels festlegen
public Dimension getPreferredSize() {
return new Dimension(320, 200);
}
//Frame erzeugen Panel anzeigen
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(new SimpleDraw(), BorderLayout.CENTER);
f.pack();
f.show();
}
}
Listing 139: SimpleDraw (Forts.)
85
Wie zeichne ich verschiedene Rahmen?
Wenn Sie keinen Standard-Rahmen um eine geometrische Figur zeichnen wollen,
sondern beispielsweise einen gestrichelten Rahmen, oder einen Rahmen in einer
anderen Strichstärke verwenden wollen, so können Sie die Methode setStroke() der
Klasse Graphics2D verwenden. In Verbindung mit der Klasse java.awt.BasicStroke
Wie zeichne ich verschiedene Rahmen?
331
lassen sich sehr viele Einstellungen für den zu zeichnenden Rahmen vornehmen. Mit
ihrer Hilfe können Strichstärke, Linienenden, Linienverbindungen und unterbrochene Linien erzeugt werden. Sie stellt einige Konstanten bereit, über die das Ende
und die Verbindung von Linien definiert werden können. Die Werte CAP_BUTT,
CAP_ROUND und CAP_SQUARE bestimmen den Stil, in dem ein Linienende gezeichnet
wird (gerade, abgerundet oder mit geradem Anhang).
Die Werte JOIN_BEVEL, JOIN_MITER und JOIN_ROUND legen fest, wie das Zusammentreffen von zwei Linienenden behandelt wird. Es kann ohne Effekt (BEVEL), mit spitzem
Ende (MITER) oder abgerundet (ROUND) gezeichnet werden.
Die Klasse BasicStroke bietet verschiedene Konstruktoren an, um die gewünschten
Effekte zu erzielen. Die meisten sind sehr einfach zu benutzen. Der Konstruktor für
das Erzeugen von gestrichelten Linien ist etwas komplexer. Er hat die Form public
BasicStroke(float width, int cap, int join, float miterlimit, float[] dash,
float dash_phase). Die Parameter width, cap, join sind leicht erkennbar (Strichstärke und die oben genannten Stile). Miterlimit beschreibt, wie lang zwei Linien
am Ende verbunden werden. Das wird wichtig, wenn zwei Linien in sehr spitzem
Winkel aufeinander treffen. Wird eine Spitze gezeichnet, so kann diese sehr lang
werden. Dies wird durch das miterlimit begrenzt (würde die Spitze länger als der
Wert miterlimit, wird stattdessen mit der BEVEL-Funktion gezeichnet). Die Parameter dash und dash_phase beschreiben gestrichelte Linien. Im Array dash werden die
Längen der einzelnen Strichabschnitte angegeben. Dabei wird alternierend zwischen
gezeichnetem und nicht gezeichnetem Strich gewechselt. Ein Array in der Form
{5,5} macht dasselbe wie {5}, da abwechselnd 5 Punkte gezeichnet werden und die
nächsten 5 nicht. Der Wert dash_phase dient als Offset für das Zeichnen der Strichelung, d.h. die ersten x Punkte werden übersprungen.
Die Klasse StrokeExamples zeigt, wie die verschiedenen Rahmenarten und Linienenden gezeichnet werden.
package javacodebook.media.draw.stroke;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class StrokeExamples extends JPanel{
public void paintComponent(Graphics graphics) {
Listing 140: StrokeExamples
Core
I/O
GUI
Multimedia
Datenbank
Netzwerk
XML
RegEx
Daten
Threads
WebServer
Applets
Sonstiges
332
Multimedia
super.paintComponent(graphics);
//Graphics-Objekt ist in Wahrheit ein Graphics2D-Objekt
Graphics2D g = (Graphics2D) graphics;
//aktuelle Zeichenfarbe setzen
g.setColor(Color.black);
//Ein Rechteck mit Rahmendicke 5 zeichnen
BasicStroke fatBorder = new BasicStroke(5.0f);
g.setStroke(fatBorder);
g.draw(new Rectangle2D.Double(10, 10, 80, 60));
//Ein Rechteck mit gestricheltem Rahmen zeichnen
BasicStroke stroke =
new BasicStroke(1.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL,
1.0f, new float[] {5.0f},
0.0f);
g.setStroke(stroke);
g.draw(new RoundRectangle2D.Double(110, 10, 80, 60,
15, 15));
//Rahmen mit verschiedenen Strichlängen zeichnen
stroke = new BasicStroke(1.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL,
1.0f, new float[] {5.0f, 5.0f,
2.0f, 5.0f},
0.0f);
g.setStroke(stroke);
g.draw(new RoundRectangle2D.Double(210, 10, 80, 60, 15, 15));
//Linien überschneiden sich am Ende ohne Effekt
stroke = new BasicStroke(10.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL);
g.setStroke(stroke);
int x = 25;
g.draw(new Line2D.Double(x, 160, x+25, 135));
g.draw(new Line2D.Double(x+25, 135, x+50, 160));
//Linien überschneiden sich am Ende, Spitze wird gezeichnet
stroke = new BasicStroke(10.0f,
BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER);
g.setStroke(stroke);
x = 125;
g.draw(new Line2D.Double(x, 160, x+25, 135));
g.draw(new Line2D.Double(x+25, 135, x+50, 160));
//Linien überschneiden sich am Ende, Spitze wird abgerundet
Listing 140: StrokeExamples (Forts.)
Wie zeichne ich verschiedene Rahmen?
stroke = new BasicStroke(10.0f,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
g.setStroke(stroke);
x = 225;
g.draw(new Line2D.Double(x, 160, x+25, 135));
g.draw(new Line2D.Double(x+25, 135, x+50, 160));
333
Core
I/O
GUI
}
/** Die gewünschte Größe des Panels festlegen */
public Dimension getPreferredSize() {
return new Dimension(300, 180);
}
/** Einen Frame erzeugen und das Panel anzeigen */
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(new StrokeExamples(),
BorderLayout.CENTER);
f.pack();
f.show();
}
Multimedia
Datenbank
Netzwerk
XML
RegEx
Daten
}
Threads
Listing 140: StrokeExamples (Forts.)
Und so sieht das Ergebnis aus.
WebServer
Applets
Sonstiges
Abbildung 68: Verschiedene Möglichkeiten, Rahmen zu zeichnen
334
Multimedia
Für die Definition eigener Rahmen muss das Interface java.awt.Stroke implementiert werden, dann können selbst definierte Rahmen innerhalb der Zeichenfunktionen der Graphics2D-Klasse verwendet werden. Der mit Hilfe von setStroke()
festgelegte Zeichenstrich kann auf alle Zeichenobjekte angewendet werden, die eine
Unterklasse von java.awt.Shape sind.
86
Wie kann ich etwas mit Farbverläufen füllen?
Ein Farbverlauf wird mit Hilfe der Klasse java.awt.GradientPaint definiert. Er verläuft von einem Startpunkt hin zu einem Endpunkt, die beide in Koordinatenform
angegeben werden. Eine Ausgangs- und eine Endfarbe müssen angegeben werden,
dazwischen wird entlang einer Geraden zwischen Start- und Endpunkt ein linearer
Farbverlauf berechnet. Mit der Methode fill(Shape shape) aus der Klasse Graphics2D
wird der Farbverlauf gezeichnet. Auch zyklisches Füllen ist möglich.
Die Klasse GradientPaint hat zwei Konstruktoren, die es in zwei Variationen gibt,
einmal mit einzelnen Koordinaten und einmal mit Point2D-Objekten als Parameter,
um die Endpunkte der Farbverläufe festzulegen. Wir beschränken uns hier auf die
Variante mit einzelnen Koordinaten:
public GradientPaint(float x1, float y1, Color color1,
float x2, float y2, Color color2);
public GradientPaint(float x1, float y1, Color color1,
float x2, float y2, Color color2,
boolean cyclic);
Mit der ersten Variante wird ein einfacher, azyklischer Farbverlauf vom Punkt x1,y1
hin zu Punkt x2, y2 erzeugt. Die zweite Variante ermöglicht sowohl einen azyklischen als auch einen zyklischen Farbverlauf. Mit dem Parameter cyclic kann angegeben werden, ob der Farbverlauf zyklisch wiederholt werden soll. Er wird allerdings
nur dann zyklisch verlaufen, wenn der angegebene Endpunkt {x2,y2} nicht auch der
Endpunkt des zu füllenden Shapes ist. Wird der Endpunkt {x2, y2} z.B. in der Mitte
des zu füllenden Shapes angegeben, so wird der Farbverlauf einmal vom Rand bis
zur Mitte von Farbe 1 zu Farbe 2 verlaufen und dann von der Mitte zum anderen
Rand von Farbe 2 zu Farbe 1.
Das vorherige Bild zeigt die Möglichkeiten, die sich mit GradientPaint ergeben.
Wie kann ich etwas mit Farbverläufen füllen?
335
Core
I/O
GUI
Multimedia
Abbildung 69: Füllen mit Farbverläufen
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
Graphics2D g = (Graphics2D) graphics;
float startx = 10, starty = 10;
float width=80, height=60;
//Farbverlauf definieren und ein gefülltes Rechteck zeichnen
//Farbverlauf von links (schwarz) nach rechts (weiß) linear
GradientPaint gradient =
new GradientPaint(startx, starty, Color.black,
startx + width, starty, Color.white);
g.setPaint(gradient);
g.fill(new Rectangle2D.Double(startx, starty, width,
height));
startx = 110;
//Farbverlauf diagonal von links oben nach rechts unten
gradient =
new GradientPaint(startx, starty, Color.black,
startx + width, starty + height,
Color.white);
g.setPaint(gradient);
g.fill(new Rectangle2D.Double(startx, starty, width,
height));
startx = 20;
starty = 110;
//Zyklischer Farbverlauf mit Zentrum in der Mitte
gradient =
new GradientPaint(startx, starty, Color.black,
startx + width, starty, Color.white, true);
Datenbank
Netzwerk
XML
RegEx
Daten
Threads
WebServer
Applets
Sonstiges
336
Multimedia
g.setPaint(gradient);
g.fill(new Rectangle2D.Double(startx, starty, width*2,
height));
}
87
Wie kann ich eine Grafik laden und anzeigen?
Eine Grafik wird in Java von der Klasse java.awt.Image repräsentiert. Java unterstützt von sich aus die Formate GIF und JPEG. Sie haben mehrere Möglichkeiten,
ein Bild zu laden. Innerhalb eines Applets kann die Methode getImage() verwendet
werden, in einer Anwendung die Methode getImage() der Klasse java.awt.Toolkit.
Dabei kann letztere nur über das Default-Toolkit, das von der Java Runtime bereitgestellt wird, verwendet werden. Das Default-Toolkit wird von der Klasse Toolkit
über die Methode getDefaultToolkit() geliefert. Somit sieht ein entsprechender
Aufruf in einer Anwendung so aus:
Image image = Toolkit.getDefaultToolkit().getImage(dateiname);
Die Methode getImage() kehrt sofort zurück und das Laden der Grafik erfolgt im
Hintergrund. Dies hat zur Folge, dass ein Bild u.U. noch nicht vollständig geladen
ist, wenn es verwendet werden soll. Die Kontrolle über den Ladevorgang können Sie
durch den Einsatz eines MediaTrackers (java.awt.MediaTracker) behalten. Im Kapitel
über Applets wird dies gezeigt.
Wenn Sie das Bild sofort verwenden wollen, nehmen Sie die Klasse javax.swing.
ImageIcon. Sie verwendet intern einen MediaTracker und erspart so diverse Codezeilen. Der Konstruktor erhält als Parameter eine Datei oder URL, von der das Bild
geladen wird. Mit der Methode getImage() kann das Bild anschließend genutzt werden.
Um das Bild anzuzeigen, kann eine entsprechende Swing-Komponente verwendet
werden, die ImageIcons unterstützt (z.B. ein JLabel). Es ist jedoch auch sehr einfach,
eine eigene Komponente zu schreiben, die Grafiken anzeigt. Eine eigene Komponente kann oft flexibler innerhalb von GUI-Anwendungen eingesetzt werden. Die
Klasse ImagePanel ist als Komponente realisiert, die von JPanel erbt. Damit kann sie
an beliebiger Stelle verwendet werden, z.B. innerhalb einer JScrollPane. Im Konstruktor wird die Grafik übergeben. Das ImagePanel nimmt danach die Größe der
Grafik als seine optimale Größe an.
Wie kann ich eine Grafik laden und anzeigen?
337
package javacodebook.media.graphic.load;
import java.awt.*;
import javax.swing.JPanel;
Core
I/O
public class ImagePanel extends javax.swing.JPanel {
//Die Grafik, die angezeigt werden soll
private Image image;
public ImagePanel(Image image) {
this.image = image;
}
//Grafik auf das Panel zeichnen
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0,
image.getWidth(this),
image.getHeight(this), this);
}
GUI
Multimedia
Datenbank
Netzwerk
XML
RegEx
//Größe der Grafik als PreferredSize zurückgegeben
public Dimension getPreferredSize() {
return new Dimension(image.getWidth(this),
image.getHeight(this));
}
}
Daten
Threads
Listing 141: ImagePanel
WebServer
Damit lässt sich ein einfacher Bildbetrachter erstellen. Die Klasse ImageViewer stellt
eine einfache Möglichkeit dar, Bilder zu laden und anzuzeigen. Dabei wird der
Frame jeweils der Größe der anzuzeigenden Grafik angepasst.
Applets
package javacodebook.media.graphic.load;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
public class ImageViewer extends JFrame {
//das ImagePanel
ImagePanel imagePanel = null;
Listing 142: ImageViewer
Sonstiges
338
public ImageViewer() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new BorderLayout());
//Einen Button zum Öffnen von Dateien erstellen
JPanel buttonPanel = new JPanel();
getContentPane().add(buttonPanel, BorderLayout.NORTH);
JButton openButton = new JButton("Datei öffnen");
buttonPanel.add(openButton);
//Einen ActionListener auf den Button legen, der einen
//FileChooser öffnet
openButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
JFileChooser chooser = new JFileChooser();
int status = chooser.showOpenDialog(ImageViewer.this);
if (status == JFileChooser.APPROVE_OPTION) {
//ausgewählte Datei ermitteln und abspielen
File file = chooser.getSelectedFile();
try {
ImageIcon icon = new ImageIcon(file.getAbsolutePath());
//vorher vorhandenes ImagePanel entfernen
if(imagePanel != null)
getContentPane().remove(imagePanel);
//das ImagePanel anzeigen
imagePanel = new ImagePanel(icon.getImage());
getContentPane().add(imagePanel, BorderLayout.CENTER);
//Frame auf die Bildgröße anpassen
pack();
} catch(Exception e) {
e.printStackTrace(System.out);
}
}
}
});
}
public static void main(String[] args) {
ImageViewer viewer = new ImageViewer();
viewer.pack();
viewer.show();
}
}
Listing 142: ImageViewer (Forts.)
Multimedia
Herunterladen