PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java - ActionListener auslagern


Capt'N Coax
2004-04-29, 18:55:24
Hoi!

Für die Java Cracks hier:
Wie kann ich den ActionListener von meiner Gui trennen?

Angenommen ich erstelle eine Klasse CGui mittels JFrame und eine Klasse CControl die den Actionlistener implementiert. In der Hauptfunktion wird CControl erstellt, dann die Gui die CControl übergeben bekommt. Allerdings muss ich der Gui CControl ja auch bekanntmachen, mittels add() stehe ich da aber momentan auf dem Schlauch, da sich ein Actionlistener nicht direkt per add() Methode übergeben lässt.

Hat da einer eine Idee?
Wahrscheinlich ist die Lösung nicht schwer...

El Fantastico
2004-04-29, 19:36:47
Ok, die Frage ist jetzt vielleicht doof, aber warum nimmst Du nicht die addActionListener() Methode der jeweiligen Komponente?

Oder versteh ich Dich jetzt nicht :???:

HellHorse
2004-04-29, 20:16:54
Ehm, einem JFrame kann man kein ActionListener hinzufügen, da ein JFrame ein Knopf im weiteren Sinne ist.
Du kannst ihm aber diverse andere Listener hinzufügen. Bei welchen Ereignissen soll die CControl Instanz denn benachrichtigt werden?

Mit add() kann man einem JFrame nix hinzufügen. Bei einem Frame konnte man damit dem Fenster einen Komponenten hinzufügen. Willst du das bei einem JFrame machen, brauchst du die add Methode des ContentPanes.

Capt'N Coax
2004-04-29, 23:02:02
Die Add() Methode für Componenten ist bekannt. Ich beziehe mich einfach mal auf folgendes Tut:

Der dritte Punkt (Trennung Gui/Control) ist hier von interesse:

http://www.addison-wesley.de/Service/Krueger/kap18002.htm#E15E189


public MainFrameGUI(KeyListener cmd)
{
super("Nachrichtentransfer");
setBackground(Color.lightGray);
setSize(300,200);
setLocation(200,100);
setVisible(true);
addKeyListener(cmd);
}


Ein KeyListener kann also implementiert werden, MouseListener ebenso. Allerdings kein Actionlistener, das ist in diesem Kontext etwas verwirrend.
Mein Gedanke war, dann per MouseEvent einen Linksklik abzufangen und mittels Position den Auslöser zu bestimmen. Erscheint mir allerdings unlogisch und aus dem Zweck gerissen.

Auf der anderen Seite auch wieder logisch, das ein Frame durchaus auf Tastatur und Mauseingaben reagieren kann. Mir fällt aber im Moment keine funktionierende Art der Trennung ein (zwischen GUI und Steuerung)...

Da muss ich wohl nochmal die Codebank drücken.

Edit:
Stimmt, ich hab ganz vergessen das JFrame über ContentPane lüppt...Dabei erst vorige Woche noch gemacht.
Gut, ich schau mir das nochmal an...

HellHorse
2004-04-30, 08:52:49
Bevor du dich wieder fragst, warum es nicht geht.
Frame bekommt seit 1.4 KeyEvents nicht mehr mit.
http://www.forum-3dcenter.org/vbulletin/showthread.php?s=&postid=1660886#post1660886
Du musst also den KeyListener bei einem Komponentem im Frame registrieren.

Capt'N Coax
2004-04-30, 13:18:42
Gut, ich glaub insgesamt ist der rote Faden vielleicht auch etwas untergegangen.

Im Grunde geht es mir darum, das ich eine externe Steueungsklasse mit implementierten ActionListener habe. Wenn ich in der GuiKlasse also einem Button diese Steuerungsklasse übergebe (adde), die ja nun ActionListener ist, kann ich in der Steuerungsklasse natürlich nicht auf den Button vergleichen, da der Button der Steuerungsklasse nicht bekannt ist.


public class CSteuerung implements ActionListener
{
// Konstruktor
public CSteuerung()
{
}


public void actionPerformed(ActionEvent a_event)
{
Object EventSource = a_event.getSource();

if (EventSource == ButtonEnde)
{
//zwecks test
System.exit(0);
}
}
}


ButtoEnde ist in CSteuerung unbekannt.
Gebe ich mir das Actionevent Objekt mal aus, bekomme ich zwar alle möglichen Informationen, unter anderem den Text des Buttons und das es sich beim Auslöser um einen JButton handelt, allerdings nirgends eine Info WELCHER Button genau das ist. Ich kann also keinen Vergleich anstellen, um eine bestimmte Aktion dem Button zuzuweisen.

Der gleiche Code direkt in die GUI geschrieben funktioniert natürlich, aber ich möchte gerne eine Trennung haben.

Was ich im Grunde genommen also wissen möchte ist eine Möglichkeit extern auf den Auslöser zu prüfen. Die Textbezeichnung ist unbrauchbar, da ich nicht jeden Button beschriften kann und diese nicht eindeutig ist.

