2012-09-15

Ultimate Swing, Teil 22

Sie wissen ja schon, wie das bei mir ist – wenn mich das Thema Mobilty allzu zu sehr nervt, kommt ein Post zu Swing.
Zwinkerndes Smiley

Ü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;
  }
}
Java2html

Screenshot: Swing-Demo

Um dem Programm einem neuen Look zu geben, wird das gewünschte Look and Feel mittels UIManager.setLookAndFeel() gesetzt. Dies sollte so früh wie möglich auf dem EDT geschenen. In der Klasse LAFTester bietet sich deshalb die Methode run() an, und zwar vor dem Ausdruck new LAFTester().setVisible(true);.

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();
}
Java2html

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>
Java2html

Screenshot: Demo-App mit eigenem Look

Das Aussehen von Komponenten wird in Swing üblicherweise durch Kinder der Klasse javax.swing.plaf.ComponentUI bestimmt, die von den Look and Feels zur Verfügung gestellt werden. Nicht so in Synth. Synth basiert auf so genannten Regionen, die das Aussehen von (Teilen von) Komponenten festlegen. Eine Komponente kann aus einer (zum Beispiel JButton) oder mehrerer (beispielsweise JScrollBar) Regionen bestehen. 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.

No comments:

Post a Comment