Malen in AWT und Swing

Aus Byte-Welt Wiki
Version vom 19. April 2008, 06:09 Uhr von AndreUhres (Diskussion | Beiträge) (Guter Zeichnen-Code ist der Schlüssel zu einer leistungsfähigen Anwendung)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Zur Navigation springenZur Suche springen

Zeichnen in AWT und Swing

Guter Zeichnen-Code ist der Schlüssel zu einer leistungsfähigen Anwendung Von Amy Fowler http://java.sun.com/products/jfc/tsc/articles/painting/

In einem graphischen System ist normalerweise ein Fensterdarstellungstoolkit dafür verantwortlich, einen Rahmen zur Verfügung zu stellen, der es relativ einfach macht, auf einer graphische Benutzerschnittstelle (GUI) die richtigen Bits zur rechten Zeit darzustellen. Sowohl AWT (abstraktes Fensterdarstellungstoolkit) als auch Swing liefert solch einen Rahmen. Aber die APIs, die es verwirklichen, werden nicht gut von manchen Entwicklern verstanden -- ein Problem, das zu Programmen geführt hat, die nicht so leistungsfähig sind, wie sie sein könnten.

Dieser Artikel beschreibt die AWT und Swing Malmechanismen im Detail. Sein Zweck ist, Entwicklern zu helfen, korrekten und leistungsfähigen GUI Zeichnen-Code zu schreiben. Während der Artikel die allgemeine Malmechanismen umfaßt (wo und wann man darstellt), erklärt er nicht, wie man die Swing Graphiken APIs benutzt, um korrekte Graphiken darzustellen. Um zu erlernen wie man nette Graphiken macht, besichtigen Sie die Java 2D Website.

Entwicklung des Swing Malsystems

Als die ursprüngliche AWT API für JDK 1.0 entwickelt wurde, gab es nur heavyweight Komponenten ("heavyweight" bedeutet, daß die Komponente ihr eigenes undurchlässiges natives Fenster hat). Dies erlaubte es dem AWT, sich voll auf das Maluntersystem der nativen Plattform zu stützen. Dieser Entwurf kümmerte sich um Details wie Beschädigungserkennung, Clipberechnung und z-Einrichtung.

Mit der Einführung der lightweight Komponenten in JDK 1.1 (eine "lightweight" Komponente ist eine, die das native Fenster seines nächsten heavyweight Vorfahrs wiederverwendet), mußte das AWT das Malen für lightweight Komponenten im gemeinsamen Java Code einführen. Somit gibt es feine Unterschiede bezüglich des Malvorgangs für heavyweight und lightweight Komponenten. Nach dem JDK 1.1, als der Swing Toolkit freigegeben wurde, stellte es seine eigenen Malmechanismen vor. In den meisten Fällen ähnelt und beruht die Swing MalMechanismus auf dem AWT. Aber sie führt auch einige Unterschiede bezüglich der Mechanismen ein, sowie neues APIs, mit denen die Anwendungen den Malvorgang einfacher anpassen können.

Malen in AWT

Zu verstehen, wie die Mal-API von AWT arbeitet, hilft zu erkennen, was einen Malvorgang in einer Fensterdarstellungsumgebung auslöst. In AWT gibt es zwei Arten von Malvorgängen: systemausgelöstes Malen und anwendungsausgelöstes Malen.

Systemausgelöstes Malen

In einem systemausgelösten Malvorgang, bittet das System eine Komponente, seinen Inhalt darzustellen, normalerweise aus einem der folgenden Gründe:

Die Komponente wird zum erstenmal sichtbar auf dem Bildschirm abgebildet.

Die Komponente wird in der Größe verändert.

Die Komponente wurde beschädigt und muß repariert werden (zum Beispiel, wurde etwas verschoben, das vorher die Komponente versteckte, und ein vorher versteckter Teil der Komponente wurde sichtbar).

Anwendungsausgelöstes Malen

Bei einem anwendungsausgelösten Malvorgang, entscheidet die Komponente, daß sie ihren Inhalt aktualisieren muß, weil sich sein interner Zustand geändert hat (zum Beispiel, ein Button erkennt, daß eine Maustaste betätigt worden ist und stellt fest, daß er einen "niedergedrückten" Button malen muß).

Die paint Methode

Ungeachtet dessen, wie ein Malantrag ausgelöst wird, benutzt das AWT einen "callback" ("Wiederholungsbesuch") Mechanismus zum Zeichnen, und dieser Mechanismus ist derselbe für heavyweight und lightweight Komponenten. Dies heißt, daß ein Programm den Darstellungscode der Komponente innerhalb einer bestimmten überschriebenen Methode setzen sollte, und der Toolkit ruft diese Methode auf, wenn es Zeit ist zu malen. Die zu überschreibende Methode ist in java.awt.Component:

public void paint(Graphics g)

Wenn AWT diese Methode aufruft, der Parameter "Graphics g" mit dem passenden Zustand für das Zeichnen auf dieser bestimmten Komponente vorkonfiguriert:

