Was ich so gemacht habe

Die letzten Wochen habe ich mich hautpsächlich mit dem Programmieren einer etwas aufwändigeren Java Applikation beschäftigt. Zunächst sollte es nur darum gehen, eine Exceltabelle mit vorgegebenem Format ein ein anderes vorgegebenes Format zu konvertieren.

Mit der Zeit stellte sich jedoch heraus, dass hier gewisse Faktoren eine Rolle spielen, die mir das Ganze nicht so einfach machen würden.

Kleiner Überblick

Gegeben war eine Beispielexceltabelle, die dem Format der anderen verwendeten Exceltabellen entsprach. Desweiteren hatte ich ein etwa zwanzigseitiges Dokument, in dem das Zielformat spezifiziert war.

Das Problem mit Exceltabellen: Prinzipiell sind sie nicht strukturiert. Es gibt für die Zeilen und Spalten kein festgelegtes Format, und ein Benutzer kann Daten in eine Zelle schreiben, die – bezogen auf das Zieldateiformat – nicht viel Sinn machen. Aufgrund der Tatsache, dass es keine eindeutige Abbildung von Quell- auf Zieldaten gibt, musste eine grafische Benutzeroberfläche her. Der Anwender soll die Möglichekeit bekommen, die konvertierten Daten nachträglich nocheinmal zu bearbeiten und, wo nötig, zu korrigieren.

Kleine Helferlein

Zum Einlesen der aus den Excelfiles erzeugten csv-Daten, diente mir der Excel-csv-Parser von http://ostermiller.org/utils/CSV.html. Entwickelt habe ich das ganze unter Ubuntu Linux, der Einsatzbereich würde jedoch eine Win2K Umgebung sein. Hier kann Java natürlich seine cross-platform Stärke ausspielen; die JGoodies Look & Feel’s sorgen für ein plattformunabhägiges, ansprechendes Auftreten.

Beim Bauen der Oberfläche hat mit das Visual Editor Project von Eclipse sehr gute Dienste geleistet.

Wichtige Kleinigkeiten

Während des Projektes sind ein paar Sachen aufgetaucht, deren Lösung zunächst schwierig schien. Dabei waren dies keine größeren Probleme, allerdings doch Unbequemlichkeiten für den Nutzer der GUI.

JComboBox ist zu eng

Auch eine JComboBox wird, wie alle anderen GUI-Elemente, nur so breit gezeichnet wie die Fenstergröße und der LayoutManager zulässt. An sich kein Problem – wäre es nicht so, dass auch das Comboboxpopup in nur eben dieser Breite gezeichnet wird.

Im Bild links ist schnell ersichtllich: So lässt sich nicht arbeiten. Dieses Problem ist schon lange bekannt. So gibt es auf bugs.sun.com, Reports die auf Ende 1998 datieren. Umso verwunderlicher, dass es bis heute noch keine Standardmöglichkeit gibt das gewünschte Verhalten zu erzeugen.

Allerdings finden sich dort auch mehrere Workarounds.

Scrollbarposition soll fest bleiben

Sobald sich die Cursorposition innerhalb einer JTextArea ändert, wird automatisch der zu sehende Ausschnitt innerhalb einer JScrollPane verschoben. Meistens ist dies sinnvoll und erwünscht.

Kurzer Exkurs zur oben erwähnten Import-Funktion. Eine bestimmte Zeile pro Probe, sollte aus der csv-Datei in das Protokoll übernommen werden. Der jeweilige Text sieht für jede Probe ziemlich gleich aus – beim Durchblättern der Proben ist es jedoch wichtig, dass ein ganz bestimmter Teil des Protokolls direkt lesbar ist.

Nun wird beim Durchblättern der Beprobungen, immer der Text im Protokollfeld (man klicke oben auf den kleinen Screenshot) neu gesetzt. Damit wurde zunächst der Textausschnitt immer bis ans Ende gescrollt. Es ist zwar relativ einfach möglich, einfach den Cursor im Textfeld neu zu setzten (z.B. ganz an den Anfang). Die gewünschte Funktionalität war jedoch, dass der Scrollbalken immer an der gleichen Position bleibt.

Folgender Codeschnipsel garantiert dies:

prevRect = textArea.getVisibleRect();
textArea.setText( someStringHere );

SwingUtilities.invokeLater( new Runnable() {
  public void run() {
    textArea.scrollRectToVisible( prevRect );
  }
});

Der wichtige Teil ist hier das invokeLater. Normalerweise ist es so, dass die TextArea ihr Model mit dem neuen Text updatet, und dann irgendein Event feuert. Dieses Event führt dann bei der Parent-Scrollpane zum verschieben des Bildausschnittes.

Nun ist es so, dass die AWT-Eventbehandlung in einem extra Thread abläuft. Wenn man also direkt nach dem Setzen des Textes zum gewünschten Bereich scrollt, ist es sehr wahrscheinlich dass das Swinginterne Scrollevent erst danach behandelt wird. Schließlich erfolgt das in diesem separaten Thread.

Die Sachen die nun in invokeLater stehen, werden innerhalb des AWT-Eventthreads ausgeführt – und zwar erst nach allen anderen Swinginternen Events. Diese Geschichte geht sogar soweit, dass es heute wohl Best Practice ist, direkt am Anfang der main-Methode, alles Weitere in den Eventthread zu legen.

Contact

Alexander Gitter
contact at agitter net