Galileo Computing :: Java 7 - Mehr als eine Insel - 10 Grafikprogrammierung
Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Neues in Java 7
2 Threads und nebenläufige Programmierung
3 Datenstrukturen und Algorithmen
4 Raum und Zeit
5 Dateien, Verzeichnisse und Dateizugriffe
6 Datenströme
7 Die eXtensible Markup Language (XML)
8 Dateiformate
9 Grafische Oberflächen mit Swing
10 Grafikprogrammierung
11 Netzwerkprogrammierung
12 Verteilte Programmierung mit RMI
13 RESTful und SOAP Web-Services
14 JavaServer Pages und Servlets
15 Applets
16 Datenbankmanagement mit JDBC
17 Technologien für die Infrastruktur
18 Reflection und Annotationen
19 Dynamische Übersetzung und Skriptsprachen
20 Logging und Monitoring
21 Java Native Interface (JNI)
22 Sicherheitskonzepte
23 Dienstprogramme für die Java-Umgebung
Stichwort

Download:
- openbook, ca. 21,3 MB
Buch bestellen
Ihre Meinung?

Spacer
Java 7 - Mehr als eine Insel von Christian Ullenboom
Das Handbuch zu den Java SE-Bibliotheken
Buch: Java 7 - Mehr als eine Insel

Java 7 - Mehr als eine Insel
Galileo Computing
1433 S., 2012, geb.
49,90 Euro, ISBN 978-3-8362-1507-7
Pfeil 10 Grafikprogrammierung
Pfeil 10.1 Grundlegendes zum Zeichnen
Pfeil 10.1.1 Die paint()-Methode für das AWT-Frame
Pfeil 10.1.2 Zeichnen von Inhalten auf ein JFrame
Pfeil 10.1.3 Auffordern zum Neuzeichnen mit repaint()
Pfeil 10.1.4 Java 2D-API
Pfeil 10.2 Einfache Zeichenmethoden
Pfeil 10.2.1 Linien
Pfeil 10.2.2 Rechtecke
Pfeil 10.2.3 Ovale und Kreisbögen
Pfeil 10.2.4 Polygone und Polylines
Pfeil 10.3 Zeichenketten schreiben und Fonts
Pfeil 10.3.1 Zeichenfolgen schreiben
Pfeil 10.3.2 Die Font-Klasse
Pfeil 10.3.3 Einen neuen Font aus einem gegebenen Font ableiten
Pfeil 10.3.4 Zeichensätze des Systems ermitteln *
Pfeil 10.3.5 Neue TrueType-Fonts in Java nutzen
Pfeil 10.3.6 Font-Metadaten durch FontMetrics *
Pfeil 10.4 Geometrische Objekte
Pfeil 10.4.1 Die Schnittstelle Shape
Pfeil 10.4.2 Kreisförmiges
Pfeil 10.4.3 Kurviges *
Pfeil 10.4.4 Area und die konstruktive Flächengeometrie *
Pfeil 10.4.5 Pfade *
Pfeil 10.4.6 Punkt in einer Form, Schnitt von Linien, Abstand Punkt/Linie *
Pfeil 10.5 Das Innere und Äußere einer Form
Pfeil 10.5.1 Farben und die Paint-Schnittstelle
Pfeil 10.5.2 Farben mit der Klasse Color
Pfeil 10.5.3 Die Farben des Systems über SystemColor *
Pfeil 10.5.4 Composite und Xor *
Pfeil 10.5.5 Dicke und Art der Linien von Formen bestimmen über Stroke *
Pfeil 10.6 Bilder
Pfeil 10.6.1 Eine Übersicht über die Bilder-Bibliotheken
Pfeil 10.6.2 Bilder mit ImageIO lesen
Pfeil 10.6.3 Ein Bild zeichnen
Pfeil 10.6.4 Programm-Icon/Fenster-Icon setzen
Pfeil 10.6.5 Splash-Screen *
Pfeil 10.6.6 Bilder im Speicher erzeugen *
Pfeil 10.6.7 Pixel für Pixel auslesen und schreiben *
Pfeil 10.6.8 Bilder skalieren *
Pfeil 10.6.9 Schreiben mit ImageIO
Pfeil 10.6.10 Asynchrones Laden mit getImage() und dem MediaTracker *
Pfeil 10.7 Weitere Eigenschaften von Graphics *
Pfeil 10.7.1 Eine Kopie von Graphics erstellen
Pfeil 10.7.2 Koordinatensystem verschieben
Pfeil 10.7.3 Beschnitt (Clipping)
Pfeil 10.7.4 Zeichenhinweise durch RenderingHints
Pfeil 10.7.5 Transformationen mit einem AffineTransform-Objekt
Pfeil 10.8 Drucken *
Pfeil 10.8.1 Drucken der Inhalte
Pfeil 10.8.2 Bekannte Drucker
Pfeil 10.9 Benutzerinteraktionen automatisieren, Robot und Screenshots *
Pfeil 10.9.1 Der Roboter
Pfeil 10.9.2 Automatisch in die Tasten hauen
Pfeil 10.9.3 Automatisierte Mausoperationen
Pfeil 10.9.4 Methoden zur Zeitsteuerung
Pfeil 10.9.5 Bildschirmabzüge (Screenshots)
Pfeil 10.9.6 Funktionsweise und Beschränkungen
Pfeil 10.9.7 MouseInfo und PointerInfo
Pfeil 10.10 Zum Weiterlesen

