Java Standard: Grafische Oberflächen mit SWT


Java SWT und JFace

Bearbeiten

Das Standard Widget Toolkit [SWT] ist ein Framework mit der ähnlich AWT und Swing grafische Oberflächen mit Java erstellt werden können. SWT setzt dabei direkt auf die betriebssystemspzifischen Komponenten auf und hat daher unter den verschiedenen Java Plattformen ein sehr natives Aussehen. Um dies zu erreichen ist bei dem Deployment unserer Anwendung auch eine passende Bibliothek (dynamic link library / shared objects) auszuliefern.

JFace ist eine Erweiterung on-top auf SWT. Für den Anwendungsentwickler macht es (fast) keinen Unterschied ob er JFace oder SWT verwendet, da dass Ziel eine Oberfläche zu erstellen verfolgt wird.

Fenster mit "pure SWT"

Bearbeiten

Anwendungsfenster können in SWT mit Hilfe der Klasse Shell erzeugt werden. JFace bietet mit der Klasse ApplicationWindow eine Alternative, welche weitere Möglichkeiten bietet.

Fenster werden in SWT über die Klasse Shell erzeugt. Um ein neues Fenster zu erzeugen müssen wir diesem das Display zur Anzeige übergeben. SWT erwartet, dass die Main-Event-Schleife eigenständig verwaltet wird.

  Display display = new Display();
  // Ein einfaches neues (Standard-)Fenster erzeugen.
  Shell shell = new Shell(display); 
  shell.setText("Fenstertitel");
  shell.open(); 
  // Nach dem Anzeigen die Main-Event-Loop abfangen
  while(!shell.isDisposed()){ // Fenster zerstört?
    if(!display.readAndDispatch()){ // Nichts zu tun?
      display.sleep(); 
    }
  }

Besser ist mit JFace zu arbeiten und ein Anwendungsfenster zu erstellen.

Anwendungsfenster

Bearbeiten

Für den Aufbau einer Anwendung sollte die Klasse ApplicationWindow erweitert werden. Ein ApplicationWindow stellt uns mehrere Hilfsmethoden zur Verfügung, um unser Anwendungsfenster zu erzeugen. Der Konstruktor hätte hierbei gern von uns eine Shell Instanz (oder null). Um dem Fenster einen sprechenden Namen zu geben, widmen wir uns einer der Hilfsmethoden: configureShell. Um das Fenster nicht gleich wieder verschwinden zu lassen, nutzen wir die Methode setBlockOpen(true).


import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class AnwendungsFenster extends ApplicationWindow {

  public AnwendungsFenster() {
    super(null);
  }

  @Override
  protected void configureShell(Shell newShell) {
    super.configureShell(newShell);
    newShell.setText("Einfaches Anwendungsfenster");
  }

  public static void main (final String args[]) {
    try {
      AnwendungsFenster window = new AnwendungsFenster();
      window.setBlockOnOpen(true);
      window.open();
      Display.getCurrent().dispose();
    } catch (Throwable t) {
      t.printStackTrace();
    }
  }
}

MessageBox und MessageDialog

Bearbeiten

Einfach mal eine Nachricht an den Nutzer ausgeben? Die MessageBox oder gleich der MessageDialog ist unsere Wahl .

Die MessageBox ist die einfache (pure) SWT Variante. Im Konstruktor übergeben wir dieser unsere Shell zur Anzeige und eine Kombination der Konstanten zur Konfiguration des Aussehens. Anschließend setzen wir den Title und den Nachrichtentext.

  MessageBox mb = new MessageBox(
    Display.getCurrent().getActiveShell(), 
    SWT.OK | SWT.ICON_INFORMATION);
  mb.setText("Monitorauswahl");
  mb.setMessage("Danke, dass Sie Monitor Nr."+id.getValue()+" ausgewählt haben");
  mb.open();

__annotation JFace__ Bei der JFace Variante MessageDialog dürfen wir alle Eigenschaften direkt übergeben:

  1. unsere Shell,
  2. den Titel,
  3. ein Bild, gern auch ein Eigenes,
  4. den Nachrichtentext,
  5. den Typ unseres Nachrichtenfenster,
  6. ein Array mit den Text unserer Schaltflächen und
  7. den Index der vorausgewählten Schaltfläche
  MessageDialog md = new MessageDialog(
    Display.getCurrent().getActiveShell(), 
    "Monitorauswahl", 
    MessageDialog.getDefaultImage(), 
    "Danke, dass Sie Monitor Nr."+id.getValue()+" ausgewählt haben", 
    MessageDialog.INFORMATION, 
    new String[]{"OK"}, 
    0);
  md.setBlockOnOpen(true);
  md.open();

InputDialog

Bearbeiten

__annotation JFace__

 
SWT InputDialog Beispiel

Um Eingaben vom Anwender abzufragen steht uns der InputDialog zur Verfügung. Neben der obligatorischen Shell wird von uns die Übergabe von

  1. einem Titel,
  2. einer beschreibenden Nachricht,
  3. einem Initialwert und
  4. optional einem Objekt zur Eingabeprüfung erwartet.
