Zurück   Weiter

10 GUI Programmierung mit Java

Die GUI Programmierung ist in Java ähnlich aufgebaut wie in den meisten modernen Programmiersprachen. Man verwendet Container, in denen die Komponenten angeordnet werden. Durch Verschachtelung von Containern kann man sehr komplexe GUI erstellen.

Java stellt zur Programmierung von Graphical User Interafces (GUI), also grafischen Benutzeroberflächen, die zwei Packages Abstract Window Toolkit (AWT) und Swing zur Verfügung. AWT und Swing stellen dem Programmierer all die Elemente zur Verfügung, die am häufigsten zur GUI-Programmierung benötigt werden. Dazu gehören unter anderem Buttons, Menüs, Checkboxen usw.

Das AWT basiert auf Systemkomponenten. Solche Komponenten stehen in jedem Betriebssystem mit grafischer Benutzeroberfläche zur Verfügung und erzeugen das für Betriebssystem typische Aussehen. Mit dem AWT erstellte GUIs haben ein vom Betriebssystem abhängiges Aussehen (Look and feel).

Die Swing Komponenten werden komplett gezeichnet. Ihr aussehen ist nicht vom Betriebssystem abhängig. Damit können Applikationen entwickelt werden, welche immer das gleiche Aussehen haben, egal auf welchem Betriebssystem sie gestartet werden. Das Aussehen der Komponenten wird über ein austauschbares 'Look and feel' gesteuert. In den Java Implementierungen wird bei jeder Plattform das zugehörige 'Look and feel' mit ausgeliefert. Die 'Look and feels' der anderen Plattformen stehen aus rechtilichen Gründen nicht immer zur Verfügung. In der Zwischenzeit lassen sich im Internet von Drittanbietern weiter 'Look and feel' Implementierungen herunterladen.

10.1 Das Package java.awt

Das Package java.awt enthält alle Grundklassen, die zur Erstellung einer grafischen Benutzeroberfläche benötigt werden. Ausserdem sind in diesem Package die AWT-Komponenten definiert.

10.1.1 Die Klasse java.awt.Component

java.awt.Component ist die Superklasse aller Java Gui-Klassen (auch der Swing Klassen). Sie stellt einige grundlegende Methoden zur Verfügung. Einige wichtige werden hier aufgeführt.

10.1.1.1 setSize(...), getSize()

Mit der Methode setSize kann die Grösse der Komponente gesetzt werden. getSize liefert eine Instanz der Klasse Dimension, welche die Breite und Höhe der Komponente enthält.

10.1.1.2 setBounds(...), getBounds()

Mit setBounds kann nicht nur die Grösse, sondern auch die Position einer Komponente innerhalb ihres möglichen Bereichs beeinflusst werden. getBounds liefert eine Rectangle Instanz mit Position, Breite und Höhe zurück.

10.1.1.3 paint(Graphics g)

Die Methode paint einer Komponente wird immer dann von der JVM aufgerufen, wenn diese neugezeichnet werden muss. Z.B. wenn das Fenster in dem sie sich befindet verdeckt war und wieder im Vordergrund angezeigt wird. Die Klasse Graphics, von der eine Instanz als Parameter übergeben wird, wird in einem der nächsten Kapitel näher betrachtet.

10.1.2 Die Klasse java.awt.Container

Container sind grafische Komponenten, in denen andere Komponenten angeordnet werden können. Diese Komponenten werden in Java in der Regel mit Hilfe der LayoutManager positioniert. Jedes GUI benötigt mindestens einen Startcontainer in den weitere Komponenten eingefügt werden. Ein Button kann z.B. nicht ohne einen Container angezeigt werden. Startcontainer einer GUI-Applikation in Java können die Klasse java.awt.Window und alle ihre Subklassen sein. In der Regel verwendet man die Klassen java.awt.Frame oder javax.swing.JFrame. In diese Container werden alle anderen GUI-Elemente eingefügt. Dies können einfache Elemente wie Buttons sein, aber auch wieder Container. So entsteht ein Hierarchie-Baum, an dessen Ende immer ein java.awt.Window steht.

10.1.2.1 add(Component comp)

Mit der Methode add werden Komponenten einem Container hinzugefügt.

10.1.2.2 add(Component comp, Object constraints)

Dieser Methode wird zusätzlich zur hionzufügenden Komponente noch ein Objekt mitgegeben, welches Informationen über die Anordnung der Komponente innerhalb des Containers enthält.

10.1.3 Die Klasse java.awt.Frame

