2013-12-26

Frohe Weihnachten und alles Gute für das Jahr 2014

Frohe Weihnachten und alles Gute für das Jahr 2014
Ich wünsche den Besucherinnen und Besuchern meines Weblogs frohe Weihnachten und alles Gute für das neue Jahr 2014.
Das nun zu Ende gehende 2013 hat Zeit für den einen oder anderen Post gelassen, gerne hätte ich mehr geschrieben. Für alle Kommentare bedanke ich mich herzlich. Ich freue mich auch weiterhin über Anregungen, Lob und Tadel.
2014 wird für den Gadget-Junkie sicher wieder interessant. Bald schon (7. bis 10. Januar) öffnet die CES in Las Vegas ihre Pforten. Und einen Monat später (24. bis 27. Februar) ist Barcelona wieder Gastgeberin für den Mobile World Congress. Ich bin mir sicher, wieder wird alles schneller, leichter, besser, größer…
Wobei – vielleicht sollten die Smartphones wieder etwas schrumpfen…? Jedenfalls bin ich mir sicher, dass Apple sich dem Trend zu größeren Bildschirmen nicht verschließen wird und uns ein iPhone XL vorstellen wird. Ob und wann es aus Cupertino die herbei gesehnte Smart Watch geben wird, muss sich zeigen. Auch, ob sich der Markt dann mehr dafür interessiert.
Smiley
Für mich persönlich beginnt im März ein neuer beruflicher Abschnitt. Nach über zehn Jahren im öffentlichen Dienst freue ich mich auf neue Einsichten, neue Projekte und neue Herausforderungen.

2013-12-21

Lektüretipp: Windows Phone 8 Guide for Android Application Developers

Durch Zufall bin ich über eine interessante Lektüre gestolpert. Der Windows Phone 8 Guide for Android Application Developers möchte Android-Entwicklern die Portierung ihrer Apps für Windows Phone schmackhaft machen. Selbst wenn man eine Portierung nicht im Sinn hat, ist es doch ein lesenswerter Blick über den Tellerrand. Hier noch ein passender Blog-Post

2013-12-15

Android-Tipp: Roman Nurik’s Undo Bar

Android kennt schon sehr lange kleine Infotäfelchen (Toasts), die den Anwender über einen bestimmten Sachverhalt informieren. Beispielsweise kann eine App auf diese Weise den erfolgreichen Abschluss einer Dateiübertragung oder Speichervorgangs anzeigen. Ausführliche Hinweise hierzu enthält der Abschnitt Confirming & Acknowledging auf Googles Android Design. Die dort ebenfalls beschriebene Undo Bar ist derzeit nicht Teil der Plattform. Wie man das Muster umsetzen kann, zeigt Roman Nurik mit einer Beispiel-App, die man sich hier ziehen kann. In Aktion sieht das Ganze dann so aus:
Roman Nurik’s Undo Bar
Roman Nurik’s Undo Bar

2013-12-14

Visual Studio 2013 Express für Windows