Die Farbe des Graphics Objektes wird auf die foreground-Eigenschaft der Komponente eingestellt. Die Schriftart des Graphics Objektes wird auf die font-Eigenschaft der Komponentes eingestellt. Die "translation" des Graphics Objektes wird so eingestellt, daß die Koordinate (0.0) die obere linke Ecke der Komponente darstellt. Das "clip"-Rechteck des Graphics Objektes wird auf den Bereich der Komponente eingestellt, der neu gezeichnet werden muss.

Programme müssen dieses Graphics Object verwenden (oder ein von ihm abgeleitetes) um die Oberfläche darzustellen. Sie sind frei, den Zustand des Graphics Objektes so zu ändern wie es benötigt wird. Hier ist ein einfaches Beispiel von einem paint callback, der einen gefüllten Kreis innerhalb einer Komponente darstellt:

   public void paint(Graphics g) {
       // Dynamically calculate size information
       Dimension size = getSize();
       // diameter
       int d = Math.min(size.width, size.height); 
       int x = (size.width - d)/2;
       int y = (size.height - d)/2;
       // draw circle (color already set to foreground)
       g.fillOval(x, y, d, d);
       g.setColor(Color.black);
       g.drawOval(x, y, d, d);
   }
Entwickler, die neu bei AWT sind, könnten einen Blick auf das PaintDemo Beispiel (http://java.sun.com/products/jfc/tsc/articles/painting/src/PaintDemo.java) werfen wollen, das ein lauffähiges Programmbeispiel liefert, wie man den paint callback in einem AWT Programm verwendet. 

Im allgemeinen sollten Programme es vermeiden, darstellenden Code an irgendeinen Punkt zu setzen, wo er von außerhalb des Bereichs der paint callback Methode aufgerufen werden könnte. Warum? Weil solcher Code manchmal aufgerufen werden kann, wenn zu malen nicht ist angebracht -- zum Beispiel bevor der Komponente sichtbar ist oder Zugang zu einem gültigen Graphics Objekt hat. Es wird nicht empfohlen, daß Programme paint() direkt aufrufen.

Um anwendungsausgelöstes Malen zu ermöglichen, liefert das AWT die folgenden java.awt.Component Methoden, um Programme eine asynchrone paint Operation anfragen zu lassen:

   public void repaint() 
   public void repaint(long tm) 
   public void repaint(int x, int y, int width, int height) 
   public void repaint(long tm, int x, int y, 
                  int width, int height)

Der folgende Code zeigt ein einfaches Beispiel eines MouseListener, der repaint() benutzt, um Updates auf einer theoretischen Taste auszulösen, wenn die Maus gedrückt und losgelassen wird:

       MouseListener l = new MouseAdapter() {
           public void mousePressed(MouseEvent e) {
               MyButton b = (MyButton)e.getSource();
               b.setSelected(true);
               b.repaint();            
           }
           public void mouseReleased(MouseEvent e) {
               MyButton b = (MyButton)e.getSource();
               b.setSelected(false);
               b.repaint();            
           }
       };

Komponenten, die komplizierte Darstellungen ausgeben, sollten repaint() mit den Argumenten aufrufen, welche nur den Bereich definieren, der die Aktualisierung erfordert. Es ist ein allgemeiner Fehler, immer die keine-Argumente Version zu benutzen, die immer die gesamte Komponente neu zeichnet, was häufig zu nicht notwendigen Malverarbeitungen führt.

paint() gegen update()

Warum unterscheiden wir zwischen systemausgelöstem und anwendungsausgelöstem Malen? Weil AWT jeden dieser Fälle etwas anders für heavyweight Komponenten behandelt (der lightweight Fall wird später besprochen), was leider eine Quelle großer Verwirrung ist.

Für heavyweight Komponenten geschehen diese zwei Arten des Zeichnens in zwei unterschiedlichen Wegen, abhängig davon, ob ein Malvorgang systemausgelöst oder anwendungsausgelöst ist.

systemausgelöstes Malen

dies ist, wie ein systemausgelöster Malvorgang stattfindet:

1. Das AWT stellt fest, daß entweder ein Teil oder die ganze Komponente gemalt werden muß.

2. Das AWT verursacht den Event Dispatch Thread, um paint() auf der Komponente aufzurufen.

anwendungsausgelöstes Malen

Das Malen und die lightweight Komponenten

Wie lightweight Komponenten gemalt werden

lightweight und systemausgelöstes Malen

lightweight und Transparenz

Das "intelligente" Malen

AWT Malrichtlinien

Zeichnen in Swing

Doppelter Puffer Unterstützung

Zusätzliche Maleigenschaften

Die Opazität

"optimiert" zeichnen

Die Malmethoden

Das Malen und der UI Delegate

Malverarbeitung

Synchrones Zeichnen

Das RepaintManager

Das "gegenwärtige" RepaintManager

Den "gegenwärtigen" RepaintManager ersetzen

Globale Steuerung über Doppelten Puffer

Swing Malrichtlinien

Zusammenfassung