2013-07-28

Unendliche Weiten

Für Mac OS X 10.8 hat Apple einige iOS-Anwendungen auf den Mac portiert. Ich hatte seinerzeit über die Notizen-App gebloggt und insbesondere die Sinnhaftigkeit des Vollbildmodus hinterfragt. Kurz gesagt glaube ich, Apple hat die Notizen für große Bildschirme schlecht umgesetzt. Mit Windows 8 hat Microsoft meiner Meinung nach ein ähnliches Problem geerbt.
Die Idee, den Bildschirm exklusiv für eine Anwendung zu reservieren (F11), ist ja nicht neu. Sie hat schon vor vielen Windows-Versionen funktioniert. Und noch früher kannten es Computernutzer überhaupt nicht anders. ;-) Das Problem ist, dass Anwendungen den zur Verfügung stehenden Platz optimal nutzen sollten, um ein angenehmes Benutzererlebnis zu erzeugen. Android-Entwickler können ein Lied davon singen. Der Aufwand, eine komplexe Benutzeroberfläche, die ursprünglich für ein Smartphone konzipiert wurde, auf ein Tablet zu portieren, kann durchaus hoch sein.
Zurück zu Windows 8. Windows style Apps (oder wie auch immer man sie derzeit nennt) arbeiten im Vollbildmodus. Was das bedeutet, ist nicht zuletzt von der Größe des Bildschirms abhängig. Ich sitze vor einem nicht besonders spektakulären 22-Zöller und tippe den Text dieses Blog-Posts in Code Writer, einer sehr schönen Windows 8-App. Im folgenden drei Screenshots.
Screenshot: Seite zum Anlegen neuer Dokumente
Screenshot: Seite zum Anlegen neuer Dokumente
Screenshot: ein Text in 22 Punkt
Screenshot: ein Text in 22 Punkt
Screenshot: Dasselbe Dokument in 14 Punkt
Screenshot: Dasselbe Dokument in 14 Punkt
Erkennen Sie das Problem?
Ich habe mit 22 Punkt eine vergleichsweise große Schriftgröße gewählt, weil es mir das Lesen erleichtert. Wer keine Sehprobleme hat, wird wahrscheinlich eher zu 14- oder 12-Punkt-Schriften greifen. Die dann entstehenden Bandwurmzeilen sind aber nicht mehr sinnvoll erfassbar. Das Arbeiten wird unnötig erschwert.
Die Lösung für das konkrete Problem ist simpel. Der Editor könnte die Funktion Nach 72 Zeichen umbrechen anbieten. Dann bleibt aber wieder viel Platz ungenutzt. Meiner Meinung nach muss Microsoft die Windows Runtime und ihre Philosophie so erweitern, dass nicht nur hohe Pixelzahlen sinnvoll unterstützt werden, sondern auch Geräte mit physikalisch großen Ausmaßen. Und, um den Kreis zu schließen, Apple natürlich auch.

2013-07-20

Ultimate Swing, Teil 26

Wenn Sie Notes and Tasks schon einmal selbst ausprobiert haben, ist Ihnen sicherlich aufgefallen, dass Größen- und Positionsänderungen des Anwendungsfensters verloren gehen, wenn Sie das Programm beenden und neu starten. Ich habe kürzlich eine aktualisierte Version in das Repository eingecheckt, die zeigt, wie leicht Java und Swing Ihnen die Speicherung (und Abfrage) von solchen Einstellungen machen.

Java enthält mit java.util.Preferences eine Klasse, die eine hierarchische (baumartige) Datenstruktur ähnlich der Windows Registrierungsdatenbank implementiert. Wo und wie Java die Preferences speichert, hängt vom Wirtssystem ab. Unter Windows wird (selbstverständlich) die Registrierungsdatenbank verwendet. Unter Mac OS X landen Einträge in XML-Dateien. Es gibt zwei separate Zweige: einen für Systemeinstellungen und einen für benutzerspezifische Daten. Üblicherweise liegen Farb- und Schrifteinstellungen sowie Positionsangaben von Fenstern im benutzerspezifischen Zweig der Preferences. Installationspfade würden Sie hingegen im Systemzweig speichern. Seine hierarchische Struktur erhält die Preferences-Datenbank durch Knoten, die sehr an Verzeichnisse erinnern. Knoten können wiederum Knoten enthalten sowie Blätter als die eigentlichen Informationsträger. Auch die Adressierung erfolgt analog zu Pfaden eines Dateisystems.

Das folgende Quelltextfragment zeigt den Einstieg in den benutzerspezifischen Teil der Preferences (hierzu wird die statische Methode userRoot() aufgerufen) sowie den Zugriff auf einen Knoten. Sein Name ist etwas länglich - er besteht aus einem voll qualifizierten Klassennamen sowie der Bildschirmbreite und Höhe.

 private Preferences getBaseNode() {  
  return Preferences.userRoot().node(callback.getClass().getName()   
                    + "_"   
           + Integer.toString(screenSize.width)   
           + "_"   
           + Integer.toString(screenSize.height));  
 }  