Langjährige Leser meines Blogs wissen, dass ich gerne über den Tellerrand schaue. Deshalb habe ich immer wieder über Visual Studio, .Net und Co. berichtet. Zur Zeit ist das Ökosystem aus Redmond ziemlich in Bewegung. Wohin entwickeln sich Windows 8(.x), Windows RT und Windows Phone? Verschmelzen die APIs der unterschiedlichen Linien? Vieles deutet darauf hin, dass es zu einer Harmonisierung kommt. Dass es bei der Unterstützung so unterschiedlicher Geräte wie Smartphones und Tablets viele Fallstricke gibt, wissen alle, die schon Android-Apps gebaut haben. Nimmt man Windows und Windows Phone, wird es sicher vieles geben, das man vereinheitlichen kann. Die UI wird aber immer Rücksicht auf Anforderungen der Hardware nehmen müssen.
Aber ich schweife ab. Da ich schon immer mal eine Windows Store-App schreiben wollte, habe ich mir Visual Studio 2013 Express für Windows installiert. Hier die Stationen bis zum Anlegen des Projekts.
Installation von Visual Studio 2013 Express für Windows
Installation von Visual Studio 2013 Express für Windows
Abschluss der Installation
Abschluss der Installation
Bei Visual Studio anmelden
Bei Visual Studio anmelden
Anmeldevorgang
Anmeldevorgang
Hinweis auf Entwicklerlizenz
Hinweis auf Entwicklerlizenz
Entwicklerlizenz erwerben
Entwicklerlizenz erwerben
Eine befristet gültige Entwicklerlizenz
Eine befristet gültige Entwicklerlizenz
Finally there - endlich am Ziel
Finally there - endlich am Ziel
Naja, am Ziel bin ich natürlich noch nicht. Schließlich muss noch das Projekt angelegt werden. Aber die Installation und Einrichtung ist zumindest abgeschlossen. Woran ich mich definitiv gewöhnen muss, ist dieser “düstere” Look. Scheint sich ja auf breiter Front etabliert zu haben. Man kennt es von vielen kommerziellen Programmen. Und auch Android Studio hat ihn.
Anlegen des Projekts
Anlegen des Projekts
Ich habe mich für eine Leere App entschieden, um möglichst viel selbst ausprobieren (und falsch machen) zu können. Was ich basteln möchte, können Sie am Projektnamen leicht erkennen.
Smiley
Eine Datumsauswahl und ein Textfeld sind schnell auf die Hauptseite gezogen. Auch die Methode, die bei Änderungen des Datums aufgerufen wird, ist zwei Mausklicks entfernt. Das Ermitteln der Referenz auf die Textkomponente ist intuitiv erledigt, sofern man schon auf anderen Plattformen Oberflächen in XML-Dateien erstellt hat.
Datumsauswahl und Textfeld
Datumsauswahl und Textfeld
Ein paar Zeilen Quelltext
Ein paar Zeilen Quelltext
Spätestens hier sind natürlich ein paar Fragen zu stellen… Wo findet die Ermittlung der Referenz am besten statt? Werden die Namen der Objekte irgendwo als Konstanten hinterlegt?
Fest steht: die Arbeit mit Visual Studio Express macht Spaß. Das Bauen einer App geht leicht von der Hand. Ich bin schon gespannt, wann ich mein Progrämmchen bei Microsoft einreichen kann.
Zwinkerndes Smiley

Ein wenig Transparenz

Mit Android 4.4 hält ein neuer Launcher Einzug in die Plattform - zumindest auf dem Nexus 5. Er nutzt die Möglichkeit, Statuszeile und Navigationsschaltflächen halbdurchsichtig darzustellen. Auch der neue Lockscreen tut dies. In der ersten Auslieferung ebenfalls nur auf dem Nexus 5. Zumindest für diesen hat Google nun die Transparenz allen Android 4.4-Nutzern geschenkt. Hier zwei Bilder zum Vergleich, Android 4.4 und 4.4.2 auf einem Nexus 4.
Lockscreen unter Android 4.4
Lockscreen unter Android 4.4
Lockscreen unter Android 4.4.2
Lockscreen unter Android 4.4.2
Ob diese Neuerung schon unter 4.4.1 vorhanden war, müsste ich im Netz recherchieren. Mein Nexus 4 hat gleich den Sprung von 4.4 auf 4.4.2 gemacht. Die Nutzung semitransparenter Navigationsschaltflächen und Statuszeile ist auf stackoverflow schön zusammen gefasst.
P.S. Ich gebe zu, dass der Effekt bei dem von mir eingestellten Hintergrund nicht besonders gut zu sehen ist. Hört man Musik, zeigt der Lockscreen aber fullscreen-cover art. DAS sieht richtig cool aus.

2013-11-26

Erster Blick auf den Launcher des FairPhone

Die Macher des FairPhone haben auf facebook bekannt gegeben, dass sie Teile der Systemsoftware auf GitHub veröffentlicht haben. Ich war natürlich neugierig… Hier ein paar Screenshots des Launchers zur Einstimmung…
Screenshot des FairPhone-Launchers (1/4)
Screenshot des FairPhone-Launchers (2/4)
Screenshot des FairPhone-Launchers (3/4)
Screenshot des FairPhone-Launchers (4/4)
Das Peace of Mind-Widget werde ich mir demnächst ansehen…

