Inhaltsverzeichnis

Java Tutorial #4 - "Problemlösung beim Zeichnen in ein Swing-Fenster"

Autor: 'Kriz'

Wichtiger Hinweis!!!

Die Grundlagen über die Java-Programmierung ist nicht Bestandteil der Tutorialreihe! Zum Lernen von Java empfehle ich das hervorragende Online-Tutorial von Guido Krüger, welches man sich kostenlos von www.javabuch.de runterladen kann.


In diesem Tutorial geht es um das Problem vieler Anfänger, daß das direkte Zeichnen in ein Swing-Fenster mittels der Methode paint() anscheinend nicht funktioniert. Besitzer langsamerer PCs werden dabei feststellen, daß Java für einen Bruchteil der Sekunde das gewünschte Zeichnungsrepertoire ausführt, aber sofort wieder überzeichnet mit der aktuellen Fensterhintergrundfarbe der ContentPane.

Zuerst einmal (wieder) etwas graue Theorie:

Anders als bei regulären Java-Fenstern, die mit dem nativen Abstract Windowing Toolkit (AWT) erzeugt und dargestellt werden, arbeiten Java Foundation Classed (JFC) erzeugte Fenster (bekannter unter dem Namen Swing Technology) mit mehreren Ebenen, die das aktuelle Fenster überlagern (siehe auch Online-Tutorial von Guido Krüger). Einer dieser Ebenen, die für das eigentliche, primäre Darstellen des Fensterinhalts ist, schimpft sich ContentPane (Inhaltsebene). Da Swing einem anbietet, den gesamten Fensterinhalt unabhängig von der darunterligenden GUI zu zeichnen (AWT-Fenster benutzen die darunterliegende GUI, was Swing nicht macht), überlagern alle Swing-basierten Fenster die Methode paint(), um primär die Fensterkomponenten zu zeichnen. Erst dann „erlaubt“ Java dem Programmierer, eigene Ideen direkt ins Fenster bzw. auf die ContentPane zu zeichnen oder auszugeben.

Im Prinzip verknüpft sich das zugrundeliegende Problem perfekt mit der Objektorientierung von Java. Da die Methode paint() von jeder JComponent Instanz geerbt wird, muß man sie in seiner eigenen Fensterklasse nur passend überlagern. Die Überlagerung ist so simpel, daß dies hier auch nur ein kleines Minitutorial wird ;-)

Schauen wir uns also an, was wir machen müssen, um ein Swing-Fenster zum Zeichnen unserer eigenen Ideen zu bewegen:

Das Codegerüst

Im Prinzip geht es hier nur um die Methode paint(), die wir überlagern müssen in unserer eigenen, abgeleiteten Fensterklasse. Dazu erzeugen wir eine eigene Klasse MyJFrame zu Demonstrationszwecken, die eine Linie von links oben nach rechts unten zeichnen soll:

// MyJFrame.java
 
/* Eventuell hier erst das Paket definieren, falls erforderlich! */
 
import java.awt.*;
import javax.swing.*;
 
public class MyJFrame
extends JFrame
{
    public MyJFrame()
    {
        super("MyJFrame Testfenster");
 
        setLocation(0, 0);
        setSize(200, 200);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
 
        setVisible(true);
    }
 
    public void paint(Graphics g)
    {
        super.paint(g);
 
        g.drawLine(0, 0, getSize().width, getSize().height);
    }
}

Das Testfenster wird von der Oberklasse JFrame abgeleitet. Swing-Komponenten haben übrigens immer ein großes J vor dem Klassenbezeichner. Damit kann man sie leichter von den normalen AWT-Komponenten unterscheiden. Im Konstruktor unserer Testklasse übergeben wir dem übergeordneten Konstruktor der eingebetteten Klasse JFrame den Fenstertiteltext „MyJFrame Testfenster“. Dazu rufen wir einfach nur das Schlüsselwort super() auf (der eigentlich ein Zeiger auf das nächste, übergeordnete Subobjekt der Klasse darstellt…) mit dem String als Parameter.

Danach legen wir mit den Methoden setLocation() die linke obere Startposition des Fenster fest (in diesem Falle das Koordinatenpaar [0|0]), mit setSize() die Größenangaben des Fensters (200*200 Pixel) und mit der Methode setDefaultCloseOperation() das Verhalten des Fensters beim Schließen über die Systemschaltflächen (Schließen-Kreuz oder Menüauswahl ect.). Als Parameter übergeben wir einfach EXIT_ON_CLOSE, was ja selbsterklärend sein dürfte. Als letztes rufen wir setVisible() mit dem Parameter true auf und lassen so das Fenster anzeigen.

Nun schauen wir uns die überlagerte Methode paint() an. Das erste was uns hier ins Auge fällt ist wieder der Zeiger super, diesmal ohne nachfolgendes Klammernpaar. Diesmal greifen wir mittels dem Dereferenzierungsoperator '.' direkt und explizit auf eine Methode des übergeordneten, eingebetteten Subobjekts JFrame zu. In diesem Falle auf die ursprüngliche Version von paint(), die - wie ersichtlich ist - ein Objekt vom Type Graphics erwartet. Wir leiten einfach das erhaltene Objekt 'g' als Parameter weiter, mehr nicht. Damit kann Java erstmal alles das erledigen, was das ursprüngliche paint() in JFrame beabsichtigt hatte. Erst danach, wenn das ürsprüngliche paint() seine Aufgaben erfüllt hat und zurückspringt, dürfen wir in unserem Fenster rumzeichnen. Wir benutzen das Graphics-Objekt 'g' und rufen dessen Methode drawLine() auf. Die Methode erwartet vier Parameter, nämlich die Startkoordinaten der Linie und die Endkoordinaten. Dabei beziehen sich alle Angaben der Koordinaten auf den darstellbaren Fensterbereich der ContentPane, sind also relativ zum Fenster. Die Startkoordinaten [0|0] bezeichnen also demnach die linke obere Ecke der ContentPane. Gezeichnet wird die Linie letztendlich in der aktuellen Vordergrundfarbe.

Und das war's auch hier schon wieder =)

Tipps

Immer dran denken, daß bei jeder Ungültigkeitswerdung des Fensters die Methode paint() aufgerufen wird. Somit wird auch jedesmal die Linie von links oben bis zum aktuellen Punkt rechts unten gezeichnet. Professionelle Projekte prüfen daher meist mit Hilfe boole'scher Variablen ect., ob eine Linie gezeichnet werden muß oder nicht.

Anmerkung

Dieses Tutorial stammt aus der ehemaligen Sammlung des resourcecode.de und konnte dank der freundlichen Zustimmung des Autors in das thewall-Wiki übertragen werden.