Informatik beim Klick Bild ändern?

1 Antwort

Vom Beitragsersteller als hilfreich ausgezeichnet

Ich habe zunächst einmal recherchiert, woher denn diese Methoden (setzeSichtbar, wurdeGedrueckt, ...) kommen. Wie ich herausgefunden habe, handelt es sich hierbei um die SuM-Bibliothek, die direkt für BlueJ entwickelt wurde. Gib das bei künftigen Fragen besser mit an, denn so bekannt ist diese Bibliothek nicht (wenn eher nur für Schüler und Lehrer). In dem herunterladbaren ZIP-Archiv liegt im doc-Verzeichnis eine HTML-basierte JavaDoc.

Folgend ist mir bei deinem Code eine erste Ungereimtheit aufgefallen: Die Methode wurdeGedrueckt gibt es nur für die Tastatur. Deinen Angaben zufolge hast du aber Knöpfe (Buttons), für die du prüfen möchtest, ob sie angeklickt wurden. Solltest du wegen deines Codes im if-Kopf nicht bereits einen Fehler zurückbekommen?

Laut API Referenz musst du dem Knopf, wenn du ihn erstellst, den Namen der Methode übergeben, die er später aufrufen soll, sobald er vom Nutzer angeklickt wurde.

Knopf knopf = new Knopf(xPosition, yPosition, breite, hoehe, "Ein Knopf", "knopfAktion");

Ich vermute, der Kopf der Aktionsmethode sieht dann so aus:

public void knopfAktion()

und die Methode muss sicherlich in derselben Klasse liegen (die zudem von EBAnwendung erbt).

Da man aber ganz sicher nicht für jeden Button eine eigene Aktionsmethode erstellen möchte (viel Schreibarbeit mit Fehlerpotenzial), wäre es wohl günstig, sich eine eigene Knopf-Klasse zu schreiben bzw. noch einfacher eine eigene Bild-Klasse. Die kann, wie ich sehe, ebenso schon auf Klick-Ereignisse reagieren.

public class MemoryKarte extends Bild {
  public final static double KARTEN_BREITE = // ...;
  public final static double KARTEN_HOEHE = // ...;

  public MemoryKarte(
    double pLinks,
    double pOben,
    String pPfad) {

    super(pLinks, pOben, KARTEN_BREITE, KARTEN_HOEHE, pPfad);
    this.setzeBearbeiterGeklickt("aendereZustand");
  }

  public void aendereZustand() {
    if (this.isSichtbar()) {
      this.verstecke();
    }
    else {
      this.zeige();
    }
  }
}

Wenn das soweit korrekt ist, brauchen nur noch Objekte dieser Klasse erstellt und auf der Spielfläche angeordnet werden.

Um nun noch die eigentliche Memory-Logik hineinzubringen, könnte man alle Karten in einer Liste speichern. Entweder, ein separater Controller verwaltet folgend die Aktionen oder man schreibt die Logik einfach direkt in die aendereZustand-Methode.

Beispiel für a)

public class Spielverwalter {
  private List<MemoryKarte> karten;

  public Spielverwalter() {
    karten = new ArrayList<>(anzahlMemoryKarten);
  }

  public void fuegeHinzu(MemoryKarte karte) {
    karten.add(karte);
  }

  public void agiere(MemoryKarte aktivierteKarte) {
    // ...
  }
}

// Nutzung:
Spielverwalter verwalter = new Spielverwalter();
verwalter.fuegeHinzu(new MemoryKarte(/* position, etc. ... */, verwalter);
// fuege noch weitere Karten hinzu ...

Die Klasse MemoryKarte wiederum würde ein Feld hinzubekommen, welches eine Referenz dieses Verwalters hält und dessen agiere-Methode auch aufrufen, sobald sie angeklickt wurde.

public class MemoryKarte extends Bild {
  public final static double KARTEN_BREITE = // ...;
  public final static double KARTEN_HOEHE = // ...;

  private String bildPfad;

  private final Spielverwalter verwalter;
      
  public MemoryKarte(
    double pLinks,
    double pOben,
    String pPfad,
    Spielverwalter pVerwalter
  ) {
    super(pLinks, pOben, KARTEN_BREITE, KARTEN_HOEHE, pPfad);
    this.bildPfad = pPfad;
    this.verwalter = pVerwalter;

    this.setzeBearbeiterGeklickt("aendereZustand");
  }

  public void aendereZustand() {
    this.verwalter.agiere(this);

    // fuehre weitere Aktionen aus ...
  }

  public String gibBildPfad() {
    return this.bildPfad;
  }
}

Das heißt, in der agiere-Methode kann folglich die Spiellogik definiert werden, wenn eine Karte angeklickt wurde (aufdecken, zudecken, usw.).

Beispiel für b)