2013-11-07

Gruß aus der Vergangenheit – jasconn ist wieder da!

Vor vielen Jahren habe ich auf java.net das Projekt jasconn gepflegt. Der seltsam klingende Name steht für Java-AppleScript-Connector. Damit war es möglich, JSR-223-konform auf AppleScript zuzugreifen. Im Zuge der java.net-Migration auf Project Kenai (auch dies ist viele Jahre her) ist jasconn dann verloren gegangen – ich hatte eine Migrationsfrist verstreichen lassen. Neulich habe ich mein altes Projekt selbst gebraucht und gedacht, dass es schade ist, wenn niemand mehr etwas davon hat und weiß. Deshalb habe ich es mit freundlicher Unterstützung der java.net-Admins neu angelegt: https://java.net/projects/jasconn
Wozu ist jassconn nun gut? Wie bereits angedeutet, kann man damit aus Java heraus AppleScript-Code ausführen und das Ergebnis in Java weiter verarbeiten. Das kann so aussehen:
 public class ScriptDemo2 {  
   public static void main(String[] args) {  
     ScriptEngineManager manager = new ScriptEngineManager();  
     List<ScriptEngineFactory> factories = manager.getEngineFactories();  
     for (ScriptEngineFactory factory : factories) {  
       if (factory.getEngineName().indexOf("Java-AppleScript-Connector") != -1) {  
         ScriptEngine engine = factory.getScriptEngine();  
         String script = "tell application \"System Events\"\nitem 1 of (get name of processes whose frontmost is true)\nend tell";  
         try {  
           Object result = engine.eval(script);  
           if (result != null) {  
             System.out.println(result.toString());  
           }  
         } catch (ScriptException ex) {  
           Logger.getLogger(ScriptDemo2.class.getName()).log(Level.SEVERE, "engine.eval", ex);  
         }  
         break;  
       }  
     }  
   }  
 }  
Dieses Quelltextfragment nutzt praktisch nur Klassen, die in JSR-223 spezifiziert sind. Der Bezug zu jasconn ergibt sich ausschließlich aus der Suche nach einer bestimmten Factory. Die auszuführenden AppleScript-Befehle sehen so aus:

tell application "System Events"
item 1 of (get name of processes whose frontmost is true)
end tell
Auf diese Weise kann der Name der aktiven Anwendung ermittelt werden.
Es liegt auf der Hand, dass jasconn nur unter Mac OS X läuft. Denn jasconn ist kein AppleScript-Interpreter, sondern nur eine Brücke. Das Subversion-Repository sowie ein fertiges Jar sind über die Projekthomepage erreichbar. Die Nutzung ist denkbar einfach: das Jar muss nur unter /Library/Java/Extensions abgelegt werden.
Ein letzter Hinweis: Irgendwann hat Apple in seine Java-Versionen ebenfalls eine JSR-223-kompatible AppleScript-Integration eingebaut. Damit ist jasconn eigentlich nicht mehr nötig. Warum meine Version dennoch die bessere Wahl sein kann, zeige ich in einem späteren Post.

2013-11-02

TKWeek 1.6.6

Heute ist mal wieder ein Screenshot von TKWeek fällig. Ich habe nämlich eine neue Version veröffentlicht, die das Modul Informationen über ein Jahr um eine Wochentagsverteilung erweitert.

Screenshot von TKWeek: Informationen über ein Jahr mit Wochentagsverteilung
Screenshot von TKWeek: Informationen über ein Jahr mit Wochentagsverteilung

Ich weiß nicht, ob Sie sich jemals gefragt haben, wie viele Donnerstage oder Freitage ein bestimmter Monat hat. Meine App kann diesen Wissensdurst jedenfalls stillen.

Zwinkerndes Smiley

2013-10-17

Android-Tipp: auf Telefonie prüfen

Manchmal kann es nützlich sein, auf das Vorhandensein oder Fehlen einer bestimmten Funktion zu prüfen. Der folgende Zweizeiler liefert true, wenn Telefoniefunktionen verfügbar sind.
 PackageManager pm = getPackageManager();  
 boolean hasTelephony   
   = pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);  