InputDialog inputDialog = new InputDialog (
  Display.getCurrent().getActiveShell(), 
  "Suchbereich", 
  "Bitte geben Sie den gewünschten Suchbereich ein.", 
  "Wikibooks", 
  null
);
switch (inputDialog.open()) {
case InputDialog.OK: 
  break;
default: 
  break;
}

TitleAreaDialog

Bearbeiten

__annotation JFace__

Der TitleAreaDialog ermöglicht uns mit seinem strukturierten Aufbau komplexere Dialoge aufzubauen. Er besteht dabei aus:

  1. eine Titelzeile
  2. einen Nachrichtenbereich, quasi eine Kurzbeschreibung
  3. ein Bildbereich für unser CD
  4. einen Bereich zur freien Gestaltung
  5. Schaltflächen
 
SWT TitleAreaDialog sample

Der Konstruktor des TitleAreaDialog benötigt die Shell für die Anzeige. Mit Aufruf der Methode create sorgen wir für die Initialisierung und mit open sorgen wir für die Anzeige...

this.create();
this.setTitle("Ein TitleAreaDialog");
this.setMessage("Zwei Zeilen für...\n...eine unqualifizierte Nachricht.");
final int result = this.open();
if (TitleAreaDialog.OK == result) {
}
 
Beispiel für einen angepassten SWT TitleAreaDialog

Grundsätzlich wird der Dialog mit den Schaltflächen OK und CANCEL generiert. Dies passen wir jetzt an, da wir nur eine Schaltfläche wollen und nutzen die createButtonsForButtonBar Methode:

  protected void createButtonsForButtonBar(Composite parent) {
    createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
  }

  protected void buttonPressed(final int buttonId) {
    switch (buttonId) {
    case IDialogConstants.OK_ID:
      close();
      break;
    default:
      break;
    }
  }

Jetzt nutzen wir den "Bereich zur freien Gestaltung" durch überschreiben von createDialogArea. Wir basteln uns hier einen einfachen Dialog für die allgemeine Fehlerroutine in der main-Methode:

  protected Control createDialogArea(Composite parent) {
    final Composite stacktraceView = new Composite(parent, SWT.NONE);
    GridLayout stacktraceLayout = new GridLayout();
    stacktraceLayout.marginWidth = 0;
    stacktraceLayout.verticalSpacing = 0;
    stacktraceLayout.horizontalSpacing = 0;
    stacktraceLayout.marginHeight = 0;

    TextViewer textViewer = new TextViewer(stacktraceView, SWT.BORDER);
    StyledText styledText = textViewer.getTextWidget();
    styledText.setEditable(false);
    styledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
    StringBuilder content = new StringBuilder(String.format("Fehlerinformation:%n"));
    if (null == this.error) {
      content.append("keine");
    } else {
      content.append(String.format("%s%n", this.error.getLocalizedMessage()));
      for (StackTraceElement ste : this.error.getStackTrace()) {
        content.append(String.format("%d: %s.%s - Datei %s%n",
            ste.getLineNumber(), ste.getClassName(), ste.getMethodName(),
            ste.getFileName()));
      }
    }
    styledText.setText(content.toString());
    stacktraceView.setLayout(stacktraceLayout);

    stacktraceView.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

    return stacktraceView;
  }

Nun noch den Feinschliff und ein neues TitleImage setzen:

  public void create() {
    super.create();
    this.setTitle("Schwerer Anwendungsfehler aufgetreten.");
    this.setErrorMessage("Es ist ein schwerer Anwendungsfehler aufgetreten.\n"
        + " Bitte informieren Sie Ihren Anwendungsbetreuer");
    this.setTitleImage(this.imageRegistry.get("logo.wikibooks"));
  }

Die Optik dieses Dialogs wird uns im Rahmen der Wizards erneut ins Auge fallen.

__screenshot missing__


Fortgeschrittene Themen

Bearbeiten

Mehrere Monitore

Bearbeiten

Um mit mehreren Monitoren umzugehen ermöglicht uns die Displayinstanz die Anzahl der Monitore abzufragen. Über das dann erhaltene Monitor Objekt können wir den Anzeigebereich unseren Fenstern zuweisen:

  Monitor monitor = Display.getDefault().getMonitors[0];
  Shell first = new Shell (Display.getDefault());
  MessageBox mb = new MessageBox(primary, SWT.OK | SWT.ICON_INFORMATION);
  mb.setText("Monitorauswahl");
  mb.setMessage("Diese Anzeige erfolgt auf dem ersten gefunden Monitor.");
  first.setBounds(monitor.getBounds());
  mb.open();

Fenster besonderer Form

Bearbeiten

Eine der wenigen Ausnahmefälle, wo wir Fenster direkt über Shell erzeugen müssen ist das Verwenden einer besonderen Form für unser Anwendungsfenster. In diesen Fällen können die Betriebsstandards (natürlich) nicht greifen. Daher ist es notwendig unsere Shell mit den Parametern SWT.NO_TRIM | SWT.ON_TOP zu konfigurieren.

Auch wenn ein solches Fenster einem der elementaren Ansätze von SWT widerspricht - L&F der Java Anwendung am Betriebssystem zu orientieren - kann dies aus Design-, Imagegründen oder einfach "weil wir es können" Anwendung finden.