Stufe 2: Swing mit Layout Manager

In der zweiten Phase werden viele Zellen mit JButtons zu einem Feld zusammengefaßt.

Layout

Nutzen Sie einen Layoutmanager mit dem Sie Oben das Feld darstellen können und unten die Statusinformationen. Was bietet sich hier an?

Welcher Layoutmanager bietet sich für das Feld an?

Unten sollen Sie zwei Komponenten implementieren, einen Text und einen variablen Text. Welcher Layoutmanager bietet sich hier an?

Komponenten

Welche Komponenten benötigen Sie?

Pull-down Menü

Implementieren Sie das Pull-down Menü.

Das Feld soll editierbar sein oder schreibgeschützt. Welche Komponente bietet sich hier an?

Implementieren Sie eine Textausgabe für Weg finden, Oeffnen, Speichern. Diese Funktionen werden in späteren Phasen implementiert.

Implementieren Sie Editieren, Loeschen, Beenden.

Labyrinth ohne Menü

GUI mit ausgeklapptem Menü

Labyrinth mit Menue

 

Klasse Backtrack

Zur Klasse Backtrack gibt es einige Entwurfsüberlegungen. Die Klasse soll in den nächsten Stufen in der Lage sein das Laden und Speichern von Dateien zu unterstützen. Sie soll es auch erlauben, dass die Suchalgorithmen eingebunden werden.

Dies kann man erreichen in dem man alle Aktionen des Pull-Down Menüs als eigene Methoden implementiert. Jede zukünftige Phase wird aus dieser Klasse spezialisert. Durch überschreiben der Methoden kann man in zukünftigen Phasen die Aktionen ändern, ohne das man die alte Implementierung ändern muss.
Die Methoden für spätere Phasen machen nur ein Update im Textfeld. So kann man sehen, dass sie aufgerufen werden.

Dadurch kann man auf den Code der vorhergehenden Klasse zurückgreifen und muss den Code nicht replizieren.

Hierzu muss man das GUI in einer Instanz von Backtrack implementieren. Das GUI der vorhergehenden Phase wurde als statische Methode in Zelle implementiert. Dies ist hier nicht geschickt.

Um das Editieren zur ermöglichlichen muß der Benutzer einen aus mehreren Zuständen auswählen. Dies kann man mit der Klasse javax.swing.JCheckBoxMenuItem erreichen.

Klasse Labyrinth

Die Klasse Labyrinth dient zum Darstellen und Speichern des Labyrinths. Sie enthält keine Aspekte der Benutzeroberfläche. Das Labyrinth wird von Backtrack benutzt um die entsprechenden graphischen Elemente korrekt anzuzeigen.

Da das GUI immer etwas sinnvolles anzeigen soll, wird ein Labyrinth nach dem Laden immer in das Labyrinth umkopiert welches zur Anzeige benutzt wird. Jeder JButton (Zelle) hat einen Zeiger auf eine Position. Würde man den neuen Zustand nicht in ein Objekt der Klasse Position umkopieren müsste man alle Zeiger der Zellenobjekte auf die neuen Objekte des Labyrinths ändern.

Die Klasse Labyrinth hat deshalb zwei Konstruktoren

  • einen Copy-Konstruktor: ein existierendes Feld wird als Vorlage verwendet.
  • einen Konstruktor mit den Dimensionen und der Start und Ziel-Position: Ein Ausgangszustand
    • Hier ist es wichtig die Koordinaten der Start- und Ziel Objekte zu nutzen und die dazu passenden Objekte im Feld zu identifizieren.

Die Methode update() kopiert ein existierendes Labyrinth in ein anderes um.

getPos(x,y) ist eine Bequemlichkeitsmethode die in den späteren Phasen beim Suchen helfen wird.

UML

uml Diagramm

Die Klasse Labyrinth wird alle Information enthalten die gespeichert werden:

  • Labyrinth
  • Position 

Die Klasse Labyrinth enthält Zeiger auf den Start und das Ziel des Labyrinths. Diese beiden Objekte müssen Teile des Feldes sein welches angezeigt wird!

Die Klassen die zum GUI gehören sind:

  • Backtrack
  • Zelle

sie werden nicht gespeichert.

 Speicherstruktur

Im Speicher gibt es Objekte die mit der Hilfe von Swing das GUI repräsentieren. Das ist eine Instanz von Ariadne und das Feld der Buttons vom Typ Zelle. Der Zustand der Zellen holen sich die graphischen Objekt aus der Instanz des Labyrinths und dem Feld der Positionen. Später wird der nicht grafische Zustand vom Backtrackingalgorithmus und von den Methoden zum Speichern und Laden verwendet. Die Idee, hier ist, mir Hilfe einer Kopiermethode und eines Copykonstruktors jeweils eine Kopie anzulegen die dann für den entsprechenden Zweck verwendet wird.