Die Klasse java.awt.Frame stellt für alle AWT basierten Standalone-Applikationen den äussersten Container zur Verfügung. Das Frame entspricht dem systemspezifischen Fensterrahmen mit den zum Maximieren, Minimieren und Schliessen des Fensters üblichen Buttons. Für eigene Applikationen wird man in der Regel eine Subklasse von java.awt.Frame erstellen, welche das gewünschte Aussehen und Verhalten implementiert.

10.1.3.1 setVisible(boolean visible)

Mit der Methode setVisible kann man ein Frame sichtbar oder unsichtbar machen. Der Wert true zeigt das Fenster an, der Wert false macht das Fenster unsichtbar. Beim ersten Anzeigen des Fensters, wird das Fenster im Betriebssystem angemeldet und bekommt einen Bildschirmbereich zugewiesen.

10.1.3.2 pack()

Die Methode public void pack() bewirkt, dass das Frame die minimale Grösse bei optimaler Anordnung der in ihm enthaltenen Komponenten bekommt.

  public class FrameName extends Frame {
    ...

    public FrameName() {
      ...
      createGui();
      ...
    }

    private void createGui() {
      ...
      add(component1);
      add(component2);
      ...
    }
    ...
  }

  public class ProgrammName {
    public static void main(String[] args) {
      FrameName frame = new FrameName();
      frame.pack();
      frame.setVisible(true);
    }
  }

Aufgabe 10.1

10.1.4 Die Klasse java.awt.Color

Die Klasse java.awt.Color repräsentiert die Zeichenfarben zur GUI-Darstellung. In dieser Klasse sind die am häufigsten benötigten Farben als statische Konstanten definiert (z.B. Color.RED, Color.BLUE). Alle anderen Farben lassen sich mit Hilfe der Konstruktoren erzeugen. Sie werden in RGB-Werten übergeben.

10.1.5 Die Klasse java.awt.Font

java.awt.Font ist der Font, in dem die Schrift angezeigt wird. Um einen Font zu erzeugen muss man seinen Namen, den Stil und seine Grösse angeben. Der Name kann ein logischer Name (Dialog, DialogInput, Monospaced, Serif, SansSerif oder Symbol) oder ein Systemspezifischer Name sein. Die im System zur Verfügung stehenden Fonts erhält man durch den Aufruf der Methode GraphicsEnvironment.getAllFonts() oder GraphicsEnvironment.getAvailableFontFamilyNames(). Der Stil des Fonts wird mit einem Integer-Wert festgelegt. Hierzu sind in der Klasse Font die Konstanten Font.PLAIN, Font.ITALIC und Font.BOLD definiert. PLAIN und BOLD sowie ITALIC und BOLD können mit einem oder '|' verknüpft werden (PLAIN | BOLD).

10.1.6 Die Klasse java.awt.Graphics

java.awt.Graphics ist der Bildschirmbereich, auf den gezeichnet werden kann. Die Klasse enthält Methoden zum Schreiben von Text und zeichnen von geometrischen Figuren. Einige von diesen Methode werden hier vorgestellt. Die weiteren Methoden kann man der Javadokumentation entnehmen. Der Nullpunkt des Koordinatensystems befindet sich im rechten oberen Eck des Bildschirmbereichs.

10.1.6.1 setColor(Color color)

Die Methode setColor setzt die Zeichenfarbe, mit der ab diesem Zeitpunkt weitergezeichnet wird. Übergeben wird der Methode eine Instanz der Klasse java.awt.Color.

10.1.6.2 setFont(Font font)

setFont setzt einen neuen Font, mit dem die Methode drawString den Text darstellt. Übergeben wird der Methode eine Instanz der Klasse java.awt.Font.

10.1.6.3 drawString(String text, int x, int y)

Zeichnet einen Text an die Position x, y.

10.1.6.4 drawLine(int xStart, int yStart, int xEnd, int yEnd)

Zeichnet eine Linie vom Punkt xStart, yStart zum Punkt xEnd, yEnd.

10.1.6.5 drawRect(int x, int y, int width, int height)

Zeichnet ein Rechteck mit der Breite width und der Höhe height an die Position x, y.

10.1.6.6 drawRoundRect(int x, int y, int width, int height)

Zeichnet ein Rechteck mit abgerundeten Ecken mit der Breite width und der Höhe height an die Position x, y.

10.1.6.7 fillRect(int x, int y, int width, int height)