Statt einer separaten Verwalter-Klasse wird die Liste aller Karten einfach so an jede Karteninstanz übergeben und in einem Feld gespeichert.

List<MemoryKarte> karten = new ArrayList<>();
karten.add(new MemoryKarte(x, y, "pfad/zu/bild", karten));
// fuege noch weitere Karten hinzu ...

Die Spiellogik wird in aendereZustand definiert.

Diese Option ist zwar (zumindest bis hierhin) kürzer in der Umsetzung, aber die Spiellogik wird weniger gut abgekoppelt. Wenn die Liste an Karten bspw. leer ist (weil alle Paare gefunden wurden), möchte man ja z.B. eine Erfolgsmeldung an den Nutzer weitergeben. So eine Ausgabe hat aber m.E. nichts in der Klasse MemoryKarte zu suchen.

An sich wären noch striktere Abkopplungen der einzelnen Programmteile möglich (ein Stichwort: MVC), aber darauf werde ich an dieser Stelle nicht eingehen.


SCHEIBOMI 
Beitragsersteller
 05.10.2020, 05:12

Ich Danke so sehr!

Das mit den Methoden,dass sie von Bluej kommen wusste ich auch nicht.

Aber viel Dank, es hat mir sehr geholfen.

regex9  08.10.2020, 06:02
@SCHEIBOMI

SuM ist eine externe Bibliothek, um die BlueJ erweitert werden kann. Da ich gerade eine andere Frage bearbeitet habe, die ebenfalls mit SuM zutun hat, möchte ich dich auf diese (bzw. den Kommentar) kurz hinweisen:

https://www.gutefrage.net/frage/bluej-2#comment-266648769

Du verwendest im Unterricht vermutlich ebenso nicht SuM 7.1.3, sondern noch irgendeine Spezialimplementation die SuM erweitert (z.B. um eine wurdeGedrueckt-Methode für Knöpfe, u.ä.; vgl. einfach mal die Imports, evt. nutzt du auch nur das basis-Package?). Zu so einer Implementation kann ich jedenfalls keine Hilfestellung geben, da ich keinen Zugriff auf Docs o.ä. habe und daher nicht weiß, wie das Event Handling wirklich korrekt gehandhabt wird.

Zudem eine Korrektur meiner obigen Antwort: So, wie ich es mir mit aendereZustand (s. Antwort oben) vorgestellt habe, funktioniert es leider nicht. Wie ich gelernt habe, muss die Aktionsmethode bei SuM in einer Ereignisanwendung stehen (also einer Klasse, die bspw. von EBAnwendung erbt).

Was man bei SuM also machen könnte (ich gehe nun einmal nur von Option a aus - also der Implementation einer Verwalter-Klasse):

1) Die Methode aendereZustand kann aus der MemoryKarte raus und als Bearbeiter gibt man agiere an.

this.setzeBearbeiterGeklickt("agiere");

2) Das Feld verwalter braucht man in der Klasse ebenfalls nicht mehr. Der Konstruktor hat dann wieder einen Parameter weniger.

3) Der Spielverwalter erbt von EBAnwendung und der Parameter für die agiere-Methode wird entfernt. Die Methode muss parameterlos sein.

Aber man möchte ja dennoch erfahren, welches Bild gerade angeklickt wurde. Das geht glücklicherweise ziemlich einfach. Eine Hilfsmethode kann dabei helfen:

private MemoryKarte gibSelektierteKarte() {
  for (MemoryKarte karte : this.karten) {
    if (karte.besitztFokus()) {
      return karte;
    }
  }

  return null;
}

Die zuletzt angeklickte Komponente befindet sich auch im Fokus. Daher braucht man nur über alle registrierten Karten zu iterieren und zu schauen, welche denn gerade den Fokus hat.

Die Hilfsmethode ruft man dann nur noch in agiere auf:

public void agiere() {
  MemoryKarte aktivierteKarte = this.gibSelektierteKarte();

  if (aktivierteKarte == null) {
    return;
  }

  // ...
}