Die Objekte die den Status des GUI repräsentieren sind getrennt vom GUI.

Speicherlayout der Klassen

Musterlösung

Github Projekt scalingbits/dhbwjava

Hilfe! Wie packe ich das an?

Hier ein paar Tips wie man dieses Problem in kleineren Schritten lösen kann...

Wichtig: Immer ein lauffähiges Programm behalten!

  1. Implementieren Sie die Klasse Labyrinth
    1. Erzeugen Sie im Konstruktor ein zweidimensionales Feld von Position
    2. Der graphische Teil der Anwendung soll auf eigenen Datenstrukturen arbeiten. Deshalb benötigen Sie
      1. Eine Updatemethode: Der Zustand einer Vorlage wird in in ein Labyrinth kopiert
      2. Einen Copy-Konstruktur
      3. Die Klasse Position sollte eine ähnliche Infrastruktur haben
    3. Start und Ziel: Können theoretisch aus Objekten bestehen, die nicht zum aktuellen Labyrinth gehören (Gefahr von Inkonsistenzen!)
      1. Lesen Sie das Start- und Ziel-objekt im Konstruktor nur
        1. Ziegen Sie mit Ihren internen Zeigern auf die äquivalenten Objekte im Labyrinth.
        2. Setzen Sie deren Zustand.
          1. Equals- und Update-Methoden in Position sind hier sehr nützlich
    4. Setzen Sie beim Anlegen des Feldes alle Positionen auf eine leere Position (ausgenommen Start und Ziel)
    5. Testen ist schwierig: Mut zur Lücke...
  2. Design Ihres GUIs (Klasse Backtrack)
    1. Bauen Sie Ihr GUI im Konstruktor der Klasse
    2. Legen Sie ein JFrame an
      1. Beenden Sie die Anwendung beim Schliessen des Fenster
    3. Nutzen Sie ein Borderlayout als Hauptstruktur
      1. Im Süden implementieren Sie ein JPanel mit z.Bsp. ein Flowlayout
        1. Ein JLabel links
        2. Ein JTextField rechts
          1. Nur lesbar
          2. Merken Sie sich den Zeiger auf das Textfeld als Attribut in Ihrer Hauptklasse.
            1. ActionListener in inneren Klassen müssen hier den Zustand dokumentieren
      2. Im Zentrum wird ein neues JPanel mit Gridlayout angelegt
        1. Die Dimension muss zum Labyrinth passen
        2. Legen Sie ein Labyrinth an.
        3. Legen Sie zu jedem Feld im Labyrinth eine Zelle an und stecken Sie die Zellen in das JPanel mit Gridlayout
        4. Speichern Sie sich einen Zeiger auf das Anzeigelabyrinth in einem Attribut der äusseren Klasse
  3. Implementieren Sie das Hauptprogramm
    1. Legen Sie ein Objekt Ihrer Klasse an
    2. Implementieren und benutzen Sie eine Methode anzeigen()
      1. Die Methode "packt" das JFrame und zeigt es an
      2. Man muss später Spezialisierungen der Klasse mit dem spezifisichen Konstruktor in zukünftigen main() Methoden erzeugen. Dies Methode kann man dann wiederverwenden...
    3. Testen Sie alles!
  4. Implementieren Sie das Menü mit allen Einträgen. Diese Klasse wir noch spezialisiert. Esa erspart später Schreibarbeit
    1. Schreiben Sie hierfür eine eigene Methode
    2. Rufen Sie die Methode aus dem Hauptprgramm auf
  5. Behandlung der Aktionen (siehe Screenshot)
    1. Viele Behandlungen werden erst später benötigt. Implementieren nur eine Kontrollausgabe auf der Konsole
    2. Vorgehen:
      1. Die Klasse Backtrack wird in späteren Phasen spezialisiert
      2. Schreiben Sie für jede Aktion eine Methode die einen Zeiger auf die graphische Komponente entgegen nimmt.
      3. Diese Methoden werden später überschrieben!
      4. In der Methode wird ein ActionListener angelegt und registriert
    3. Jetzt zu implementierende Behandlungen
      1. Beenden der Anwendung
      2. Schützen der Anwendung vor Editierversuchen
      3. Löschen des Labyrinths
    4. Testen, testen, testen
  6. Feiern: Sie haben die nächste Stufe geschafft.