Java 2D

Bei der Java 2D API handelt es sich um eine Menge von Klassen für die zweidimensionale Grafikverarbeitung, welche wiederum Teilmenge der Java-Klassenbibliothek der Java Foundation Classes des Java Development Kit ist. Mit Hilfe dieser vordefinierten Objekte lassen sich mit relativ geringem Aufwand anspruchsvolle 2D-Grafiken erzeugen. Nachfolgend befindet sich eine Auflistung der Pakete, deren Klassen die Java 2D API bilden.

Beteiligte Klasse

Die Java 2D-API besteht aus folgenden Paketen:

Grundlagen

Was kann man damit machen?

Die folgende Liste zeigt, welche Elemente die Java 2D-API dem Programmierer zur Verfügung stellt:

Das Koordinatensystem

Es existieren in Java 2D zwei von einander unterschiedliche Koordinatensysteme. Die Koordinatensysteme brauchen nicht übereinzustimmen, die nötigen Umrechungen übernimmt das System und brauchen nicht vom Benutzer vorgenommen werden.

Zeichenfläche (Fenster)

Bevor wir mit der Java 2D-API zweidimensionale Grafiken zeichnen können, müssen wir als erstes ein Ausgabefenster (Frame) erzeugen. Darum müssen wir uns als erstes mit Fenstern beschäftigen, bevor wir auf die Java 2D-API eingehen können. Aus einfachhalberkeit werden wir uns aber nur mit Swing-Applikationen beschäftigen und nicht mit AWT-Applikationen oder mit Applets.

Swing-Fenster darstellen

Um unter Swing ein Fenster öffnen zu können, müssen wir zunächst das Paket javax.swing.* einbinden. Dann können wir eine Klasse JFrame und deren Methoden nutzen.

import javax.swing.JFrame;

public class Java2D
{
  public static void main(String args[])
  {
    // Neues Fenster erzeugen
    JFrame f = new JFrame("Ein Fenster");
    // Fenstergroesse angeben
    f.setSize(300, 200);
    // Damit das Fenster geschlossen werden kann
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // Fenster anzeigen
    f.setVisible(true);
  }
}


Von JFrame ableiten

Wir können unsere neue Klasse auch direkt von JFrame ableiten. Dann ist es uns gestattet, die Funktionen der Klassen direkt aufzurufen, zum Beispiel setSize(). Im Hauptprogramm erzeugen wir über den eigenen Konstruktor dann das Fenster, denn der eigene Konstruktor ruft ja über die super()-Funktion automatisch den Konstruktor der Oberklasse, also von JFrame auf.

import javax.swing.JFrame;

public class Java2D extends JFrame
{
  // Konstruktor
  public Java2D(String p_Titel)
  {
    // Konstruktor der Oberklasse aufrufen
    super(p_Titel);
    // Fenstergroesse angeben
    this.setSize(300, 200);
    // Damit das Fenster geschlossen werden kann
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // Fenster anzeigen
    this.setVisible(true);
  }

  public static void main(String args[])
  {
    // Objekt der abgeleiteten Klasse erzeugen
    Java2D f = new Java2D("Ein Fenster");
  }
}

Die paint()-Methode

Um zweidimensionale Grafiken zeichnen zu können, müssen wir die Funktion paint() der Klasse Frame, JFrame, JApplet oder Applet überschreiben und dort alles einsetzen, was gezeichnet werden soll, etwa Linien, Texte oder gefüllte Polygone. Der gewünschte Inhalt wird immer dann gezeichnet, wenn das Fenster neu aufgebaut oder wenn von außen repaint() aufgerufen wird. In genau diesem Fall wird das Grafiksystem paint() aufrufen und das Zeichnen anstoßen.

import javax.swing.JFrame;

public class Java2D extends JFrame
{
  // Konstruktor
  public Java2D(String p_Titel)
  {
    // Konstruktor der Oberklasse aufrufen
    super(p_Titel);
    // Fenstergroesse angeben
    this.setSize(300, 200);
    // Damit das Fenster geschlossen werden kann
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // Fenster anzeigen
    this.setVisible(true);
  }

  // Überschreiben der Methode paint()
  public void paint(Graphics g)
  {
    // Zeichnen von zweidimensonalen Grafiken
  }

  public static void main(String args[])
  {
    // Objekt der abgeleiteten Klasse erzeugen
    Java2D f = new Java2D("Ein Fenster");
  }
}

Zeichnen mit JFrame (Die paintComponent()-Methode)

Grundsätzlich ließe sich auch von JFrame einen Unterklasse bilden und paint() überschreiben, aber das ist nicht der übliche Weg. Stattdessen wählen wir einen anderen Ansatz: Wir bilden eine eigene Komponente, eine Unterklasse von JPanel und setzten diese auf das Fenster. Wenn nun das Fenster neu gezeichnet wird, wird auch die Komponente JPanel neu gezeichnet und ruft die paint()-Funktion auf. Allerdings überschreiben Unterklassen von Swing im Regelfall paint() nicht, sondern eine andere Funktion: paintComponent(). Das liegt daran, dass Swing in paint() zum Beispiel noch Rahmen zeichnet und sich um eine Pufferung des Bildschirminhaltes zur Optimierung kümmert. So ruft paint() die drei Funktionen paintComponent(), paintBorder() und paintChildren() auf und bei Wiederdarstellung kümmert sich ein RepaintManager um eine zügige Darstellung mit Hilfe der gepufferten Inhalte, was bei normalen Swing-Interaktionskomponenten wie Schaltflächen wichtig ist.