Galileo Computing - Zum Seitenanfang

10.7 Weitere Eigenschaften von Graphics *Zur nächsten Überschrift


Galileo Computing - Zum Seitenanfang

10.7.1 Eine Kopie von Graphics erstellenZur nächsten ÜberschriftZur vorigen Überschrift

Das Zeichensystem übergibt an die paintXXX()-Methoden ein Graphics-Objekt, das wir zum Zeichnen oft verändern, etwa um eine Farbe für nachfolgende Operationen zu setzen. Stehen eigene Zeichenmethoden jedoch nicht am Ende der Zeichenfolge, ist es wichtig, den Grafikkontext so zu restaurieren, wie er am Anfang war, um nachfolgende Zeichenoperationen nicht zu beeinflussen. Wichtig ist dies etwa bei Swing-Komponenten, wo paint() der Reihe nach die Methoden

  • protected void paintComponent(Graphics g)
  • protected void paintBorder(Graphics g)
  • protected void paintChildren(Graphics g)

aufruft. Nun lässt sich leicht ausmalen, was passiert, wenn unsere Methode paintComponent() ein verhunztes Graphics-Objekt hinterlässt.

Die erste Lösung ist, sich die alten Zustände zu merken und später das Objekt auf diese zurückzusetzen:

Color oldColor = g.getColor();
g.setColor( newColor );
...
g.setColor( oldColor ); // Farbe zurücksetzen

Bei wenigen Eigenschaften funktioniert das gut, doch werden es mehr, ist es sinnvoller, eine Kopie aller Zustände des Grafikkontextes vorzunehmen. Dazu dient die Methode create(). Von großer Wichtigkeit ist hier, diesen temporären Kontext auf jeden Fall mit dispose() wieder freizugeben, um keinen Speicherplatz zu blockieren:

@Override protected void paintComponent( Graphics g )
Graphics gcopy = g.create(); // Kopie erfragen
try
{
gcopy.draw... // Alle Zeichenoperationen über gcopy
}
finally
{
gcopy.dispose(); // Kopie freigeben
}

Ist definitiv keine Ausnahme zu erwarten, kann der try-Block entfallen; er ist aber ein gutes Sicherheitsnetz.

Beispiel

Es gibt noch eine zweite Notwendigkeit, ein Graphics freizugeben, nämlich dann, wenn es mit getGraphics() von einer Komponente erfragt wurde. Die an Komponenten übergebenen Graphics-Objekte gibt das System frei, denn die Regel lautet immer: Wer Ressourcen anlegt, muss sie auch wieder freigeben.


Galileo Computing - Zum Seitenanfang

10.7.2 Koordinatensystem verschiebenZur nächsten ÜberschriftZur vorigen Überschrift

Standardmäßig liegt der Koordinatenursprung bei (0,0), also in der oberen linken Ecke. Zum Verschieben des Ursprungs an eine neue Position dient die Graphics-Methode translate(int x, int y). Alle nachfolgenden Zeichenoperationen werden dann relativ zu x, y sein, was auch den Vorteil hat, dass negative Koordinaten bei Zeichenmethoden möglich sind. Soll etwa ein Graph in ein Rechteck x = [-10...+10], y = [-10...10] gezeichnet werden, so ist es viel praktischer, den Koordinatenursprung in die Mitte zu legen.