Zeichnet ein ausgefülltes Rechteck mit der Breite width und der Höhe height an die Position x, y.

10.1.6.8 fillRect(int x, int y, int width, int height)

Zeichnet ein ausgefülltes Rechteck mit der Breite width und der Höhe height an die Position x, y.

10.1.6.9 drawOval(int x, int y, int width, int height)

Zeichnet ein Oval mit der Breite width und der Höhe height an die Position x, y. Die Position und die Breite beziehen sich auf ein Rechteck, welches das Oval umschliesst.

10.1.6.10 fillOval(int x, int y, int width, int height)

Zeichnet ein ausgefülltes Oval mit der Breite width und der Höhe height an die Position x, y. Die Position und die Breite beziehen sich auf ein Rechteck, welches das Oval umschliesst.


10.1.7 Die Klasse java.awt.Canvas

Das java.awt.Canvas ist die einfachste Komponente in Java. Sie stellt eine einfache Oberfläche zum Zeichnen dar. Überschreibt man in einer von java.awt.Canvas abgeleiteten Klasse die Methode public void paint(Graphics g) so kann man mit Hilfe der Methoden der Klasse java.awt.Graphics direkt in die Komponente zeichnen.


  public class CanvasName extends Canvas {
    ...

    public CanvasName() {
      ...
    }

    public void paint(Graphics g) {
      ...
      g.setColor(...);
      g.drawLine(...);
      g.setColor(...);
      g.fillRect(...);
      ...
    }
    ...
  }

Eine auf diese Weise erzeugte Komponente kann mit der Methode add(Component c) einem Frame hinzugefügt und damit angezeigt werden.

  public class FrameName extends Frame {
    ...

    public FrameName() {
      ...
      createGui();
      ...
    }

    private void createGui() {
      ...
      CanvasName myCanvas = new CanvasName();
      add(myCanvas);
      ...
    }
    ...
  }
Paintable2.java

package graphic;

import java.awt.Graphics;

public interface Paintable2 {
  public void paint(Graphics g);
}

Sourcefile

DrawCanvas.java

package graphic;

import java.awt.Canvas;
import java.awt.Graphics;

public class DrawCanvas1 extends Canvas {
  private Paintable2[] paintables;

  public DrawCanvas1(Paintable2[] paintables) {
    this.paintables = paintables;
  }
  
  public void paint(Graphics g) {
    for(int i = 0; i < paintables.length; i++) {
      paintables[i].paint(g);
    }
  }
}

Sourcefile

Line7.java

package graphic;

import java.awt.Graphics;

public class Line7 implements Paintable2 {
  private Point15 start;
  private Point15 end;

  public Line7(Point15 p1, Point15 p2) {
    start = p1;
    end = p2;
  }

  public void paint(Graphics g) {
    g.drawLine(
      start.getXPosition(), start.getYPosition(),
      end.getXPosition(), end.getYPosition()
    );
  }

  public String toString() {
    String result = "Line7: start = " + start.toString() +
                    ", end = " + end.toString();
    return result;
  }
}

Sourcefile

Rectangle2.java

package graphic;

import java.awt.Graphics;

public class Rectangle2 implements Paintable2 {
  private Point15 insertPoint;
  int width;
  int height;

  public Rectangle2(Point15 insertPoint, int width, int height) {
    this.insertPoint = insertPoint;
    this.width = width;
    this.height = height;
  }

  public void paint(Graphics g) {
    g.drawRect(
      insertPoint.getXPosition(), insertPoint.getYPosition(),
      width, height
    );
  }
}

Sourcefile

Paint11.java

package programs;

import graphic.DrawCanvas1;
import graphic.Line7;
import graphic.Paintable2;
import graphic.Point15;
import graphic.Rectangle2;
import java.awt.Frame;

public class Paint11 extends Frame {
  public Paint11() {
    super("Paint");
    setSize(400, 300);
    Paintable2[] paintables = createPaintableObjects();
    DrawCanvas1 drawCanvas = new DrawCanvas1(paintables);
    add(drawCanvas);
  }

  private Paintable2[] createPaintableObjects() {
    Point15 a = new Point15(9, 13);
    Point15 b = new Point15(137, 98);
    Paintable2[] paintObjects = new Paintable2[2];
    paintObjects[0] = new Line7(a, b);
    Point15 c = new Point15(57, 40);
    paintObjects[1] = new Rectangle2(c, 99, 30);
    return paintObjects;
  }

  public static void main(String[] args) {
    Paint11 paintFrame = new Paint11();
    paintFrame.setVisible(true);
  }
}
Sourcefile