Android-typisch gibt es aber ein kleines caveat: der Android-Emulator liefert in der Grundeinstellung false

2013-10-13

Kenne Dein API–die praktische Hilfsklasse Pair

Die Klassenbibliothek von Android ist im Laufe der Jahre ziemlich umfangreich geworden. Viele praktische Helfer könnten den Programmieralltag erleichtern. Aber nicht jede Klasse wird oft genug eingesetzt. Ein Beispiel ist Pair.Sie gibt es schon seit der ersten Ausgabe von Eclair (API Level 5). Das war Ende 2009. Grund genug, sie mit ein paar Sätzen zu würdigen.
Pair kombiniert zwei Werte zu einem Tupel. Deren Datentypen können unterschiedlich sein. Der Zugriff erfolgt über die Variablen first und second.
 Pair<String, Date> p1 = new Pair<String, Date>("heute", new Date());  
 Pair<String, Date> p2 = Pair.create("jetzt", p1.second);  
 System.out.println(p1.first);  
 System.out.println(p2.first);  
 System.out.println(p1.equals(p2));  
Paare kommen recht häufig vor. Denken Sie an Koordinaten, Größenangaben oder Seitenverhältnissen. Aber auch für jede Form von Schlüssel-Wert-Kombinationen kann diese Klasse ein praktischer Container sein. Die sie schon mit API Level 5 eingeführt wurde, dürfte ihre Verwendung kaum noch ein Problem darstellen.

2013-09-17

Android Quicktipp: Abschnittsüberschriften

In TKWeek gibt es die so genannte Tagesübersicht, die alle entgangenen Anrufe, Aufgaben, Ereignisse und Termine übersichtlich darstellt.Screenshot: Tagesübersicht in TKWeek
Screenshot: Tagesübersicht in TKWeek

Bislang habe ich die Abschnittsüberschriften sehr einfach gestaltet:

 android:textAppearance="?android:attr/textAppearanceMedium"  

Mit ganz wenig Aufwand kann man das nicht nur aufpeppen, sondern standardkonform machen.

Screenshot: TKWeek mit "echten" Abschnittsüberschriften
Screenshot: TKWeek mit "echten" Abschnittsüberschriften

Sieht schön aus, oder? Und funktioniert mit nur einer Zeile:

 style="?android:attr/listSeparatorTextViewStyle"

Lassen Sie sich übrigens nicht von dem “no” stören. Das war noch ein Relikt und wurde mittlerweile entfernt.

2013-09-03

Paukenschlag

Wäre heute der 1. April, hätte man diese heise.de-Meldung (vor dem Update um 21:15) als gelungenen Aprilscherz beklatscht. Nun ist aber heute der dritte September. Kaum hat man die Schlagzeile gelesen, ist die Marketingmaschinerie auch schon in vollem Gange.

Es wird (wahrscheinlich recht bald) eine neue Android-Version 4.4 geben, die nicht Key Lime Pie heißt, sondern KitKat. Unter www.android.com/kitkat/ werden alle bisher erschienenen (und natürlich die angekündigte neue) Android-Versionen mit ihrem Süßspeisen-Maskottchen gezeigt.

Screenshot von www.android.com/kitkat/

Außerdem kann man sich mit seiner E-Mail-Adresse registrieren, um beim Erscheinen des Releases informiert zu werden.

Bemerkenswert finde ich die angekündigte enge Verzahnung mit Nestlé bzgl. Marketing. Es soll Verlosungen von Nexus Hardware, von Geschenkkarten für den Play Store sowie von exklusiven KitKat-Riegeln in Form eines Android-Roboters geben. Wer von wem nun mehr profitiert, weiß ich nicht. Cool und erfrischend unerwartet ist das Ganze allemal.

2013-08-28

Unboxing Android Mini Collectible Summer Ed. 13

Bevor der Sommer so ganz langsam zu Ende geht, muss ich doch unbedingt noch ein Unboxing nachschieben; die kleinen Freunde wohnen nun schon seit geraumer Zeit bei Moni und mir…

Android Mini Collectible Summer Ed. 13 (1)Android Mini Collectible Summer Ed. 13 (2)Android Mini Collectible Summer Ed. 13 (3)Android Mini Collectible Summer Ed. 13 (4)Android Mini Collectible Summer Ed. 13 (5)Android Mini Collectible Summer Ed. 13 (6)