Da Graphics2D vor dem Zeichnen beliebige affine Transformationen anwenden kann, kommen unter Graphics2D noch einige Methoden hinzu:

  • rotate(double theta)
  • rotate(double theta, double x, double y)
  • scale(double sx, double sy)
  • setTransform(AffineTransform Tx)
  • shear(double shx, double shy)
  • transform(AffineTransform Tx)
  • translate(double tx, double ty)

Galileo Computing - Zum Seitenanfang

10.7.3 Beschnitt (Clipping)Zur nächsten ÜberschriftZur vorigen Überschrift

Alle primitiven Zeichenoperationen wirken sich auf den gesamten Bildschirm aus und sind nicht auf bestimmte Bereiche eingeschränkt. Wenn wir Letzteres erreichen wollen, setzen wir einen sogenannten Clipping-Bereich, außerhalb dessen nicht mehr gezeichnet wird. Der Beschnittbereich lässt sich mit zwei Methoden des aktuellen Graphic-Objekts modifizieren beziehungsweise einschränken:

abstract class java.awt.Graphics
  • abstract void setClip(int x, int y, int width, int height)
    Setzt den neuen Beschnittbereich, sodass gezeichnete Zeilen außerhalb des Bereichs nicht sichtbar sind.
  • abstract void clipRect(int x, int y, int width, int height)
    Verkleinert den Beschnittbereich, indem der aktuelle Bereich (getClip()) mit dem übergebenen Rechteck geschnitten wird. Wurde vorher kein Beschnittbereich festgelegt – getClip() lieferte null –, ist clipRect() mit setClip() identisch.

Das folgende Programm setzt den ersten Clipping-Bereich mit setClip(100, 100, 100, 200). Das Füllen eines sehr großen Bereichs wird dann lokal nur im Beschnittbereich ausgeführt. Anschließend verkleinert clipRect(0, 200, 150, 50) den Bereich, doch obwohl x bei 0 beginnt, hat schon setClip() ihn bei 100 gesetzt, sodass die Verbindung der beiden Bereiche (100, 100, 100, 200) geschnitten (0, 200, 150, 50) = (100, 200, 50, 50) ergibt:

Listing 10.24: com/tutego/insel/ui/graphics/ClipDemo.java

package com.tutego.insel.ui.graphics;

import java.awt.*;
import javax.swing.*;

public class ClipDemo extends JPanel
{
private static final long serialVersionUID = 3020598670163407618L;
@Override protected void paintComponent( Graphics g )
{
super.paintComponent( g );

Graphics gcopy = g.create();

// Set first clipping region. clipRect() is also possible.
g.setClip( 100, 100, 100, 200 ); // setClip()

g.setColor( Color.ORANGE );
g.fillRect( 0, 0, getWidth(), getHeight() );
g.setColor( Color.BLACK );
g.drawOval( 150, 100, 100, 100 );

// Second clipping region.
g.clipRect( 0, 200, 150, 50 ); // clipRect()

g.setColor( Color.BLUE );
g.fillRect( 0, 0, 5000, 5000 );

// With gcopy we are working with original settings.

gcopy.setColor( Color.GREEN );
gcopy.fillRect( 50, 50, 20, 50 );

gcopy.dispose();
}

public static void main( String[] args )
{
JFrame f = new JFrame();
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setSize( 400, 400 );
f.add( new ClipDemo() );
f.setVisible( true );
}
}

Abbildung

Abbildung 10.27: Screenshot der Anwendung zeigt Clipping-Bereiche

Alternative Formen

Durch setClip() können alternativ zu den rechteckigen Formen auch beliebige Shape-Objekte die Clipping-Form vorgeben. Die folgende paintComponent()-Methode benutzt als Beschnitt ein Dreieck:

Listing 10.25: com/tutego/insel/ui/graphics/ClipTri.java, Ausschnitt