Aufgabe 10.2

10.1.8 Die Klasse java.awt.Panel

10.2 Das package javax.swing

10.2.1 Die Klasse javax.swing.JComponent

10.2.3 Die Klasse javax.swing.JFrame

10.2.3 Die Klasse javax.swing.JPanel

10.3 Die LayoutManager

Es gibt grafische Oberflächen, dei denen die Anordnung der Komponenten über die Angabe absoluter Koordinaten in Pixeln erfolgt. Diese Vorgehensweise ist aber aus mehreren Gründen unvorteilhaft:

10.3.1 Das FlowLayout

FlowLayout ist der voreingestellte Layout-Manager von Panel und JPanel und damit auch von Applet und JApplet. Er ordnet all seine Komponenten einfach nebeneinander in einer logischen Reihe an. Logisch bedeutet hierbei, dass die Komponenten am Bildschirm in mehreren Zeilen dargestellt werden, sobald der Container zu schmal ist, um alle Komponenten in einer Zeile anzuzeigen. Die Komponenten werden in ihrer bevorzugten Grösse dargestellt. Wenn sich die Breite des Containers ändert, passt der FlowLayout-Manager die Darastellung entsprechend an, so dass die Breite stets voll ausgenutzt wird. Hierbei hat flowLayout drei verschiedene Möglichkeiten, die Komponenten bezüglich des Containers anzuordnen. Eine der folgenden Konstanten kann dem Konstruktor übergeben werden, um die Ausrichtung festzulegen:

Zusätzlich kann dem Konstruktor der horizontale und der vertikale Abstand der Komponente übergeben werden. Letzterer wird nur dann verwendet, wenn die Komponenten in mehreren Zeilen angeordnet werden müssen. Der voreingestellte Wert für diese Abstände ist 5.

10.3.2 Das BorderLayout

BorderLayout ist der voreingestellte Layout-Manager der Klasse Window und damit auch der der Klassen Frame, Dialog, JFrame und JDialog. Das BorderLayout sollte benutzt werden, wenn Komonenten gezielt untereinander oder nebeneinander angeordnet werden sollen. Der BorderLayout-Manager verwaltet bis zu fünf Komponenten, eine obere, eine linke, eine untere, eine rechte sowie eine in der Mitte. Diese können jeweils einzeln definiert werden. Hierzu muss beim Hinzufügen einer Komponenten mit der Methode add zusätzlich eine der folgenden Konstanten der Klasse BorderLayout übergeben werden:

Dem Konstruktor der Klasse BorderLayout kann zusätzlich noch der horizontale und vertikale Abstand der Komponenten zueinander übergeben werden.

10.3.3 Das CardLayout

Der CardLayout-Manager ordnet seine Komponenten wie einen Stapel Spielkarten an, von denen nur die Oberste sichtbar ist. Alle Komponenten werden hierbei in der gleichen Grösse dargestellt. Zum Blättern definiert der CardLayout-Manager vier Methoden, mit denen die nächste, die vorhergehende, die letzte oder die erste 'Karte' sichtbar gemacht werden: next(), previous(), last() und first(). Dadurch nimmt der CardLayout-Manager eine Sonderrolle unter den Layout-Managern ein, da dieses Layout gewissermassen interaktiv ist, wohingegen die anderen Layout-Manager ihre Arbeit im Hintergrund verrichten, nachdem sie einmal konfiguriert worden sind.

10.3.4 Das GridLayout

Der GridLayout-Manager ordnet seine Komponenten in einem Gitter an, das aus gleich grossen Gitterzellen besteht. Hierbei nimmt jede Komponente genau eine Gitterzelle ein. Die Breite der Gitterzellen richtet sich nach der breitesten Komponente, die sich in diesm Layout befindet. Dassebe gilt auch für die Höhe der Gitterzellen. Der Abstand der Gitterzellen kann dem Konstruktor wahlweise ebenfalls übergeben werden. Er ist per Voreinstellung Null. Die Gitterzellen werden von oben links beginnend nach rechts und unten mit Komponenten aufgefüllt.