2013-08-24

Ultimate Swing, Teil 29

Ich habe in letzter Zeit ein paar Verbesserungen an Notes and Tasks und der Schuhschachtel (Sie erinnern sich vielleicht: ich fasse potenziell wiederverwendbare Teile zu diesem Mini-Framework"chen" zusammen) vorgenommen. Neu ist beispielsweise das Interface IController. Es enthält die Methoden setup(), getModel() und getView(). Die beiden letztgenannten sind praktisch, weil übergeordnete Controller nicht für jedes Modell und jede Sicht untergeordneter Controller eigene Referenzen halten müssen. Die Klasse MainController von Notes and Tasks nutzt dies. Sie ist übrigens kein IController, da sie ja schon das Interface ApplicationCallback implementiert und damit den Lebenszyklus der Anwendung steuert. Natürlich ist auch das eine Art Controller. Die Methode setup() von IController-Implementierungen wird auf dem EDT aufgerufen. Hier können Bedienelemente mit Werten aus dem Modell befüllt oder Einstellungen aus den Preferences übernommen werden.

Eine weitere Ergänzung betrifft Drag and Drop. Wenn Sie eine Textdatei auf das Hauptfenster von Notes and Tasks schieben, wird diese zeilenweise eingelesen und jede Zeile erscheint als neue Aufgabe. Hierzu habe ich das Interface ApplicationCallback um die Methode fileDropped() erweitert. Für jede fallen gelassene Datei wird sie einmal aufgerufen. Die Implementierung der Drag and Drop-Funktionalität ist sehr einfach: In der Schuhschachtel-Klasse Application gibt es die Methode configureFrame(). Sie wird um wenige Zeilen Code erweitert.

 // drag and drop support  
 setTransferHandler(new TransferHandler() {  
     
   @Override  
   public boolean canImport(TransferHandler.TransferSupport support) {  
      // support.setDropAction(COPY); ??  
      return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor);  
   }  
   
   @Override  
   public boolean importData(TransferHandler.TransferSupport support) {  
      boolean success = false;  
      if (canImport(support)) {  
        final Transferable t = support.getTransferable();  
        try {  
           final List<File> l =  
                (List<File>) t.getTransferData(DataFlavor.javaFileListFlavor);  
           for (File f : l) {  
              INSTANCE.callback.fileDropped(f);  
           }  
           success = true;  
        } catch (UnsupportedFlavorException e) {  
           LOGGER.throwing(getClass().getName(), "importData", e);  
        } catch (IOException e) {  
           LOGGER.throwing(getClass().getName(), "importData", e);  
        }  
      }  
      return success;  
   }  
 });  
   

Letztlich wird nur ein Objekt des Typs TransferHandler erzeugt und der Methode setTransferHandler() des Anwendungshauptfensters übergeben.

Die Implementierung der Leseroutine in Notes and Tasks (im MainController) ist schon fertig. Bauen Sie doch einmal eine Testdatei und ziehen diese auf das Hauptfenster. Einige Aufgaben werden angelegt, aber nicht alle. Sind Sie tief genug mit dem Quelltext vertraut, um herauszufinden, was da schief geht? Das Problem liegt in einer Methode, die in fileDropped() aufgerufen wird.

2013-08-21

Berechtigungen erben

Nutzer von Android-Apps sind leider oft leichtfertig, wenn es um das Abnicken von Berechtigungen geht. Umso mehr freut es mich, wenn Anwender nachfragen.

Ein Nutzer meiner App TKBirthdayReminder wollte wissen, warum ich lesend und schreibend auf das Anrufprotokoll zugreife. Ich war erstaunt. Mir war nämlich nicht bewusst, dies zu tun. Ein Blick in das Manifest offenbarte:

READ_CONTACTS, WRITE_CONTACTS, CALL_PHONE, RECEIVE_BOOT_COMPLETED, WAKE_LOCK, GET_ACCOUNTS und READ_SYNC_SETTINGS

Also kein Hinweis auf das Anrufprotokoll.