paintComponent() besitzt ist in der Oberklasse die Sichtbarkeit protected, was wir beibehalten sollten. Die Funktion wird nicht von außen aufgerufen und daher muss eine Unterklasse die Sichtbarkeit nicht zu public erweitern. Der Aufruf von super.paintComponent() ist immer dann angebracht, wenn die Oberklasse ihre Inhalte zeichnen soll. Bei vollständig eigenem Inhalt ist das nicht nötig.

class Java2D extends JPanel
{
  // Überschreiben der Methode paintComponent()
  protected void paintComponent(Graphics g)
  {
    // Konstruktor der Oberklasse aufrufen
    super.paintComponent(g);
    // Zeichnen von zweidimensonalen Grafiken
  }

  public static void main(String args[])
  {
    // Neues Fenster erzeugen
    JFrame f = new JFrame("Ein Fenster");
    // Fenstergroesse angeben
    f.setSize(300, 200);
    // Damit das Fenster geschlossen werden kann
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // Neue Komponente zum Fenster hinzufuegen
    f.add(new Java2D());
    // Fenster anzeigen
    f.setVisible(true);
  }
}

Die Klasse Graphics

Ein spezieller Parameter wird in der paint()-Methode übergeben – der Grafikkontext, ein Objekt vom Typ Graphics. Die Klasse Graphics, besitzt verschiedene Methoden zum Zeichnen, etwa von Linien, Kreisen, Ovalen, Rechtecken, Zeichenfolgen oder Bildern. Das grafische System übergibt unserem Programm über paint() ein gültiges Graphics-Objekt, und wir können auf diese Weise auf der grafischen Oberfläche zeichnen. Dies funktioniert auch dann, wenn die Zeichenfläche nicht direkt sichtbar ist, wie bei Hintergrundgrafiken. Wir können nur in der paint()-Methode auf das Graphics-Objekt zugreifen. Diese wiederum wird immer dann aufgerufen, wenn die Komponente neu gezeichnet werden muss.

Die Klasse Graphics2D

Wenn nun die grafische Oberfläche Objekte zeichnet, wird für alle Komponenten die paint()-Methode mit dem passenden Grafikkontext aufgerufen. Unter Java 2D wurde dies um einen neuen Grafikkontext mit der Klasse Graphics2D erweitert. Um die erweiterte Funktionalität zu nutzen, muss das bekannte Graphics-Objekt in ein Graphics2D-Objekt gecastet werden. Da Graphics2D eine Unterklasse von Graphics ist, lassen sich natürlich noch alle AWT-Operationen weiterverwenden.

public void paint(Graphics g)
{
  Graphics2D g2 = (Graphics2D) g;
  ...
}

Geometrische Formen

Linien

Rechtecke

Ovale und Kreisbögen

Polygone und Polylines

Die Polygon-Klasse

Neben der Möglichkeit, die Linienzüge durch Koordinatenfelder zu beschreiben, gibt es in Java die Polygon-Klasse Polygon. Sie ist eine Erweiterung des Interfaces Shape. Sie ist aber minimal, lediglich die Methode getBounds() wird implementiert. Ein Polygon-Objekt verwaltet seine Koordinaten eigenständig, und von außen können wir Elemente hinzunehmen. Mit der mächtigen Methode contains() können wir herausfinden, ob ein Punkt in der von dem Polygon ausgezeichneten Fläche liegt. Doch zunächst müssen wir ein Polygon-Objekt erzeugen. Dazu dienen zwei Konstruktoren:

Nun können wir Punkte hinzufügen und Anfragen an das Polygon-Objekt stellen:

Das erzeugte Polygon können wir mit speziellen Methoden, natürlich aus Graphics, zeichnen.

Text

Für die Textausgabe gibt es in der Klasse Graphics verschiedene Methoden.

Zeichensatz

Die Methoden drawString, drwawChars und drawBytes verwenden immer den aktuellen Zeichensatz.

Um den Zeichensatz umändern zu können, müssen wir zuerst ein Font-Objekt von der Klasse Font erzeugen.

Font f = new Font(name, style, size);

Danach können wir mit der Methode void setFont(Font f) von der Klasse Graphics unseren neuen erzeugten Font setzen.

public void paint(Graphics g)
{
  Graphics2D g2 = (Graphics2D) g;

  Font f = new Font("Serif",Font.BOLD, 20);
  g2.setFont(f);

  g2.drawString("Hallo Java", 10, 10);
}

Um den aktuell verwendeten Font abzurufen, können wir die Methode Font getFont() verwenden.

public void paint(Graphics g)
{
  Graphics2D g2 = (Graphics2D) g;

  System.out.println(g2.getFont());

}


Die Font-Klasse

Die FontMetrics-Klasse

Bilder

Farben

Filter

Drucken