Sie wissen ja schon, wie das bei mir ist – wenn mich das Thema Mobilty allzu zu sehr nervt, kommt ein Post zu Swing. Über austauschbare Look and Feels habe ich schon mehrfach gebloggt, zuletzt ging es um TKPLAFUtility. Heute (und sehr wahrscheinlich weiteren Folgen) beschäftige ich mich mit Synth, Swings Look and Feel-Baukasten. Ich möchte Synth nutzen, um ein Windows Phone und Windows 8 nachempfundenes Look and Feel zu realisieren.

Disclaimer: Die Definition und Umsetzung einer Designsprache ist alles andere als trivial. Look and Feels bestehen aus weit mehr als ein paar vordefinierten Farben und Formen. Insofern erhebt meine Version keinen Anspruch auf Vollständigkeit oder Konsistenz.

Natürlich soll mein Look and Feel später in Notes and Tasks eingesetzt werden. Für die Erprobung verwende ich aber ein rudimentäres Rumpfprogramm:

package com.thomaskuenneth.shoebox.swing;

import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class LAFTester extends JFrame {
  private LAFTester() {
    super("LAFTester");
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setContentPane(createUI());
    pack();
    setLocationRelativeTo(null);
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        new LAFTester().setVisible(true);
      }
    });
  }

  private JComponent createUI() {
    JPanel p1 = new JPanel(new FlowLayout());
    p1.add(new JButton("Laden"));
    p1.add(new JButton("Speichern"));
    p1.add(new JCheckBox("eine Checkbox"));
    p1.add(new JTextField("ein Eingabefeld"));
    return p1;
  }
}
SynthLookAndFeel lookAndFeel = new SynthLookAndFeel();
try {
  lookAndFeel.load(
      LAFTester.class.getResourceAsStream("laf.xml"),
      LAFTester.class);
  UIManager.setLookAndFeel(lookAndFeel);
} catch (UnsupportedLookAndFeelException e) {
  // FIXME: richtig loggen
  e.printStackTrace();
} catch (ParseException e) {
  // FIXME: richtig loggen
  e.printStackTrace();
}

Was passiert in diesem Quelltextfragment? Nach dem Instantiieren eines Objekts vom Typ SynthLookAndFeel wird dessen Methode load() aufgerufen. Synth-basierende Look and Feels können ihr Aussehen entweder deklarativ oder programmatisch erhalten. In meinem Beispiel wird es in der Datei laf.xml definiert. Diese muss sich im selben Paket wie die Klasse LAFTester befinden:

<!-- http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/synth.html -->
<synth>
  <!-- Default-Style -->
  <style id="backingStyle">
    <opaque value="TRUE" />
    <font name="Verdana" size="16"/>
    <state>
      <color type="BACKGROUND" value="#f8f8f8" />
      <color type="FOREGROUND" value="#ffa000" />
    </state>
  </style>
  <bind style="backingStyle" type="region" key=".*" />
  <!-- Buttons -->
  <style id="buttonStyle">
    <opaque value="TRUE" />
    <insets top="10" left="10" bottom="10" right="10" />
    <state>
      <color type="FOREGROUND" value="WHITE" />
      <color type="BACKGROUND" value="#808080" />
    </state>
    <state value="PRESSED">
      <color type="BACKGROUND" value="BLACK" />
    </state>
    <state value="MOUSE_OVER">
      <color type="BACKGROUND" value="#505050" />
    </state>
    <state value="DISABLED">
      <color type="BACKGROUND" value="#b0b0b0" />
    </state>
  </style>
  <bind style="buttonStyle" type="region" key="Button"/>
</synth>

Welche Regions es gibt, ist in der gleichnamigen Klasse definiert. Alle ComponentUIs von Synth assoziieren mit jeder dieser Regionen einen SynthStyle. Dieser enthält unter anderem Informationen bzgl. Schriftart sowie Vorder- und Hintergrundfarbe. Für das eigentliche Zeichnen sind SynthPainter verantwortlich. Wie aber entstehen SynthStyles? ComponentUIs in Synth erhalten SynthStyles von einer SynthStyleFactory. Diese kann auf zweierlei Weise erzeugt werden. Die erste Möglichkeit kennen Sie bereits: mit Hilfe einer XML-Datei. Wie das Laden funktioniert, habe ich Ihnen weiter oben gezeigt. Die zweite Möglichkeit besteht im programmatischen Erzeugen. Hierzu leiten Sie von SynthStyleFactory ab und implementieren die Methode getStyle(). Mit SynthLookAndFeel.setStyleFactory() wird die Factory aktiviert. Im nächsten Teil sehen wir uns den Aufbau der XML-Datei genauer an.


This is a (slightly updated) repost of a piece I published on my blog Tommi’s Blog. I deleted the blog in the wake of the GDPR, so the original version is no longer available, or only through the WayBack Machine of the Internet Archive. Please note: code usually has not been updated, so language feature reflect the time the original post was written.