Ausgabe des ActionEvent-Objekts:

Source: javax.swing.JButton[,270,20,70x70,layout=javax.swing.OverlayLayout,alignmentX=0.0,alignmentY=0.5,bor der=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@1968e23,flags=296 ,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSel ectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=false,rolloverIco n=,rolloverSelectedIcon=,selectedIcon=,text=Test,defaultCapable=true]

Irgendwelche Anregungen?
Oder andere Vorschläge wie man GUI/Eventhandlig sinnvoll trennen kann?

HellHorse
2004-04-30, 15:25:13
Original geschrieben von Capt'N Coax
Im Grunde geht es mir darum, das ich eine externe Steueungsklasse mit implementierten ActionListener habe. Wenn ich in der GuiKlasse also einem Button diese Steuerungsklasse übergebe (adde), die ja nun ActionListener ist, kann ich in der Steuerungsklasse natürlich nicht auf den Button vergleichen, da der Button der Steuerungsklasse nicht bekannt ist.

Ich bin verwirrt und sehe nicht ganz was du eigentlich willst. Den auslösenden AbstractButton kriegst du ja über die getSource() Methode, wie du schon selbst rausgefunden hast.
Wenn du willst, dass eine Instanz einer Steuerungklasse nur von bestimmten Buttons benachrichtigt wird, dann registriere sie nur bei diesen.
Original geschrieben von Capt'N Coax
Gebe ich mir das Actionevent Objekt mal aus, bekomme ich zwar alle möglichen Informationen, unter anderem den Text des Buttons und das es sich beim Auslöser um einen JButton handelt, allerdings nirgends eine Info WELCHER Button genau das ist.

Ich bin verwirrt.
Es ist genau der AbstractButton, der getSource() liefert.

Original geschrieben von Capt'N Coax
Irgendwelche Anregungen?
Oder andere Vorschläge wie man GUI/Eventhandlig sinnvoll trennen kann?
Ja, Actions

public class ExitAction extends AbstractAction {

private static final String ACTION_COMMAND = "exit-command";
private static final String NAME = "Beenden";
private static final int MNEMONIC = 'B';
private static final KeyStroke ACCELERATOR = KeyStroke.getKeyStroke("control B");
private static final String SHORT_DESCRIPTION = "Beendet das Programm.";
private static final String LONG_DESCRIPTION =
"Clicken sie hier um das Programm zu beenden.";

private static final ImageIcon SMALL_ICON = new ImageIcon((ExitAction.class).getClassLoader().getResource("axon/res/Exit16.gif"));

public ExitAction() {
super(NAME);
this.putValue(Action.SHORT_DESCRIPTION, SHORT_DESCRIPTION);
this.putValue(Action.LONG_DESCRIPTION, LONG_DESCRIPTION);
this.putValue(Action.MNEMONIC_KEY, new Integer(MNEMONIC));
this.putValue(Action.ACCELERATOR_KEY, ACCELERATOR);
this.putValue(Action.ACTION_COMMAND_KEY, ACTION_COMMAND);
this.putValue(Action.SMALL_ICON, ExitAction.SMALL_ICON);
}

public void actionPerformed(ActionEvent event) {
Main.shutdown();
}
}


Action exitAction = new ExitAction();
JButton exitButton = new JButton(exitAction);
JMenuItem exitItem = new JMenuItem(exitAction);

Capt'N Coax
2004-04-30, 15:59:34
Gut, ich poste ein Beispiel:


public class MCISoft
{
public static void main(String[] S_Args)
{
// Erstelle das Steuerungsobjekt, das ALLE
// unsere Events auffängt
CSteuerung Steuerung=new CSteuerung();
// erstelle die Main Gui, von der aus das Programm
// gesteuert wird
CGuiMain GuiMain=new CGuiMain(Steuerung);


}// end main
}// end class