@Override protected void paintComponent( Graphics g )
{
super.paintComponent( g );

Rectangle r = g.getClipBounds();

System.out.println( r );

Polygon p = new Polygon( // Dreieck
new int[] { 200, 100, 300 },
new int[] { 100, 300, 300},
3 );

g.setClip( p );

g.setColor( Color.ORANGE );
g.fillRect( 0, 0, 500, 500 );
}

Verdeckte Bereiche und schnelles Bildschirm-Erneuern

Clipping-Bereiche sind nicht nur zum Einschränken der Primitiv-Operationen sinnvoll. Bei Bereichsüberdeckungen in Fenstern liefern sie wertvolle Informationen über den neu zu zeichnenden Bereich. Bei einer guten Applikation wird nur der Teil wirklich neu gezeichnet, der auch überdeckt wurde. So lässt sich Rechenzeit sparen.

Beispiel

Informationen über Clipping-Bereiche:

@Override public void paint( Graphics g )
{
Rectangle r = g.getClipBounds();
System.out.println( r );
}

Das Programm erzeugt etwa:

java.awt.Rectangle[x=4,y=23,width=392,height=373]
java.awt.Rectangle[x=104,y=87,width=292,height=309]
java.awt.Rectangle[x=104,y=87,width=286,height=211]
java.awt.Rectangle[x=104,y=87,width=243,height=196]
java.awt.Rectangle[x=104,y=87,width=221,height=219]
java.awt.Rectangle[x=101,y=89,width=221,height=219]
...

Aus den Ausgaben lassen sich verschiedene Fensteroperationen ableiten: Ein fremdes Fenster wurde über das Java-Fenster geschoben, und dann wurde das fremde Fenster verkleinert.

Die Rectangle-Informationen geben Aufschluss über die Größe der neu zu zeichnenden Bereiche. Haben wir schon daran gedacht, die Information in einem Image-Objekt abzulegen, lässt sich wunderbar drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) nutzen. Hier müssen wir die Werte aus dem Rectangle auslesen und in drawImage() übertragen. getClipBounds() liefert ein Rectangle-Objekt, dessen Werte für drawImage() nötig sind. Da jedoch auch beliebige Formen möglich sind, liefert getClip() ein Shape-Objekt. getClipRect() ist die veraltete Methode zu getClipBounds(), sonst aber identisch. Die Methode getClipBounds(Rectangle) – eine der wenigen nicht abstrakten Methoden in Graphics – legt die Informationen im übergebenen Rectangle-Objekt ab, das auch zurückgeliefert wird. Sie ruft nur getClipBounds() auf und überträgt die vier Attribute in das Rechteck.

Im Übrigen: In der paint()-Methode ist es oft klug, mit clipRect() den Bereich einzuschränken, anstatt ihn mit setClip() zu setzen, denn er könnte schon relativ klein sein; setClip() könnte den Beschnittbereich größer machen und unnötige Zeichenoperationen erzwingen.


Galileo Computing - Zum Seitenanfang

10.7.4 Zeichenhinweise durch RenderingHintsZur nächsten ÜberschriftZur vorigen Überschrift

Bisher haben wir stillschweigend eine Zeile eingefügt, die das Antialiasing (eine Art Weichzeichnen) einschaltet. Dadurch erscheinen die Bildpunkte weicher nebeneinander, sind aber etwas dicker, weil in der Nachbarschaft Pixel eingefügt werden.

Beispiel

Es soll alles weichgezeichnet werden:

g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON );

In der Programmzeile nutzen wir die setRenderingHint()-Methode der Klasse Graphics2D. Die Methode nimmt immer einen Schlüssel (daher beginnen die Konstanten mit KEY_XXX) und einen Wert (VALUE_XXX).

abstract class java.awt.Graphics2D
extends Graphics
  • abstract void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
    Setzt eine Eigenschaft des Rendering-Algorithmus.

Im Beispiel setzen wir den Hinweis auf das ANTIALIASING. Da durch das Weichzeichnen mehr Rechenaufwand nötig ist, empfiehlt es sich, für eine schnelle Grafikausgabe auf das Antialiasing zu verzichten. Um dies zu erreichen, übergeben wir den Schlüssel ANTIALIAS_OFF als zweites Argument. Weitere Hinweise sind etwa:

  • KEY_ALPHA_INTERPOLATION, KEY_COLOR_RENDERING, KEY_DITHERING, KEY_FRACTIONALMETRICS, KEY_INTERPOLATION, KEY_RENDERING, KEY_TEXT_ANTIALIASING