Nach etwas nicht allzu zielführender Recherche habe ich dann die Google-Doku konsultiert. Unter http://developer.android.com/reference/android/Manifest.permission.html#READ_CALL_LOG und http://developer.android.com/reference/android/Manifest.permission.html#WRITE_CALL_LOG steht sinngemäß: Wenn man die Berechtigungen "Kontakte lesen" und "Kontakte schreiben" anfordert (das muss TKBirthdayReminder tun, um Geburtsdaten lesen und schreiben zu können), “erbt” die App in älteren Systemversionen automatisch auch den lesenden bzw. schreibenden Zugriff auf die Anrufhistorie.

Googles Rat für alle, die die Rechte nicht benötigen: targetSdkVersion auf 16 oder höher setzen

2013-08-06

Ultimate Swing, Teil 28

Im vorherigen Post habe ich eine kleine Baustelle hinterlassen: Radiobuttons und Checkboxen haben eine Hintergrundfarbe, die zumindest unter bestimmten Look and Feels nicht zum Hintergrund von Registerkarten passt. Dieses Problem tritt auf, wenn die Komponenten Teil eines JPanels sind, für das mit setOpaque(false) der Hintergrund ausgeschaltet wurde. Das wiederum ist nötig, weil die Panels sonst (wieder nur unter bestimmten Look and Feels) den Hintergrund der Registerkarten verdecken.

Die Lösung ist einfach. Auch für die betroffenen Komponenten muss setOpaque(false) aufgerufen werden. Übrigens ist das Ganze nicht neu, wie ein Post auf code beach zeigt. Auch auf java.net gab es einen recht interessanten Thread.

Screenshot: Notes and Tasks im Metal-Look
Screenshot: Notes and Tasks im Metal-Look

Unter Windows 8 und dem guten alten Metal ist nun alles in Ordnung. Lassen Sie uns ausprobieren, wie sich Nimbus schlägt. Hierzu müssen wir nur Folgendes auf der Kommandozeile angeben:

-Dswing.defaultlaf=javax.swing.plaf.nimbus.NimbusLookAndFeel 

Screenshot: Notes and Tasks mit Nimbus-Look and Feel
Screenshot: Notes and Tasks mit Nimbus-Look and Feel

Sieht irgendwie komisch aus, oder? Lauert etwa schon der nächste Stolperstein auf uns? Der Trenner wird nicht gezeichnet, weil ich in meinem Quelltext beim Instantiieren einer JTabbedPane die so genannte tabLayoutPolicy auf SCROLL_TAB_LAYOUT gesetzt hatte. Mit diesem Parameter steuern Sie das Verhalten einer Tabbed Pane, wenn nicht alle Register gleichzeitig dargestellt werden können. Spätestens nach der Lektüre des Eintrags zu Fehler 7031915 wissen wir, dass Nimbus SCROLL_TAB_LAYOUT nicht implementiert. Zum Glück ist das Problem aber schnell behoben. Mit WRAP_TAB_LAYOUT klappt das Zeichnen. ...naja, zumindest halbwegs...

Screenshot: Verbesserte Version
Screenshot: Verbesserte Version

Wahrscheinlich fragen Sie sich wie ich, warum auf dem Screenshot kein Rahmen um die Tabbed Pane gezeichnet wird. Auch hierzu gibt es einen Eintrag in der Fehlerdatenbank. Unglücklicherweise harrt Fehler 6726247 aber noch einer Aufarbeitung. Und das wird sicher auch so bleiben.

Ein letzter Versuch, diesmal mit dem Windows Look and Feel. In diesem Fall geben Sie auf der Kommandozeile Folgendes an:

-Dswing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel

Screenshot: Notes and Tasks mit dem Windows Look and Feel
Screenshot: Notes and Tasks mit dem Windows Look and Feel

Diesmal habe ich nichts auszusetzen. Und was sagen Sie?

2013-08-04

Ultimate Swing, Teil 27