Nun zeige ich Ihnen, wie Sie Informationen schreiben.

 private void storeWindowState() {  
  Preferences p = getBaseNode();  
  int state = getExtendedState();  
  p.putInt(KEY_EXTENDED_STATE, state);  
  if ((state & Frame.MAXIMIZED_BOTH) == 0) {  
   Rectangle r = getBounds();  
   p.put(KEY_WINDOW_BOUNDS, convertToString(r));  
  }  
 }  

Nach dem Ermitteln das Basisknotens (die Methode getBaseNode() haben Sie schon kennen gelernt) werden mit p.putInt() und p.put() Name-Wert-Paare abgelegt.

Das Lesen funktioniert analog:

 ...  
 Preferences p = getBaseNode();  
 Rectangle r = getBounds();  
 String s = p.get(KEY_WINDOW_BOUNDS, convertToString(r));  
 r = convertToRectangle(s);  
 setBounds(r);  
 int state = p.getInt(KEY_EXTENDED_STATE, Frame.NORMAL);  
 setExtendedState(state);  
 ...  

Nach dem Ermitteln des Basisknotens greifen p.get() und p.getInt() lesend auf die Preferences zu.

Lassen Sie uns nun einen Blick auf meine Hilfsmethoden convertToString() und convertToRectangle() werden.

 private static String convertToString(Rectangle r) {  
  return Integer.toString(r.x)   
      + "," + Integer.toString(r.y)   
      + "," + Integer.toString(r.width)   
      + "," + Integer.toString(r.height);  
 }  

convertToString() wandelt ein Objekt des Typs Rectangle in eine Komma-getrennte Zeichenkette um. convertToRectangle() tut genau das Gegenteil.

 private static Rectangle convertToRectangle(String s) {  
  String[] args = s.split(",");  
  return new Rectangle(Integer.parseInt(args[0]),   
             Integer.parseInt(args[1]),   
             Integer.parseInt(args[2]),   
             Integer.parseInt(args[3]));  
 }  

Hinweis: Ich habe darauf verzichtet, ungültige Werte abzufangen; werden die Einträge in den Preferences manipuliert, würde sehr wahrscheinlich eine Ausnahme geworfen. Es steht Ihnen frei, eigene Implementierungen entsprechend zu härten.

Unter AWT und Swing werden Position und Größe eines Fensters in Objekten des Typs Rectangle gespeichert. Da die Preferences diesen Datentyp nicht (so ohne weiteres) kennen, wandeln wir ihn vor dem Speichern in einen String um. Sollen nach einem weiteren Programmstart die Position und Größe des Fensters wiederhergestellt werden, wird die Zeichenkette wieder zu einem Rectangle-Objekt umgewandelt.

Ist ihnen beim Lesen und Schreiben der extended window state aufgefallen? Mann könnte ja annehmen, dass ein Fenster genau dann seine Maximalgröße hat, wenn Breite und Höhe der Bildschirmgröße in Pixel entsprechen. In Java werden maximierte Fenster durch zwei spezielle Flags (horizontal und vertikal) gekennzeichnet. Wenn beim Beenden des Programms das Anwendungsfenster maximiert war, genügt es deshalb nicht, die Koordinaten entsprechend zu setzen. Beim nächsten Start muss der Fensterstatus explizit auf maximiert gesetzt werden. Und es gibt noch eine Besonderheit: ist ein Fenster maximiert, dürfen seine Größe und Position nicht gespeichert werden. Beim Zurückversetzen in den normalen Zustand würden nämlich nicht mehr die Koordinaten vor dem Maximieren zur Verfügung stehen. Sie wären ja mit den Maximalwerten überschrieben worden. Deshalb persistiere ich bei Veränderungen der Fenstergröße oder -position die Daten in die Preferences nur, sofern das Fenster nicht maximiert ist.

Hausaufgabe: warum enthält der Name meines Preferences-Schlüssels die Bildschirmgröße? Schreiben Sie mir…

2013-07-19

Quicktipp: Sound bei Eingang einer E-Mail (Outlook 2003)

Bevor Sie den Kopf schütteln (Was will der denn mit diesen ollen Kamellen?), lassen Sie mich kurz den Hintergrund schildern.
Zwinkerndes Smiley
Das Einstellen von Systemklängen ist unter Windows schnell erledigt. Hier ein Screenshot des entsprechenden Moduls der Systemsteuerung (unter Windows 8).
Das Modul "Sounds" der Systemsteuerung
Das Modul "Sounds" der Systemsteuerung
Um den Jingle, der beim Eintreffen einer neuen E-Mail abgespielt wird, zu ändern, würden Sie sicher (so wie ich) Benachrichtigung über neue E-Mail anklicken, oder?
Tja, nix war’s mit dem Hauptgewinn. Zumindest, wenn Sie (noch) Outlook 2003 nutzen. Dann müssen Sie nämlich offensichtlich Desktop-E-Mail-Benachrichtigung wählen. Ich schreibe offensichtlich, weil es auf dem nagelneuen Rechner meiner Frau dann funktioniert, sich zumindest mir der Sinn aber nicht erschließt.