Mit dem RENDERING-Schlüssel können wir zum Beispiel die Geschwindigkeit bestimmen, die direkt mit der Qualität der Ausgabe korreliert.


Galileo Computing - Zum Seitenanfang

10.7.5 Transformationen mit einem AffineTransform-ObjektZur nächsten ÜberschriftZur vorigen Überschrift

Eine affine Transformation eines Objekts ist entweder eine Translation (Verschiebung), Rotation, Skalierung oder Scherung[85](Ein Objekt wird geschert, wenn es entlang einer Koordinatenachse verzogen wird. Im Zweidimensionalen gibt es zwei Scherungsarten: entlang der x-Achse und entlang der y-Achse.). Bei diesen Transformationen bleiben parallele Linien nach der Transformation auch parallel. Um diese Operationen durchzuführen, existiert eine Klasse AffineTransform. Transformationen kommen an unterschiedlichen Stellen in der Grafik-Bibliothek zum Einsatz, unter anderem hier:

  • Einem Graphics2D-Kontext weist setTransform() eine Transformation zu. Jede anschließend mit drawXXX() oder fillXXX() dargestellte Form wird vor dem Zeichnen über die Transformationsangaben angepasst.
  • Zum Zeichnen von Grafiken kann bei drawImage() ein AffineTransform-Objekt übergeben werden; die Transformation gilt dann ausschließlich für das Bild.
  • Bei der Methode deriveFont() bestimmt ein übergebenes AffineTransform-Objekt die Eigenschaften eines neuen Fonts.

Translation, Rotation, Skalierung oder Scherung

Die zweidimensionalen Objekte können durch die Operationen Translation, Rotation, Skalierung oder Scherung verändert werden. Diese Operationen sind durch eine 3×3-Matrix gekennzeichnet. Die Klasse AffineTransform bietet nun Methoden an, damit wir diese Matrix selbst erzeugen können, sowie Hilfsmethoden, die uns die Arbeit abnehmen:

AffineTransform trans = new AffineTransform();
trans.rotate
( 0.1 );
g2.setTransform( trans );
g2.fill( new Rectangle2D.Float( 150, 100, 60, 60 ) );

Konstruktoren der Klasse AffineTransform

Die Klasse AffineTransform besitzt sechs Konstruktoren: zunächst einen Standard-Konstruktor und einen Konstruktor mit einem schon vorhandenen AffineTransform-Objekt, dann jeweils einen Konstruktor für eine Matrix mit dem Datentyp float und mit dem Datentyp double sowie zwei Konstruktoren mit allen sechs Werten der Matrix für float und double. Eine eigene Matrix ist nur dann sinnvoll, wenn wir mehrere Operationen hintereinander ausführen lassen wollen. So nutzen wir in der Regel den Standard-Konstruktor wie oben und ändern die Form durch die Methoden rotate(), scale(), shear() oder translate(). Wird nach dem Erzeugen des AffineTransform-Objekts direkt eine der Methoden aufgerufen, geht dies auch einfacher über die statischen Erzeugungsmethoden getRotateInstance(), getScaledInstance(), getShearInstance() und getTranslateInstance(). Sie füllen dann die Matrix mit den passenden Einträgen. Ein Transformationsobjekt kann mit setToIdentity() wieder initialisiert werden, sodass AffineTransform wieder verwendbar ist.

Abbildung

Abbildung 10.28: UML-Diagramm für die AffineTransform



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.







<< zurück
  Zum Katalog
Zum Katalog: Java 7 – Mehr als eine Insel
Java 7 – Mehr als eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java und XML






 Java und XML


Zum Katalog: Einstieg in Eclipse 3.7






 Einstieg in
 Eclipse 3.7


Zum Katalog: Android 3






 Android 3


Zum Katalog: NetBeans Platform 7






 NetBeans Platform 7


Zum Katalog: Java ist auch eine Insel






 Java ist
 auch eine Insel


Zum Katalog: Apps entwickeln für Android 4






 Apps entwickeln
 für Android 4


Zum Katalog: Java 7






 Java 7


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2012
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de