Die Anzahl der Zeilen und Spalten muss dem Konstruktor übergeben werden. Wenn dem Layout weniger Komponenten hinzugefügt werden, als Gitterzellen vorhanden sind, dann bleiben die überzähligen Gitterzellen leer. Werden dagegen mehr Komponenten eingesetzt, als ursprünglich im Konstruktoraufruf vorgesehen, dann werden dem Layout weitere Spalten hinzugefügt. Die anfangs im Konstruktoraufruf bestimmte Zeilenanzahl wird stets beibehalten. Der Konstruktor bietet aber auch die Möglichkeit, entweder die Spaltenzahl oder die Zeilenzahl selbst zu berechnen, indem an der entsprechenden Stelle die Null übergeben wird.

10.3.5 Das GridBagLayout

Das GridBagLayout basiert wie das GridLayout auf einem Gitter. Allerdings ist der GridBagLayout-Manager erheblich flexibler. Er bietet unter anderem folgende Möglichkeiten:

Die Einstellung dieser Eigenschaften erfolgt mit den Instanzen der Klasse GridBagConstraints, die im awt-Paket definiert ist. GridbagConstraints enthält keine Methoden, sondern nur Elemente, die in der gewünschten Weise belegt werden müssen, und einige Konstanten, die die Belegung vereinfachen. Erzeugt man eine Instanz von GridBagConstraints und belässt man alle Elemente mit ihren Standardwerten, so sieht der betreffende Container genauso aus, als hätte man ein GridLayout verwendet. Zur Ausnutzung der besonderen Fähigkeiten vom GridBagLayout ist also die Modifikation der Elemente von GridBagConstraints zwingend erforderlich. Die Eigenschaften werden beim hinzufügen der Komponente zum Container mitgegeben. Das GridBagLayout ist das variabelste Layout aber auch am kompliziertesten zu programmieren. In den meisten Fällen erreicht man durch geschicktes ineinander schachteln von Containern mit verschiedenen LayoutManagern das gleiche Ergebnis mit übersichtlicherem Sourcecode.

10.3.5.1 Breite des Anzeigebereichs

Das Element GridBagConstraints.gridwidth definiert die Anzahl an Gitterzellen, über die sich der Anzeigebereich horizontal erstreckt. Hierbei gibt es zwei wichtige vordefinierte Konstanten:

10.3.5.2 Höhe des&nbps;Anzeigebereichs

Das Element GridBagConstraints.gridheight definiert die Anzahl an Gitterzellen, über die sich der Anzeigebereich vertikal erstreckt. Hierbei gibt es zwei wichtige vordefinierte Konstanten:

10.3.5.3 Festlegung der Position

Zur Positionierung gibt es die Elemente GridBagConstraints.gridx und GridBagConstraints.gridy. Sie legen die obere linke Gitterzelle des Anzeigebereichs fest. Der voreingestellte Wert GridBagConstraints.RELATIVE ordnet die Komponente neben bzw. unter der zuvor eingefügten Komponenten an.

10.3.5.4 Anordnung im Anzeigebereich

Das Datenfeld GridBagConstraints.anchor beschreibt die Ausrichtung einer Komponente innerhalb seiner Zelle. Mögliche Werte für anchor sind:

10.3.5.5 Füllung der Gitterzellen

Im Element GridBagConstraints.fill kann spezifiziert werden, ob und wie einen Komponente ihren Anzeigebereich ausfüllt.

10.3.5.6 Aufteilung des Platzes

zur Aufteilung des Platzes gibt es die Elemente GridBagConstraints.weightx und GridBagConstraints.weighty. Sie ermöglichen eine gewichtete Verteilung des vorhandenen Platzes. Dies ist insbesondere bei der Änderung der Grösse des Containers von Bedeutung. Das Gewicht ist üblicherweise eine Dezimalzahl zwischen dem Standardwert 0.0 und 1.0. Es sind aber auch grössere Werte zulässig. Der Wert 0.0 bedeutet, dass zusätzlicher Raum, der bei der Verbreiterung des Containers entsteht, ausschliesslich zur Verbreiterung des Gitters zwischen den Gitterzellen verwendet wird. Die Komponente erhält also keinen zusätzlichen Raum. Der andere Extremwert 1.0 dagegen bewirkt das Gegenteil. Hier erhält die Komponente den gesamten zusätzlichen Platz; das Gitter behält seine Breite.

10.3.5.7 Zusätzliche Abstände

Mit dem Element GridBagConstraints.insets kann an den Rändern des Anzeigebereichs der Komponente zusätzlicher Zwischenraum eingefügt werden.

10.3.5.8 Mindestplatzbedarf

Mit GridBagConstraints.ipadx und GridBagConstraints.ipady kann der Mindestplatzbedarf einer Komponente beeinflusst werden.

Created with Vim Zurück   Weiter