Java JButton und ActionListener in zwei verschiedenen Klassen?

4 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Am sinnvollsten wäre es, wenn du jedem Button ein actioncommand hinzufügst.

also

JButton button1 = new JButton("Eins setzen");
button1.setActionCommand("Eins");
JButton button2 = new JButton("Zwei setzen");
button1.setActionCommand("Zwei");
JButton start = new JButton("start");
button1.setActionCommand("Start");

dann reicht es auch, wenn du in der Mainklasse nur ein GameBoard Objekt erstellst und dieses bei jedem Button anmeldest.

In der Gameboard-klasse kannst du dann folgendes machen

public void actionPerformed(ActionEvent e)
{
  JButton button = (JButton) e.getSource();
  String command = button.getActionCommand();

  if(command.equals("Eins"))
    System....
  else if(command.equals("Zwei"))
    System....
  else if(command.equals("Start"))
    System....

}

Müsste so oder so ähnlich funktioniren. Hab es nicht ausprobiert und auch schon Jahre nicht mehr mit Swing gearbeitet. Aber ich hab das Gefühl, dass du das soweit verstehst.

Alternativ kannst du auch der GamesBoard-Klasse einfach eine JButton Referenz hinzufügen. Also

class Gameboard implements ActionListener
{
  private JButton button;

  Gameboard(JButton button)
  {
    this.button = button;
  }

  public void ActionListener(ActionEvent e)
  {
    System.out.println(button.getActionCommand())
  }
}

Beim zweiten hättest du halt diese if Abfrage nicht, allerdings müsstest du da 3 Gameboard Objekte erstellen und noch ne Referenz auf den jeweiligen Button halten. Kann man, wie ich finde, auch machen. Erstere Variante finde ich aber schöner. Bei beiden Varianten brauchst du allerdings den ActionCommand, der gesetzt werden muss.

Wie gesagt, habs jetzt nicht ausprobiert. Vllt hab ich kleine Fehler oder so, aber das bekommst du schon hin.


lkeyz 
Fragesteller
 05.04.2020, 20:22

Das mit Command hab ich auch schon gelesen, jetzt weiß ich endlich wie ich es auch richtig einsetzen kann, vielen Dank, ich werde es ausprobieren

0
lkeyz 
Fragesteller
 05.04.2020, 20:39
@lkeyz

Es hat geklappt, vielen dank, die Herangehensweise verstehe ich soweit, nur ist mir unschlüssig was die Jbutton und String command Zeile genau macht

0
GedankenGruetze  05.04.2020, 21:11
@lkeyz

Falls du bei der Frage, dieses Konstrukt meinst:

JButton button = (JButton) e.getSource();
  String command = button.getActionCommand();

So ist das die Antwort:

Wie du bei meiner Lösung siehst, kannst du mit dem setActionCommand("Irgendein String"); einem JButton ein Kommando zuordnen, welches du mit getActionCommand(); auslesen kannst. Dieses Kommando dient einfach nur zur Identifikation der Komponente, auf die gerade geklickt wurde.

Hast du also JButtons, so kannst du dem JButton so ein Kommando zuweisen und es in der actionPerformed() Methode abfragen, damit du dann mit so einer if else Abfrage dann herausfindest, auf welchen Button geklickt wurde.

Ich persönlich bevoruge es dann einfach den String einmal zwischenzuspeichern (hier in String command), anstatt in den if else Abfragen immer wieder getActionCommand(); aufzurufen.

Der Button ist, wie ich im nachhinein festgestellt habe, völlig unnötig. Ich dachte die Methode getActionCommand() gäbe es nur in der Klasse JButton. Allerdings hat auch die Klasse ActionEvent die getActionCommand() Methode.

Sprich, du kannst es auch so schreiben

public void ActionListener(ActionEvent e)
{
    String command = e.getActionListener();

  if{command.equals("Eins")
    ....
}
1
GedankenGruetze  05.04.2020, 21:16
@GedankenGruetze

Theoretisch ginge es noch einfach. Man kann die setActionCommand() Sache auch weglassen.

Wenn du kein setActionCommand explizit setzt, ist der ActionCommand die Beschriftung des Buttons. Also

JButton button = new JButton("Hallo")

String command = button.getActionCommand();
//command ist "Hallo"

Typischerweise wird allerdings ein ActionCommand explizit mit setActionCommand() gesetzt. Falls du beispw. später die Beschriftung deines Buttons änderst, dann musst du nicht bei deiner ActionListener Klasse in der if-Abfrage die Änderung eintragen, weil das ActionCommand i.d.R. ja einfach gleich bleibt.

1

Du musst in der Klasse am besten "getter" für die Buttons machen, weil er keine Variablen hat, auf die er sich beziehen kann

An sich gibt es da mehrere Lösungsmöglichkeiten.

a) Du gibst dem Listener ein Feld, welches bspw. über den Konstruktor gesetzt wird.

class GameBoardListener implements ActionListener {
  private String boundElementName;

  public GameBoardListener(String boundElementName) {
    this.boundElementName = boundElementName;
  }

  @Override
  public void actionPerformed(ActionEvent event) {
    System.out.printf("%s clicked", boundElementName);
  }
}

// attach:
buttonStart.addActionListener(new GameBoardListener("start"));

Beachte, dass ich hier den Namen der Listener-Klasse geändert habe. Ich gehe davon aus, dass dein GameBoard eigentlich eine View repräsentiert. Der Listener ist aber ein Controller und sollte dementsprechend nach Möglichkeit von der View getrennt werden (siehe MVC Pattern).

Mit den Lambda-Ausdrücken in Java ließe sich dieser Code auch folgendermaßen verkürzen:

buttonStart.addActionListener(() -> System.out.println("start was clicked"));

In älteren Java Versionen kann man stattdessen auch eine anonyme Klasse einsetzen.

buttonStart.addActionListener(new ActionListener() {
  @Override
  public void actionPerformed(ActionEvent event) {
    System.out.println("start was clicked");
  }
});

Allerdings würde ich diese Option so nur verwenden, wenn der Listener-Code kurz ist und bleibt.

b) Du vergibst deinen Elementen Namen. Die Option des ActionCommand-Methode wurde dir ja bereits vorgestellt, ansonsten wäre ebenso der Komponentenname möglich.

An dieser Stelle könnte man zudem gleich noch eine Enumeration einsetzen, um die Gleichheit der Namen zu garantieren:

// Actions.java
public enum Actions { START, ONE, TWO }

// in your view:
buttonStart.setName(Actions.START.name());

// in actionPerformed:
String name = ((Component)event.getSource()).getName();

if (Actions.START.name().equals(name)) {
  // ...
}

Diese Lösung, mehrere Komponenten mit einer ActionListener-Klasse abzufertigen, birgt allerdings Nachteile. Vor allem wird die Klasse schnell aufgebläht, wenn jeder Button etwas anderes tun soll. Es verstößt gegen das einfache Prinzip der separation of concerns und wird deshalb oft auch als bad practice benannt.

Besser ist es, wenn man für jeden Button, der eine individuelle Aktion fordert, eine eigene ActionListener-Klasse definiert. Wenn es mehrere Buttons gibt, die gleich reagieren, kann man explizit eine Klassendatei anlegen, andernfalls reichen die oben genannten anonymen Klassen aus.

Musst die Importieren z.B. über eine Hat beziehung o.ä glaube ich vom ersten mal denken

Kann auch sein das ich trashtalke

Woher ich das weiß:Studium / Ausbildung – Informatik Studium