2012-09-17

Ultimate Swing, Teil 24

Lassen Sie uns noch ein bisschen an unserem Look and Feel basteln…

In diesem Post zeige ich Ihnen, wie wir Checkboxen wieder mit ihren Kästchen und Häkchen versehen – zur Zeit fehlen diese ja noch. Der folgende Screenshot zeigt 6 Grafiken, die wir hierfür verwenden werden.

Häkchen und Kästchen im Detail

Die obere Zeile zeigt die nicht angehakte Checkbox, die untere alle Fälle, in denen das Häkchen gesetzt ist. Von links nach rechts sehen Sie “Maus nicht über der Komponente”, “Maus über der Komponente” (MOUSE OVER) und schließlich “Komponente zum Zeitpunkt des Klicks”.

Kleiner Tipp am Rande: Unter Windows kommen Sie sehr leicht zu einem hübschen Häkchen, in dem Sie in der Zeichentabelle zuerst Windings als Schriftart auswählen und danach im Eingabefeld ü drücken.

Zeichentabelle

Nun müssen wir nur noch unsere Look and Feel-XML-Datei erweitern.

<!-- Checkbox -->
<style id="checkboxStyle">
  <opaque value="TRUE" />
   
  <imageIcon id="checkbox_off" path="checkbox_off.png"/>
  <imageIcon id="checkbox_mouse_over_off" path="checkbox_mouse_over_off.png"/>
  <imageIcon id="checkbox_pressed_off" path="checkbox_pressed_off.png"/>
  <imageIcon id="checkbox_on" path="checkbox_on.png"/>
  <imageIcon id="checkbox_mouse_over_on" path="checkbox_mouse_over_on.png"/>
  <imageIcon id="checkbox_pressed_on" path="checkbox_pressed_on.png"/>
   
  <property key="CheckBox.icon" value="checkbox_off"/>
 
  <state value="PRESSED">  
    <property key="CheckBox.icon" value="checkbox_pressed_off"/>
  </state> 
  <state value="PRESSED and SELECTED">  
    <property key="CheckBox.icon" value="checkbox_pressed_on"/>
  </state> 
  <state value="SELECTED">  
    <property key="CheckBox.icon" value="checkbox_on"/>
  </state> 
  <state value="MOUSE_OVER and SELECTED">  
    <property key="CheckBox.icon" value="checkbox_mouse_over_on"/>
  </state> 
  <state value="MOUSE_OVER">  
    <property key="CheckBox.icon" value="checkbox_mouse_over_off"/>
  </state> 
   
</style>
<bind style="checkboxStyle" type="region" key="CheckBox"/>
Java2html

Die Reihenfolge der Zustandsdefinitionen ist übrigens nicht ganz beliebig. Beispielsweise darf MOUSE_OVER nicht vor SELECTED gesetzt werden, weil SELECTED ein MOUSE_OVER bedingt und demzufolge nicht zum Tragen käme.

Screenshot: fertige Checkbox

Na – sieht doch recht hübsch aus, oder?

Zwinkerndes Smiley

Zum Schluss ein kleines Rätsel. Haben Sie meine Verwendung der Farben schon durchschaut? Das geht ja vergleichsweise einfach, indem Sie die Demo einfach selbst starten…

2012-09-16

Ultimate Swing, Teil 23

Ist Ihnen aufgefallen, dass ich im XML-File meines Look and Feels den Link auf den zugehörigen Artikel in den Java Tutorials versteckt habe? Das Dateiformat hat eine eigene Beschreibung. Mein Tipp: übernehmen Sie diesen Pfad in Ihre XML-Datei, dann müssen Sie nie lange suchen.

Wenn Sie sich meine Beispiel-Datei aus dem letzten Post ansehen, stellen Sie fest, dass die meiste Arbeit innerhalb von <style />-Elementen gemacht wird. Dann gibt es noch das Element <bind />. Beide sind in <synth /> eingebettet. Wie Sie bereits wissen, unterteilt Synth Komponenten in Regionen. Das Aussehen wird auf Java-Ebene durch SynthStyles festgelegt. Diese finden ihre Entsprechung in im Element <style /> der XML-Datei. Mit <bind /> sagen Sie dem Look and Feel, auf welche Region sich eine Stildefinition beziehen soll. Fehlt eine solche Bindung, wird die entsprechende Region nicht dargestellt. Deshalb ist es bewährte Praxis, einen Basis-Stil an JEDE Region zu binden. Hier zur Erinnerung ein Ausschnitt aus meiner Beispiel-Datei:

<!-- 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=".*" />
Java2html

Auf diese Weise erhält eine Region zumindest Informationen zu Vorder- und Hintergrundfarbe sowie Schriftart und –größe. In meinem Beispiel profitieren Eingabe- und Ankreuzfelder davon – sie wurden (noch) nicht explizit deklariert.

Wenn Sie LAFTester starten und mit der Maus über eine der beiden Schaltflächen fahren, verändert sich ihre Farbe. Warum das so ist, wird deutlich, wenn wir uns nochmals den korrespondierenden Stil ansehen:

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

Mit dem <state />-Element können Sie das Aussehen von Regionen für bestimmte Zustände definieren. Um auszuprobieren, wie sich beispielsweise das Deaktivieren einer Schaltfläche auswirkt, können Sie im Quelltext der Klasse LAFTester folgendes Fragment einfügen:

p1.getComponents()[0].setEnabled(false);
Java2html

Screenshot: deaktivierte Schaltfläche

Weitere Informationen über Zustände finden Sie im Tutorial-Artikel sowie der Dateibeschreibung.

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.

2012-09-14

Medialer Wahnsinn

Dass die Vorstellung einer neuen iPhone-Version für ein gewaltiges Medienecho gesorgt hat, wird niemanden wundern. Was aber verwundern darf, ist, wie viele Mobile Computing-Spezialisten es in Bereichen des Journalismus zu geben scheint, in denen man eher Kompetenz in Sachen Politik, Kultur und Wissenschaft erwarten würde.

Ganz offen gesagt finde ich es erstaunlich, wie selbstverständlich die etablierten Printmedien und ihre Online-Ausgaben ihre Enttäuschung über (tatsächlich vorhandene oder gefühlte) mangelnde Innovationen zum Besten geben. Schuster, bleib bei deinen Leisten”, möchte man in den Blätterwald hinein rufen… Der Markt mobiler Kommunikation ist unglaublich komplex und lässt sich nicht mit wenigen Zeilen erklären. …und ist auch nicht erst mit der Ankündigung des ersten Apfel-Telefons entstanden.

Bitte, bitte, überlasst die Bewertung doch Fachpublikationen.

2012-09-10

Lokale Webseiten im Android Emulator anzeigen

Kennen Sie das – da sagt jemand etwas, und Sie verspüren den unbändigen Drang, in die Tischplatte zu beißen…?
Zwinkerndes Smiley
In einem – sonst sehr guten – Talk war die Verwunderung des Vortragenden groß, dass man im Browser des Android-Emulators nicht einfach auf localhost (oder 127.0.0.1) surfen kann, um eine lokale (wohlgemerkt, auf dem Wirtsrechner, nicht dem Emulator!) Webseite anzuzeigen. Mit dem iPhone ginge das ja schließlich auch…
Wir erinnern uns (nach heftigen Kratzen am Kopf), dass Android ein Linux-System ist und demzufolge localhost niemals das Wirtssystem des Emulators meinen sollte.
Screenshot: nicht erreichbare Seite
Wie einfach es geht, verrät ein Blick in die Doku. Dort steht:
If you want to access services running on your development machine's loopback interface (a.k.a. 127.0.0.1 on your machine), you should use the special address 10.0.2.2 instead.
Screenshot: lokale Webseite
P. S. Wir alle verrennen uns gelegentlich, insofern ist meine Verwunderung natürlich nur gespielt. …zumal sie einen schönen Plot für den Post geliefert hat…
Zwinkerndes Smiley

2012-09-09

Unnötiger Rückbau

Bis einschließlich Mac OS X 10.7 durfte man in den Systemeinstellungen das Personal Web Sharing aktivieren. Damit stand ein lokaler Web Server zur Verfügung, mit dem man sehr schön Websites testen konnte. Unglücklicherweise hat Apple diese Option in Mountain Lion entfernt. Im Netz findet man aber natürlich Tipps, wie man die verloren gegangene Funktion reaktivieren kann. Denn der eigentliche Apache ist sehr wohl noch da. Findige Entwickler haben sogar ein eigenes Modul für die Systemeinstellungen zur Verfügung gestellt.