Thema der Woche: Google Cache

  • Studiere aufmerksam https://code.google.com/p/guava-libraries/wiki/CachesExplained.
  • Schreibe ein Swing-Programm mit einer JList und einem eigenen Listen-Model, was zu einem Index die Primfaktorzerlegung berechnet (benutze Google und suche eine Implementierung). Bei 1 wird also 1 angezeigt, bei 2 folgt 1*2, bei 3 dann 3, bei 4 folglich 2*2 usw.
  • Das Ergebnis soll nicht mehr live berechnet werden, sondern über den Google Cache zwischengespeichert werden, was ein beschleunigtes Scrollen bewerkstelligen sollte. Wie lässt sich das integrieren?
  • Gibt es mehrere Möglichkeiten den Google Cache in dem Szenario einzusetzen?

Thema der Woche: Paketierung mit <fx:deploy>

Seit Neustem kann man mit Java auch ausführbare Dateien bzw. Installer bauen. Ließ dazu http://docs.oracle.com/javafx/2/deployment/self-contained-packaging.htm bzw. suche nach weiterer Dokumentation im Netz.

  • Teste das an einer eigenen kleinen Hello-World-Anwendung.
  • Wie groß ist das Ergebnis mit JRE?
  • Welche Zielformate sind möglich und kann man alle etwa auf einem Linux-Build-Server bauen?
  • Nutze log4j und nimm die Jar mit in das Zielformat mit auf. Lassen sich auch native Dateien einbinden?
  • Gilt diese Möglichkeit nur für JavaFX oder auch für für AWT/Swing oder SWT?

Java 8 könnte auf 18. März 2014 verschoben werden, wäre dann aber “Der Profi”

