10.24Swing-Komponenten neu erstellen oder verändern *
Zum Aufbau neuer Swing-Komponenten kommt eine Reihe von Möglichkeiten in Frage. Wenn es passend ist, lässt sich eine existierende Swing-Komponente als Basisklasse nehmen und um nötige Eigenschaften erweitern, sofern die Basisklassen diese Möglichkeit im Grunde schon bieten. Soll etwa ein Texteingabefeld nur IP-Adressen zulassen, so ist dafür keine völlig neue Textkomponentenimplementierung nötig, sondern nur eine Unterklasse der Standardkomponente mit passendem Dokumentenmodell. Oder soll eine Liste nur Kontrollkästen (mit Text) darstellen, ist das schon über die JList mit passendem Renderer und Modell möglich.
Unproblematisch ist auch, wenn sich neue Komponenten aus anderen Swing-Teilkomponenten zusammensetzen lassen. Dann erweitert die neue Swing-Klasse einen Container wie JPanel, der einfach die anderen Elemente wie gewünscht platziert. Möglich ist dies zum Beispiel bei einer Statuszeile, da diese nichts Großartigeres macht, als einfach horizontal andere Komponenten anzuordnen und einen besonderen Rahmen zu setzen. Einen Dialog zur Auswahl eines Zeichensatzes bietet Swing bisher auch nicht an, der lässt sich aber als JDialog mit passenden Swing-Komponenten leicht nachbauen.
Mehr Arbeit ist nötig, wenn sich auf keine allgemeinen Swing-Komponenten zurückgreifen lässt. Die Swing-Bibliothek bietet etwa keine Ribbon-Komponente, keinen wirklich guten HTML-Renderer oder kein Docking-Framework. Bei Anforderungen dieser Art ist spezieller Programmcode zum Zeichnen nötig. Der wesentliche Unterschied ist der, dass sich die Darstellung nicht vollständig an Standardkomponenten delegieren lässt, sondern immer etwas eigener Java-Code zum Zeichnen nötig ist.
Um es richtig gut zu machen, sind für eine eigene Swing-Komponente drei Dinge nötig: Die Komponentenklasse, eine Modellklasse und ein UI-Delegate. Die Komponentenklasse ist die Hauptklasse und eine JComponent, die der Entwickler auf das GUI setzt. Sie bietet die API zum Setzen der Zustände. Die Modelldaten werden nicht selbst in der Komponentenklasse gespeichert, sondern idealerweise über eine eigene Klasse modelliert. Die Tabelle nimmt zum Beispiel die Zellen aus einem Tabellenmodell, eine Textkomponente den Text aus einem Dokumentenmodell. Als Letztes bleibt der UI-Delegate, der das wirkliche Zeichnen und die Ereignisbehandlung übernimmt. Es kann sehr anspruchsvoll sein, ein gutes Aussehen und eine effektive Navigation zu erreichen, und es kann, insbesondere wenn die Komponente in verschiedenen Look-and-Feels arbeiten soll, eine Menge Arbeit werden. Und dass die eigene Swing-Komponente die UI-Eigenschaften wie Farben, Abstände und Antialiasing-Modus toleriert, ist selbstverständlich.
10.24.1Überlagerungen mit dem Swing-Komponenten-Dekorator JLayer
Können Swing-Komponenten überlagert werden, können dadurch interessante Effekte erzieht werden. Ein paar Beispiele:
Während ein Text in die Textbox geladen wird, erscheint ein JProgressBar.
Bei aufwändigen Operationen wird das Haupt-Panel gesperrt, und eine drehende Sanduhr erscheint.
Ist die Eingabe in einem Textfeld falsch, erscheint ein kleines Symbol, das über die ungültige Eingabe informiert.
Über einer leeren Tabelle liegt eine Beschriftung, die erklärt, dass ein Doppelklick eine neue Zeile einfügt.
Alle diese Darstellungen lassen sich mithilfe der Klasse JLayer einfach lösen. Die Haupteigenschaft von JLayer ist, sich um existierende Swing-Komponenten zu legen. Soll ein JLayer um ein Textfeld gelegt werden, heißt es:
Die zu ummantelnde Komponente wird über den Konstruktor angegeben und nicht über add(…), da JLayer kein Container ist. Der nächste Schritt ist die Angabe eines Objekts, das das Zeichnen übernimmt.
Die Angabe erfordert ein LayerUI-Objekt, das eine paint(…)-Methode realisiert. Die Implementierung kann super.paint(…) aufrufen, um die ummantelte Komponente zu zeichnen, und dann eigenen Programmcode hinzufügen, um etwa eine Sanduhr darzustellen.
Das folgende Beispiel fasst die Schritte zusammen und realisiert ein Programm, das bei Eingabe von »pu« einen kleinen roten Kreis anzeigt.
Listing 10.85com/tutego/insel/ui/swing/JLayerDemo.java, main()
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.setLayout( new BorderLayout(2, 2) );
f.add( new JSeparator(), BorderLayout.PAGE_START );
f.add( new JLabel( "Name:" ), BorderLayout.LINE_START );
final JTextField textField = new JTextField();
LayerUI<JComponent> layerUI = new LayerUI<JComponent>() {
@Override public void paint( Graphics g, JComponent component ) {
super.paint( g, component );
if ( textField.getText().equalsIgnoreCase( "pu" ) ) {
g.setColor( new Color( 255, 0, 0, 100 ) );
g.fillOval( 0, component.getHeight() – 10, 10, 10 );
}
}
};
JLayer<JComponent> layer = new JLayer<>( textField );
layer.setUI( layerUI );
f.add( layer );
f.add( new JSeparator(), BorderLayout.PAGE_END );
f.pack();
f.setVisible( true );
Die Klasse JLayer kann auch das Haupt-Panel dekorieren und die Events auffangen. Das ist ein zweites Anwendungsfeld neben der Änderung der Darstellung. Die JLayer-Komponente kann einfach Events auffangen und verarbeiten und so zum Beispiel global (F1) für die Hilfe abfangen.