Heute möchte ich nicht viel über Programmierung erzählen, sondern ein paar Worte zum Thema Ergonomie sagen. Bitte sehen Sie sich den folgenden Screenshot etwas genauer an:
Screenshot: TKNotesAndTasks
Screenshot: TKNotesAndTasks
Auf den ersten Blick scheinen nur ein paar Details zu stören:
  • Die Hintergrundfarbe der Registerkarte ist uneinheitlich.
  • Der als Hinweis (hint) gedachte Text in der Eingabezeile am oberen Rand sieht aus, als wäre er vom Benutzer eingegeben worden.
  • Die erste Schaltfläche im rechten Bereich schließt nicht bündig mit dem oberen Rand der Liste ab.
Sozusagen Peanuts, oder?
Wenn Sie sich Notes and Tasks schon einmal live angesehen haben, wissen Sie, dass die Eingabezeile auf beide Registerkarten wirkt. Mit ihr können also Aufgaben und Kategorien eingegeben werden. Beim Umschalten wurde der Hinweistext sogar getauscht, damit der Anwender sofort sieht, was er im Begriff ist, einzutippen. Die Erwartung war, auf diese Weise die Eingabe so effizient wie möglich zu gestalten. Clever, oder?
Nein, nicht clever. Mit meiner Implementierung zwinge ich den Anwender nicht nur, mit den Augen zwischen Eingabezeile und Registerkarten zu pendeln, sondern erwarte zudem, dass er den langen Hinweistext liest. Wenn er dann feststellt, sich auf der “falschen” Registerkarte befindet, muss er mit den Augen (und der Maus) wieder pendeln. Die Idee, eine Eingabezeile für alle Aspekte der Anwendung (Aufgaben, Kategorien und später noch Notizen) zu bieten, ist also nicht ausgereift. Das richtige Vorgehen ist, den Anwender zuerst entscheiden zu lassen, mit welchem Teil der Anwendung er arbeiten möchte. Deshalb müssen die Reiter der Registerkarten ganz oben erscheinen. Denn wie wir die Seite eines Buches von links oben nach rechts unten abarbeiten, so betrachten wir auch den Bildschirm und damit die Benutzeroberfläche eines Programms.
Screenshot: Notes and Tasks mit verbesserten Layout
Screenshot: Notes and Tasks mit verbesserten Layout
Das sieht doch schon besser aus, oder? Der Benutzer kann neue Aufgaben anlegen und sieht diese nach dem Drücken der Enter-Taste in der Liste. Also alles in Butter?
Nehmen wir an, Sie möchten den Text einer Aufgabe bearbeiten. Die Idee könnte sein, hierfür die Eingabezeile zu “recyclen”. Bloß dann haben Sie das Problem mit der Pendelei wieder. Außerdem ist es fraglich, ob man häufiger neue Aufgaben erfasst oder den Status bestehender Aufgaben ändert. Ich habe mich deshalb zu folgendem Layout entschlossen:
Screenshot: Eingabezeile im unteren Randbereich
Screenshot: Eingabezeile im unteren Randbereich
Durch das Verschieben in den unteren Bereich sorge ich nicht nur für eine leichte Auffindbarkeit der Eingabezeile, sondern schaffe auch die richtige Gewichtung der Teilbereiche. Damit “funktioniert” sogar das Bearbeiten eines Aufgabentextes in der Eingabezeile, nachdem der Benutzer den Eintrag in der Liste angeklickt hat.
Zum Schluss noch zwei technische Details. Die “falsche” Panelhintergrundfarbe kann ich korrigieren, indem ich für Panels, die auf Registerkarten liegen, mit setOpaque(false) das Zeichnen des Hintergrunds unterbinde. Das ist im folgenden Screenshot zu sehen. Der leichte Versatz der Schaltflächen scheint ein Mac OS X-spezifisches Verhalten von Swing zu sein.
Screenshot: Wo kommen die Ränder her?
Screenshot: Wo kommen die Ränder her?
Denn unter Windows tritt der Versatz nicht auf:
Screenshot: TKNotes unter Windows
Screenshot: TKNotes unter Windows
Blöd nur, dass jetzt der Hintergrund der Checkboxen und RadioButtons nicht passt.
Oh, ich liebe Swing…
Zwinkerndes Smiley

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.

2013-06-24

Ultimate Swing, Teil 25