Bisher Vorschläge!

  • http://mail.openjdk.java.net/pipermail/jdk8-dev/2013-April/002336.html
  • http://mreinhold.org/blog/secure-the-train
  •   2013/05/09  M7  Feature Complete
      2013/07/18      Rampdown start
      2013/09/05  M8  Developer Preview
      2013/09/12      All Tests Run
      2013/10/10      API/Interface Freeze
      2013/10/24      Zero Bug Bounce
      2013/11/21      Rampdown phase 2
      2014/01/23  M9  Final Release Candidate
      2014/03/18  GA  General Availability

    Der 18.03. ist bestimmt kein Zufall, denn Luc Besson hat Geburtstag, und der ist uns ja in Erinnerung mit dem 1994er Klassiker “Der Profi”mit Jean Reno (nicht der mit Jean-Paul Belmondo, der war 1981). Damit wird Java 8 bestimmt auch so ein Profi im Bereich Sicherheit (naja, der Profi stirbt am Ende), sodass wir alle sagen können “Hier wird es uns gut gehen, Java”.

    Java 9 kommt dann in der ersten Hälfte von 2016. Oder irgendwann.

    Buchkritik: Wicked Cool Java: Code Bits, Open-Source Libraries, and Project Ideas

    Brian Eubanks; ISBN-10: 1593270615; No Starch Press; 15.11.2005; 248 Seiten
    Mit dem Buch hat Brian eigentlich das gemacht, was sich jeder Autor wünscht: sich hinzusetzen und einfach mal im Artikelstil über alles zu schreiben, was einem interessiert, ohne darauf zu achten, ob das nützlich oder wichtig ist. Dafür nimmt er sich 8 Kapitel Zeit:
    Chapter 1: Java Language and Core API, Chapter 2: String Utilities, Chapter 3: Processing XML and HTML, Chapter 4: Crawling the Semantic Web, Chapter 5: Math and Science, Chapter 6: Graphics and Data Visualization, Chapter 7: Multimedia and Sychronization, Chapter 8: Fun, Integration and Project Ideas. Im Grunde ist nur das erste Kapitel ein kleines Einstiegskapitel, insbesondere in die Neuerungen von Java 5, und zusammen mit dem zweiten Kapitel haben sie die Java SE selbst zum Inhalt. Das Niveau ist an Anfang niedrig und passt nicht zum Rest. Syntaktisch ist nicht immer alles sauber, so gibt es immer wieder umständliche Feldinitialisierungen wie int[] theList = new int[]{2,3,5,7}; oder java.util.Random random = new Random(); wo ich mich Frage, ob der Autor dort gerade wach war. Dann wird assert geklammert wie eine Methodenaufruf, das ist aber nur in C so, nicht in Java, wo assert ein Schlüsselwort ist und der Ausdruck nicht geklammert wird. (Macht Krüger im Buch aber leider auch so.) Leider sind auch nicht alle Beispiele konsequent auf Java 5 ausgelegt, immer wieder findet sich der Raw-Type etwa von Datenstrukturen, bei seinen verketten Listen-Implementierung oder mit verketteten Knoten wiederum fehlt ein Generic, hier steht nur Object content. An anderer Stelle im Buch gibt es den Hinweis, das ein Listing mit Generics auf der Buchseite (http://www.wickedcooljava.com/) ist, warum nicht gleich im Code? Mit Generics scheint Brian auch noch nicht so vertraut zu sein, anders kann ich mir nicht erklären, warum er eine Methode removeMatches(List<Number> aList) schreibt, denn man muss verstehen, da man so etwas nicht zum Beispiel mit einer List<Double> aufrufen kann; eleganter wäre ein Typ-Bound er Art List<? extends Number> hin. Weiter: Statt StringBuilder kommt noch StringBuffer zum Einsatz. Nicht gut gefällt mir auch der Bezug auf konkrete Klasse, statt Basistypen, etwa bei ArrayList<String> getNames(), hier würde als Rückgabe auch List oder Collection reichen. (In dem gleichen Beispiel ist auch unglücklich den Scanner auch nicht im finally zu schließen. Und getFloat() statt getDouble() zu nehmen ist auch Geschmackssache.) Bei den Farben verwendet der Autor noch die klein geschriebenen Namen also Color.blue statt Color.BLUE. Sehr geschwätzig ist auch if ( … ) { return true; } else { return false; } — das ist uncool.
    Im Mittelpunkt des Buches geht es um wilde Open-Source Bibliotheken, etwa für mathematische Operationen, Textanalyse, Suche (nicht wild), semantische Netze, RDF, MIDI-Sounds. Eigentlich nichts, was man wirklich/dringend/oft bräuchte, und wenn, würde man vermutlich in ein spezielles Buch schauen. Positiv ist anzumerken, dass uns der Autor die Libs vorstellt und des Lesers Horizont erweitert. Ein Probekapitel gibt es nicht online, allerdings unter http://www.wickedcooljava.com/related.jsp eine Linkliste der Bibliotheken. April 2013

    Assertions feiner aktivieren oder deaktivieren

    Assertions müssen nicht global für das ganze Programm gesetzt werden, sondern könne auch feiner deklariert werden, etwa für eine Klasse oder ein Paket. Mit geschickter Variation von –ea (Assertions aktivieren) und –da (Assertions desaktivieren) lässt seht gut steuern, was die Laufzeitumgebung prüfen soll.

    Beispiel

    Aktiviere Assertions für die Klasse com.tutego.App:

    $ java -ea:com.tutego.App AppWithMain

    Aktiviere Assertions für das Default-Paket (dafür stehen die drei Punkte):

    $ java -ea:… AppWithMain

    Aktiviere Assertions für das Paket com.tutego inklusive aller Unterpakete (auch dafür stehen drei Punkte):

    $ java -ea:com.tutego… AppWithMain

    Aktiviere Assertions für das Paket com.tutego inklusive aller Unterpakete, aber desaktiviere sie für die Klasse App in dem Paket com.tutego:

    $ java -ea:com.tutego… -da:com.tutego.App AppWithMain

    Assertions müssen Nebeneffekt frei sein

    Assertions stehen immer in der Klassendatei, da sie der Compiler immer in Bytecode abbildet. Die JVM ignoriert Assertions standardmäßig bei der Ausführung und eine Aktivierung erfolgt nur auf Befehl; ein Ablauf ohne Bedingungstests ist also der Normalfall. Daraus folgt, dass Ausdrücke in den assert-Anweisungen ohne Nebeneffekte sein müssen. So etwas wie

    assert counter– == 0;

    ist keine gute Idee, denn das Vermindern der Variablen ist ein Nebeneffekt, der nur dann stattfindet, wenn die JVM auch Assertions aktiviert hat. Allerdings lässt sich das auch für einen Trick nutzen, Assertions bei der Ausführung zu erzwingen. Im statischen Initialisierer einer Klasse können wir setzen:

    boolean assertEnabled = false;

    assert assertEnabled = true;

    if ( ! assertEnabled )

      throw new RuntimeException( "Assertions müssen aktiviert werden" );

    Mein Abendvortrag “Java 8 Features” in Braunschweig am 13. Juni

    Java User Group Ostfalen

    CKC
    13 Am Alten Bahnhof
    38122 Braunschweig
    Deutschland

    Donnerstag, 13. Juni 2013, von 19:00 bis 22:00 (MESZ)

     

    Christian Ullenboom gibt in seinem Abendvortrag einen Einblick in die Neuerungen von Java 8, angefangen von statischen Schnittstellenmethoden über Default-Methoden, Lambda-Ausdrücken, Annotationen-Ergänzungen bis zu den Erweiterungen der Standard-Bibliothek. In der gemütlichen Diskussionsrunde im Anschluss gibt es keine Denkverbote und es darf auch über Plagiate, Flughäfen in großen Städten, ausgefüllte Dirndl offen und frei gesprochen werden.

     

    Referent:

    Presenter.build().name( "Christian Ullenboom" ).born( 1973 ).studied( "Diplom-Informatik" )
    .cameToJava( "im ‚Ruck‘-Jahr 1997" )
    .knownFor( "Java-Blog", URI.create( "http://javainselblog.tutego.de" ) )
    .autored( "Java ist auch eine Insel" ).autored( "Java – mehr als eine Insel" )
    .orElse( "Den Rest des Tages verbringt er mit seiner Liebe" )
    .orElse( "Erweiterung des Schulungsunternehmens tutego", URI.create( "http://tutego.de/" ) )
    .orElse( "alten Computern/Videospielkonsolen", URI.create( "http://RetroBu.de/" ) ).done();

    Thema der Woche: Etwas Design mit UML

    DRUG LORD (http://www.old-games.com/download/5218/druglord) ist ein Spielklassiker für DOS von 1993. Mit Kauf- und Verkauf von Drogen in verschiedenen Städten muss man möglichst viel Geld verdienen.

    5218-5-druglord

    Bild © old-games.com

    Überlege, wie das Spiel in seinem Grundzügen (nur Kauf/Verkauf von Drogen, Orte und Cash aber kein Hospital/Health, keine Waffen, Bank, Schulden) objektorientiert modelliert werden kann. Nutzte UML-Diagramme zur Darstellung. Das Programm soll am Ende lauffähig sein, die Darstellung kann beliebig sein. (Wer das Retro-Feeling mag, kann http://sourceforge.net/projects/javacurses/ ausprobieren.)

    Klassen mit einer abstrakten Methode als funktionale Schnittstelle in Java 8?

    Als die Entwickler Lambda-Ausdrücke diskutierten, stand auch die Frage im Raum, ob abstrakte Klassen, die nur über eine abstrakte Methode verfügen – früher wurde hier die Abkürzung SAM (Single Abstract Method) genutzt –, ebenfalls für Lambda-Ausdrücke genutzt werden können. Sie entschieden sich dagegen, da bei Implementierung von Schnittstellen die JVM weitreichende Optimierungen vornehmen kann. Und bei Klassen wir das schwierig, was auch daran liegt, dass ein Konstruktor umfangreiche Initialisierungen mit Seiteneffekten vornehmen (die Konstruktoren aller Oberklassen nicht zu vergessen) sowie Ausnahmen auslösen könnte. Gewünscht ist aber nur die Ausführung einer Implementierung der funktionalen Schnittstelle und kein anderer Code.

    Es gibt nun im JDK einige abstrakte Klassen, die genau eine abstrakte Methode vorschreiben, etwa jva.util.TimerTask. Solche Klassen können nicht über einen Lambda-Ausdruck realisiert werden; hier müssen Entwickler weiterhin zu Klassenimplementierungen greifen, und das kürzeste ist eine innere anonyme Klasse. Eigene Hilfsklassen können natürlich den Code etwas abkürzen, aber eben nur mit eigener Implementierung. Zwei Strategien bieten sich an: durch Delegation oder Vererbung. Nehmen wir das Beispiel für TimerTask und gehen beide Varianten durch:

    import java.util.*;

    class TimerTaskLambda {

    public static TimerTask createTimerTask( Runnable runnable ) {

    return new TimerTask() {

    @Override public void run() { runnable.run(); }

    };

    }

    public static void main( String[] args ) {

    new Timer().schedule( createTimerTask( () -> System.out.println("Hi") ), 500 );

    }

    }

    Oder mit Vererbung:

    public class LambdaTimerTask extends TimerTask {

    private final Runnable runnable;

    public LambdaTimerTask( Runnable runnable ) {

    this.runnable = runnable;

    }

    @Override public void run() { runnable.run(); }

    }

    Der Aufruf ist dann statt createTimerTask(…) der des Konstruktors:

    new Timer().schedule( new LambdaTimerTask( () -> System.out.println("Hi") ), 500 );

    Inselraus: Die Farben des Systems über java.awt.SystemColor

    Bei eigenen Java-Programmen ist es wichtig, dass diese sich so perfekt wie möglich in die Reihe der anderen Client-Programme einordnen, ohne großartig aufzufallen. Dafür muss ein Fenster die globalen Einstellungen wie den Zeichensatz und die Farben kennen. Für die Systemfarben gibt es die Klasse SystemColor, die alle Farben einer grafischen Oberfläche auf symbolische Konstanten abbildet. So ist SystemColor.text[1] die Hintergrundfarbe von Texteingabefeldern. Besonders praktisch ist dies bei Änderungen von Farben während der Laufzeit. Über diese Klasse können immer die aktuellen Werte eingeholt werden, denn ändert sich beispielsweise die Hintergrundfarbe der Laufleisten, ändert sich damit auch der RGB-Wert.

    Die Systemfarben sind Konstanten von Typ SystemColor, was eine Unterklasse von Color ist. Damit lassen sich sich direkt nutzen, etwa über setColor(Color) oder über getRGB() der RGB-Anteil erfragen. Die Klasse SystemColor hat keine eigenen öffentlichen Methoden, sondern überschreibt nur toString().

    Die Klasse deklariert die folgenden statischen finalen Variablen:

    class java.awt.SystemColor extends Color implements Serializable

    SystemColor

    Welche Farbe darauf anspricht

    desktop

    Farbe des Desktop-Hintergrunds

    activeCaption

    Hintergrundfarben für Text im Fensterrahmen

    activeCaptionText

    Farbe für Text im Fensterrahmen

    activeCaptionBorder

    Rahmenfarbe für Text im Fensterrahmen

    inactiveCaption

    Hintergrundfarbe für inaktiven Text im Fensterrahmen

    inactiveCaptionText

    Farbe für inaktiven Text im Fensterrahmen

    inactiveCaptionBorder

    Rahmenfarbe für inaktiven Text im Fensterrahmen

    window

    Hintergrundfarbe der Fenster

    windowBorder

    Rahmenfarbe der Fenster

    windowText

    Textfarbe für Fenster

    menu

    Hintergrundfarbe für Menüs

    menuText

    Textfarbe für Menüs

    text

    Hintergrundfarbe für Textkomponenten

    textText

    Textfarbe für Textkomponenten

    textHighlight

    Hintergrundfarbe für hervorgehobenen Text

    textHighlightText

    Farbe des Texts, wenn dieser hervorgehoben ist

    textInactiveText

    Farbe für inaktiven Text

    control

    Hintergrundfarbe für Kontrollobjekte

    controlText

    Textfarbe für Kontrollobjekte

    controlHighlight

    normale Farbe, mit der Kontrollobjekte hervorgehoben werden

    controlLtHighlight

    hellere Farbe, mit der Kontrollobjekte hervorgehoben werden

    controlShadow

    normale Hintergrundfarbe für Kontrollobjekte

    controlDkShadow

    dunklerer Schatten für Kontrollobjekte

    scrollbar

    Hintergrundfarbe der Schieberegler

    Info

    Hintergrundfarbe der Hilfe

    infoText

    Textfarbe der Hilfe

    Konstanten der Systemfarben

    Hinweis: Die Klasse javax.swing.UIManager ist ein großer Assoziativspeicher, bei dem sich weitere Belegungen erfragen lassen. Es erfragt zum Beispiel UIManager.getColor("Table.background") die Tabellen-Hintergrundfarbe vom gerade eingestellen Look and Feel.[2]


    [1] Sun verstößt mal wieder gegen die eigenen Namenskonventionen. Die finalen Variablen – Konstanten – sollten großgeschrieben werden. Das funktioniert bei den SystemColor-Objekten aber nicht, da es alle Bezeichnernamen schon in Großbuchstaben gibt, und zwar für Variablen vom Typ Byte, die Verweise in eine interne Tabelle darstellen.

    [2] Die Seite http://www.devdaily.com/java/java-uimanager-color-keys-list liefert eine Auflistung der Schlüssel und ein Programm zur Anzeige.

    Inselraus: Zeichensätze des Systems ermitteln

    Um herauszufinden, welche Zeichensätze auf einem System installiert sind, liefert getAvailableFontFamilyNames() auf einem GraphicsEnvironment ein Feld mit Font-Objekten. Ein Objekt vom Typ GraphicsEnvironment beschreibt die Zeichensätze des Systems und liefert GraphicsDevice-Objekte. Ein GraphicsDevice ist eine Malfläche, also das, worauf das System zeichnen kann. Das kann der Bildschirm sein, aber auch ein Drucker oder eine Hintergrundgrafik. Die statische Fabrikmethode getLocalGraphicsEnvironment() liefert ein solches GraphicsEnvironment-Objekt.

    Beispiel: Im folgenden Codesegment gibt eine Schleife alle Zeichensatznamen aus:

    for ( String fonts : GraphicsEnvironment.
            getLocalGraphicsEnvironment().getAvailableFontFamilyNames() )
      System.out.println( fonts );
    

    Auf meinem System liefert die Schleife die folgenden Ausgaben:

    Arial

    Arial Black

    Arial Narrow

    Wingdings

    Wingdings 2

    Wingdings 3

    Zur API:

    abstract class java.awt.GraphicsEnvironment

    • static GraphicsEnvironment getLocalGraphicsEnvironment()
      Liefert das aktuelle GraphicsEnvironment-Objekt.
    • abstract Font[] getAllFonts()
      Liefert ein Feld mit allen verfügbaren Font-Objekten in einer Größe von einem Punkt.
    • abstract String[] getAvailableFontFamilyNames()
      Liefert ein Feld mit allen verfügbaren Zeichensatzfamilien.
    • abstract String[] getAvailableFontFamilyNames(Locale l)
      Liefert ein Feld mit verfügbaren Zeichensatzfamilien, die zu einer Sprache l gehören.

    Inselraus: n-Ecke zeichnen

    In der Graphics-Klasse gibt es keine Methode, um regelmäßige n-Ecken zu zeichnen. Eine solche Methode ist aber leicht und schnell programmiert: Wir teilen dazu einfach einen Kreis in n Teile auf und berechnen die x- und y-Koordinaten der Punkte auf dem Kreis. Diese Punkte fügen wir einem Polygon-Objekt mittels der addPoint(…)-Methode hinzu. Eine eigene statische Methode drawVertex(…) übernimmt diese Polygon-Erstellung. Der letzte Parameter der Methode ist ein Wahrheitswert, der bestimmt, ob das n-Eck gefüllt werden soll oder nicht:

    package com.tutego.insel.ui.graphics;
    
    import java.awt.*;
    import javax.swing.*;
    
    public class N_Vertex extends JPanel {
    
      private static final long serialVersionUID = -6314283966378303073L;
    
      @Override protected void paintComponent( Graphics g ) {
        VertexDrawer.drawVertex( g, getWidth() / 2, getHeight() / 2, 50, 6, true );
        VertexDrawer.drawVertex( g, getWidth() / 2, getHeight() / 2, 60, 6, false );
      }
    
      public static void main( String[] args ) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        f.add( new N_Vertex() );
        f.setSize( 200, 200 );
        f.setVisible( true );
      }
    }
    
    class VertexDrawer {
      public static void drawVertex( Graphics g, int x, int y, int r, int n, boolean filled ) {
        Polygon p = new Polygon();
    
        for ( int i = 0; i < n; i++ )
          p.addPoint( (int) (x + r * Math.cos( i * 2 * Math.PI / n )),
                      (int) (y + r * Math.sin( i * 2 * Math.PI / n )) );
    
        if ( filled )
          g.fillPolygon( p );
        else
          g.drawPolygon( p );
      }
    }
    

    Inselraus: JScrollPane Viewport und Scrollable

    Der Viewport

    Den sichtbaren Ausschnitt der Fläche bestimmt ein JViewport-Objekt, das mit zusätzlichen Listenern etwa für die Änderungen des sichtbaren Bereichs ausgestattet werden kann. Die Methode getViewport() liefert das JViewport-Objekt. Die Methoden scrollRectToVisible(Rectangle) und setViewPosition(Point) des JViewport-Objekts ermöglichen die Ansteuerung des sichtbaren Bereichs.

    Beispiel: Zeige den sichtbaren Bereich auf dem Bildschirm an:

    System.out.println( scrollPane.getViewport().getVisibleRect() );

    Die JViewport-Methode getVisibleRect() stammt aus der direkten Oberklasse JComponent. Sie liefert ein Rectangle-Objekt, und getLocation() liefert den java.awt.Point vom Rechteck oben links.

    Jeweils auf die gegenüberliegende Seite der Rollbalken lassen sich Zeilen- und Spaltenleisten legen, genauso wie in alle vier Ecken Komponenten. Die Leisten liegen selbst wiederum in einem JViewport, um zum Beispiel im Fall einer Linealbeschriftung mitzuwandern. setRowHeaderView(), setColumnHeaderView() und setCorner() setzen bei der JScrollPane die Ecken und Leisten.

    Die Schnittstelle Scrollable

    Die komplexen Komponenten, wie etwa Textanzeigefelder, Bäume oder Tabellen, implementieren eine Verschiebefähigkeit nicht selbst, sondern müssen dazu in einer JScrollPane Platz nehmen. Damit JScrollPane jedoch weiß, wie zum Beispiel nach einem Klick auf den Bildlauf der Ausschnitt zu verändern ist, implementieren die Komponenten die Schnittstelle Scrollable. Die zentralen Klassen JList, JTable, JTextComponent und JTree implementieren die Schnittstelle und teilen auf diese Weise Maße der Komponente und Anzahl der Pixel bei einer Verschiebung mit, wenn etwa der Anwender den Rollbalken um eine Position versetzt.

    Feature: Eine sinnvolle Eigenschaft ist der automatische Bildlauf. Bei diesem Verfahren wird der Bildlauf auch dann fortgesetzt, wenn der Mauszeiger die Komponente schon verlassen hat. Klickt der Benutzer etwa auf ein Element in einer Liste, und bewegt dann den Mauszeiger mit gedrückter Maustaste aus der Liste heraus, so scrollt die Liste mit eingeschaltetem automatischen Bildlauf selbstständig weiter. Die Eigenschaft wird in Swing über die JComponent-Methode setAutoScroll(boolean) gesteuert.

    Inselraus: Bilder auf AbstractButton-basierten Swing-Schaltflächen je nach Zustand ändern

    Die Integration mit den Icon-Objekten liegt in der AbstractButton-Klasse. Geben wir im Konstruktor das Icon nicht an, so lässt sich dies immer noch über setIcon(…) nachträglich setzen und ändern. Wenn die Schaltfläche angeklickt wird, kann ein anderes Bild erscheinen. Dieses Icon setzt setPressedIcon(…). Bewegen wir uns über die Schaltfläche, lässt sich auch ein anderes Icon setzen. Dazu dient die Methode setRolloverIcon(…). Die Fähigkeit muss aber erst mit setRolloverEnabled(true) eingeschaltet werden. Beide Eigenschaften lassen sich auch zu einem Icon kombinieren, das erscheint, wenn die Maus über dem Bild ist und eine Selektion gemacht wird. Dazu dient setRolloverSelectedIcon(…). Für JToggleButton-Objekte ist eine weitere Methode wichtig, denn ein JToggleButton hat zwei Zustände: einen selektierten und einen nicht selektierten. Auch hier können zwei Icon-Objekte zugeordnet werden, und das Icon der Selektion lässt sich mit setSelectedIcon(…) setzen. Ist die Schaltfläche ausgegraut, ist auch hier ein gesondertes Icon möglich. Es wird mit setDisabledIcon(…) gesetzt. Dazu passt setDisabledSelectedIcon(…).

    Inselraus: Die Schnittstelle Icon und eigene Icons zeichnen

    Bei einer genauen Betrachtung fällt auf, dass ImageIcon eine Implementierung der Schnittstelle Icon ist und dass die JLabel-Klasse ein Icon-Objekt erwartet und nicht speziell ein Argument vom Typ ImageIcon. Das heißt aber: Wir können auch eigene Icon-Objekte zeichnen. Dazu müssen wir nur drei spezielle Methoden von Icon implementieren: die Methode paintIcon(…) und ferner zwei Methoden, die die Dimensionen angeben.

    interface interface javax.swing.Icon

    • int getIconWidth()
      Liefert die feste Breite eines Icons.
    • int getIconHeight()
      Liefert die feste Höhe eines Icons.
    • void paintIcon(Component c, Graphics g, int x, int y)
      Zeichnet das Icon an die angegebene Position. Der Parameter Component wird häufig nicht benutzt. Er kann jedoch eingesetzt werden, wenn weitere Informationen beim Zeichnen bekannt sein müssen, wie etwa die Vorder- und Hintergrundfarbe oder der Zeichensatz.

    Die folgende Klasse zeigt die Verwendung der Icon-Schnittstelle. Das eigene Icon soll einen einfachen roten Kreis mit den Ausmaßen 20 × 20 Pixel besitzen:

    class CircleIcon implements Icon {
    
      @Override public void paintIcon( Component c, Graphics g, int x, int y )  {
        g.setColor( Color.red );
        g.fillOval( x, y, getIconWidth(), getIconHeight() );
      }
    
      @Override public int getIconWidth() {
        return 20;
      }
    
      @Override public int getIconHeight() {
        return 20;
      }
    }
    

    Wir überschreiben die drei erforderlichen Methoden, sodass ein Icon-Objekt der Größe 20 × 20 Pixel entsteht. Als Grafik erzeugen wir einen gefüllten roten Kreis. Dieser kann als Stopp-Schaltfläche verwendet werden, ohne dass wir eine spezielle Grafik verwenden müssen. Für die Grafik stehen uns demnach 400 Pixel zur Verfügung – genau getIconWidth() mal getIconHeight() –, und alle nicht gefüllten Punkte liegen transparent auf dem Hintergrund. Dies ist auch typisch für leichtgewichtige Komponenten. Über das Component-Objekt können wir weitere Informationen herausholen, wie etwa den aktuellen Zeichensatz oder die darstellende Vaterkomponente.

    Um das eigene Icon auf ein JLabel zu setzen, lässt sich die Icon-Implementierung entweder im Konstruktor übergeben oder später über setIcon(Icon):

    JLabel circle = new JLabel( new CircleIcon() );
    

    Was die Typen Icon und Image verbindet

    Vielleicht wird der eine oder andere sich schon überlegt haben, ob nun ImageIcon eine ganz eigene Implementierung neben der Image-Klasse ist oder ob beide miteinander verwandt sind. Das Geheimnis ist, dass ImageIcon die Icon-Schnittstelle implementiert, aber auch ImageIcon intern die Image-Klasse nutzt. Sehen wir uns das einmal im Detail an: Ein ImageIcon ist serialisierbar. Also implementiert es die Schnittstelle Serializable. Im Konstruktor kann ein URL-Objekt oder ein String mit einer URL stehen. Hier wird einfach getImage(…) vom Toolkit aufgerufen, um sich eine Referenz auf das Image-Objekt zu holen. Eine geschützte Methode loadImage(Image) wartet nun mithilfe eines MediaTrackers auf das Bild. Nachdem dieser auf das Bild gewartet hat, setzt er die Höhe und Breite, die sich dann über die Icon-Methoden abfragen lassen. Doch ein richtiges Icon muss auch paintIcon(…) implementieren. Hier verbirgt sich nur die drawImage(…)-Methode.

    Kommen wir noch einmal auf die Serialisierbarkeit der ImageIcon-Objekte zurück. Die Klasse implementiert dazu die privaten Methoden readObject(…) und writeObject(…). Der Dateiaufbau ist sehr einfach. Breite und Höhe befinden sich im Datenstrom, und anschließend existiert ein Integer-Feld mit den Pixelwerten. In readObject(…) liest s.defaultReadObject() – wobei s der aktuelle ObjectInputStream ist – das Feld wieder ein, und über die Toolkit-Methode createImage(…) wird die Klasse MemoryImageSource genutzt, um das Feld wieder zu einem Image-Objekt zu konvertieren. Umgekehrt ist es genauso einfach. writeObject(…) schreibt die Breite und Höhe und anschließend das Ganzzahl-Feld mit den Farbinformationen, das es über einen PixelGrabber bekommen hat.

    Inselraus: Zeichensatz eines Swing-Labels ändern

    Der gesetzte Text wird im zugewiesenen Zeichensatz des Swing-Look-and-Feels angezeigt. Um diesen zu ändern, müssen wir ein neues Font-Objekt erzeugen. Auf zwei Arten lässt sich dieser Font setzen: global für alle JLabel-Elemente oder lokal nur für dieses eine. Die erste Lösung arbeitet über das UIDefaults-Objekt, das die Einstellungen wie Zeichensätze und Farben für alle Swing-Elemente verwaltet:

    UIDefaults uiDefaults = UIManager.getDefaults();
    uiDefaults.put( "Label.font",
                    ((Font)uiDefaults.get("Label.font")).deriveFont(30f) );
    

    Unter dem Schlüssel »Label.font« legen wir ein neues Font-Objekt ab und überschreiben die alte Definition. Den neuen Font mit der Größe 30 leiten wir mit deriveFont(float) vom alten ab, sodass wir den Zeichensatz »erben«.

    Die zweite Lösung kann darin bestehen, den Font direkt mit der setFont(Font)-Methode zu setzen:

    JLabel l = new JLabel( "Lebe immer First-Class, sonst tun es deine Erben!" );
    l.setFont( new Font("Serif", Font.BOLD, 30) );
    

    Einen speziellen Konstruktor, der ein Font-Objekt als Argument annimmt und dieses verwendet, gibt es nicht.

    Inselraus: Dynamisches Layout während einer Größenänderung

    Wird ein Fenster vergrößert, dann kann während der Größenänderung der Inhalt sofort neu ausgerichtet und gezeichnet werden oder auch nicht. Wird er nicht dynamisch angepasst, dann sieht der Benutzer diese Anpassung erst nach dem Loslassen der Maus, wenn die Größenänderung abgeschlossen wurde. Dieses dynamische Vergrößern lässt sich im Toolkit-Objekt einstellen, und zwar über Toolkit.getDefaultToolkit().setDynamicLayout(true). Nicht jedes Toolkit unterstützt allerdings diese Fähigkeit! Ob es das tut, verrät Toolkit.getDefaultToolkit().getDesktopProperty("awt.dynamicLayoutSupported").

    Inselraus: Selektionen einer Tabelle

    In einer JTable können auf unterschiedliche Art und Weise Zellen selektiert werden: zum einen nur in einer Zeile oder Spalte, zum anderen kann auch ein ganzer Block oder können auch beliebige Zellen selektiert werden. Die Art der Selektion bestimmen Konstanten in ListSelectionModel. So wird SINGLE_SELECTION nur die Selektion einer einzigen Zelle zulassen.

    Beispiel: In einer JTable soll entweder ein ununterbrochener Block Zeilen oder Spalten ausgewählt werden dürfen:

    table.setSelectionMode( ListSelectionModel.SINGLE_INTERVAL_SELECTION );
    

    Mit Methoden lassen sich im Programm alle Elemente einer Spalte oder Zeile selektieren. Die Selektier-Erlaubnis geben zunächst zwei Methoden:

    • table.setColumnSelectionAllowed( boolean )
    • table.setRowSelectionAllowed( boolean )

    Die automatische Selektion von Spalten gelingt mit der JTable-Methode setColumnSelectionInterval(int, int), weitere Bereiche lassen sich mit addColumnSelectionInterval(int, int) hinzufügen und mit removeColumnSelectionInterval(int, int) entfernen. Das Gleiche gilt für die Methoden, die »Row« im Methodennamen tragen.

    Beispiel: Selektiere in einer JTable table die Spalte 0 komplett:

    table.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );
    table.setColumnSelectionAllowed( true );
    table.setRowSelectionAllowed( false );
    table.setColumnSelectionInterval( 0, 0 );
    

    Selektiere in einer Tabelle nur die Zelle 38, 5:

    table.setSelectionMode( ListSelectionModel.SINGLE_SELECTION  );
    table.setColumnSelectionAllowed( true );
    table.setRowSelectionAllowed( true );
    table.changeSelection( 38, 5, false, false );
    

    Als Selektionsmodus reicht SINGLE_SELECTION aus, MULTIPLE_INTERVAL_SELECTION wäre aber auch in Ordnung. Beide Selektionen sind zusammen in der Form nicht möglich. Bei einer Einzelselektion wird die Zelle nur umrandet, aber nicht wie beim Standard-Metal-Look-and-Feel blau ausgefüllt.

    Die Methode selectAll() selektiert alle Elemente, und clearSelection() löscht alle Selektionen.