public class CGuiMain extends JFrame
{
// Variablen und GUI- Definitionen

// Components werden hier bekanntgegeben.
JButton ButtonEnde;

// Konstruktor
public CGuiMain(CSteuerung Steuerung)
{
// GuiMain Eigenschaften setzen
this.getContentPane().setLayout(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(600,400);

// Components initialisieren
ButtonEnde=new JButton("Test");
ButtonEnde.setSize(70, 70);
ButtonEnde.setLocation(270,20);
ButtonEnde.addActionListener(Steuerung);

// JFrame anzeigen
this.getContentPane().add(ButtonEnde);
this.setVisible(true);
}
}//end class



public class CSteuerung implements ActionListener
{
// Konstruktor
public CSteuerung()
{
}

public void actionPerformed(ActionEvent a_event)
{

Object EventSource = a_event.getSource();

if (EventSource == ButtonEnde)Fehler! nirgends definiert.
{
System.exit(0);
}
}
}// end class


Ich vermute jetzt mal das ich einen enormen Denkfehler habe was das eventModell angeht. Der obige Code funktioniert nicht.
Einfach deswegen, da "if (EventSource == ButtonEnde)" nciht funktioniert. ButtonEnde gibt es in der Klasse nicht. Kompilierfehler. Ich will in dieser Klasse aber alle Buttons behandeln die ich habe. Dafür muss ich diese aber in CSteuerung irgendwie identifizieren können. Dieser Code oben verursacht bei Knopfdruck zwar eine Action (wenn er kompilieren würde) und beendet das Programm, das würde aber bei jedem JButton so sein.

Man denke sich in obigen Beispiel einfach mehrere Knöpfe:


ButtonEnde=new JButton("Test");
ButtonEnde.setSize(70, 70);
ButtonEnde.setLocation(270,20);
ButtonEnde.addActionListener(Steuerung);

ButtonTest=new JButton();
ButtonTest.setSize(70, 70);
ButtonTest.setLocation(270,120);
ButtonTest.addActionListener(Steuerung);


Wie soll ich diese in ein und derselben Klasse CSteuerung behandeln, wenn ich in CSteuerung nur eine Info bekomme, das ein JButton gedrückt wurde, aber nicht welcher?

Falls ich diese Identifikation doch bekomme, sag mir wie, dann peile ich das wohl nicht. Aber abstrakt ist abstrakt, und zur Unterscheidung brauch ich ein konkretes Objekt oder eine Eigenschaft des Objekts zum Vergleich.

Wahrscheinlich hab ich mich verannt. Die Lösung mit den Actions würde natürlich funktionieren, sieht in meinen Augen aber sehr aufwendig aus, da ich für jeden Knopfdruck eine Action definieren müsste.

Mein Gedanke ist der, alle ActionEvents, die irgendwo geschmissen werden (gui), in CSteuerung aufzufangen, dort den Auslöser (ButtonEnde oder ButtonTest...) zu ermitteln und dementsprechend eine Aktion einzuleiten. So wies aussieht funktioniert das wohl nicht.

mithrandir
2004-04-30, 16:13:58
Du kannst ganz einfach ein ActionCommand setzen und dieses abfragen:


button.setActionCommand( "Holodaro!" );


In der actionPerformed()-Methode:


if ( "Holodaro!".equals( e.getActionCommand() )
{
}


Natürlich ganz schnell und möglicherweise syntaktisch nicht ganz korrekt.

bye, mith

HellHorse
2004-04-30, 16:46:36
Andere Möglichkeit:

public class CGuiMain extends JFrame
{
// Variablen und GUI- Definitionen

// Components werden hier bekanntgegeben.
JButton ButtonEnde;

// Konstruktor
public CGuiMain(CSteuerung Steuerung)
{
// GuiMain Eigenschaften setzen
this.getContentPane().setLayout(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(600,400);

// Components initialisieren
ButtonEnde=new JButton("Test");
ButtonEnde.setSize(70, 70);
ButtonEnde.setLocation(270,20);
ButtonEnde.addActionListener(Steuerung);
Steuerung.setButtonEnde(ButtonEnde);

// JFrame anzeigen
this.getContentPane().add(ButtonEnde);
this.setVisible(true);
}
}//end class


public class CSteuerung implements ActionListener
{
private JButton ButtonEnde;
// Konstruktor
public CSteuerung()
{
}

public void setButtonEnde(JButton ende) {
this.ButtonEnde = ende;
}

public void actionPerformed(ActionEvent a_event)
{

Object EventSource = a_event.getSource();

if (EventSource == ButtonEnde)Fehler! nirgends definiert.
{
System.exit(0);
}
}
}// end class

(Code ist nicht geprüft)

Dieser Code oben verursacht bei Knopfdruck zwar eine Action (wenn er kompilieren würde) und beendet das Programm, das würde aber bei jedem JButton so sein.

Jedem der mit dieser Action erstellt wird. Für andere Aktionen andere Actions machen.

Capt'N Coax
2004-04-30, 17:38:14
Yo, erstmal danke, die Vorschläge funktionieren.
Einfach eine Methode zu definieren die einen internen Button setzt ist natürlich so offensichtlich dass ich mal wieder nicht draufgekommen bin :eyes:

Ich schliesse allerdings aus diesem Beitrag das man Eventhandling SO nicht handhabt...
Diverse Tuts und Anleitungen ignorieren meistens völlig eine Auslagerung des Eventhandlings von der Darstellung des Fensters. Die Sache mit der Methode ist natürlich irgendwie getrickst und erfordert eine zusätzliche Componente...

Bei mithrandirs Methode mache ich mir Sorgen wenn ich von der Steurungsklasse aus Buttons der Gui beeinflussen will, da komme ich dann um ein wenig hin und her auch nicht rum.

Sauber ist die Implementierung weiter oben per Actions, ist für mein Projekt aber wahrscheinlich zu aufwendig.

Auf jeden Fall wieder was gelernt, danke @all,
Coax


ps. Bin natürlich für weitere Vorschläge rein interessehalber offen :)