Erinnern Sie sich noch an meine lose Folge von Posts zu Swing? Nach einer längeren Pause möchte ich den Faden wieder aufnehmen und in den folgenden Monaten den einen oder anderen Kniff zum Besten geben. Außerdem gibt es ja noch das im Rahmen der Serie begonnene Notes and Tasks, das schon in seinem frühen Stadium zu schön ist, um es nicht zu vollenden. Die Quelltexte habe ich in ein Subversion-Repository gepackt, auf das Sie über die Projekthomepage von Notes and Tasks zugreifen können.

Sie erinnern sich vielleicht, dass sich die letzten Folgen um Look and Feels gedreht haben. Ich habe auf Basis von Synth einige Komponenten so gestaltet, dass sie an den (schon länger nicht mehr so genannten) Metro-Look von Microsoft erinnern. Mir ging und geht es dabei nicht darum, eine möglichst genaue Kopie anzufertigen. Eher ist es eine Verneigung vor dem Mut Redmonds, diesen radikalen Neubeginn zu wagen. Sicher gibt es an Windows 8 noch das eine oder andere zu drehen, aber die UI ist wegweisend.

In diesem Post möchte ich mein Look and Feel, das ich übrigens UTLAF (raten Sie doch einmal, wofür UT steht) getauft habe, um die Fähigkeit erweitern, Radio buttons zu rendern. Auch Windows Store apps kennen Radio buttons. Sie sind wie eh und je rund. Diese Seite gibt einen schönen Überblick über die so genannten selection controls, die Windows Store apps zur Verfügung stehen.

UTLAF wird mit der langen Tradition runder Radio buttons brechen. Wie die korrespondierenden Grafiken aussehen, ist im folgenden Screenshot dargestellt. Ich greife dabei das von mir schon mehrfach verwendete Orange (ffa000) wieder auf.

Screenshot: die Grafiken für Radio buttons
Screenshot: die Grafiken für Radio buttons

Wenn Sie wie ich ein Kind der 70er sind, hegen Sie an diese Farbe wahrscheinlich ähnliche Erinnerungen.

Smiley

Um die Grafiken anzuzeigen, müssen wir die XML-Datei des Layouts folgendermaßen erweitern:

      <!-- RadioButton -->  
      <style id="radiobuttonStyle">  
           <opaque value="TRUE" />  
           <imageIcon id="radiobutton_off" path="radiobutton_off.png"/>  
           <imageIcon id="radiobutton_mouse_over_off" path="radiobutton_mouse_over_off.png"/>  
           <imageIcon id="radiobutton_pressed_off" path="radiobutton_pressed_off.png"/>  
           <imageIcon id="radiobutton_on" path="radiobutton_on.png"/>  
           <imageIcon id="radiobutton_mouse_over_on" path="radiobutton_mouse_over_on.png"/>  
           <imageIcon id="radiobutton_pressed_on" path="radiobutton_pressed_on.png"/>  
           <property key="RadioButton.icon" value="radiobutton_off"/>  
           <state>  
             <color type="TEXT_FOREGROUND" value="#505050"/>  
           </state>  
           <state value="PRESSED">    
                <property key="RadioButton.icon" value="radiobutton_pressed_off"/>  
           </state>       
           <state value="PRESSED and SELECTED">    
                <property key="RadioButton.icon" value="radiobutton_pressed_on"/>  
           </state>       
           <state value="SELECTED">    
                <property key="RadioButton.icon" value="radiobutton_on"/>  
           </state>       
           <state value="MOUSE_OVER and SELECTED">    
                <property key="RadioButton.icon" value="radiobutton_mouse_over_on"/>  
           </state>       
           <state value="MOUSE_OVER">    
                <property key="RadioButton.icon" value="radiobutton_mouse_over_off"/>  
           </state>       
      </style>  
      <bind style="radiobuttonStyle" type="region" key="RadioButton"/>      

Wenn Sie meinem Post zu Checkboxen gefolgt sind, werden Ihnen viele Parallelen auffallen. Letztlich geht es auch bei Radio buttons nur darum, zum aktuellen Status die passende Grafik auszuwählen.

Screenshot: Radio buttons go 70s
Screenshot: Radio buttons go 70s

Und – was sagen Sie?

Zwinkerndes Smiley