Componenten und Listener

Wie findet man heraus, welche Component einen Event ausgelöst hat?

Wenn man mehrere Components erstellt, die eine “ähnliche” Funktion besitzen, möchte man sie häufig gleich behandeln. Trotzdem muss man wissen, von welcher Component ein Event stammt, den man behandeln will. Es gibt für dieses Problem im wesentlichen zwei verschiedene Lösungsansätze. Diese werden hier am Beispiel von Buttons und ActionEvents kurz beschrieben. Man kann sie aber leicht auf andere Components und Events übertragen.

  1. Möglichkeit:

Man verwendet einen einzigen Listener (genauer: nur eine Instanz eines Listeners) für alle Components. In der Methode, die den Event behandelt (z.B. in actionPerformed) muss man dann abfragen, von welcher Component der Event ausgelöst wurde. Am einfachsten geht das, indem man mit event.getSource() die Quelle des Events abfragt:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
class ButtonsFAQ01a extends JFrame implements ActionListener
{
    public static void main(String args[])
    {
        new ButtonsFAQ01a();
    }
 
    // Die Buttons in dieser Klasse
    private JButton button0;
    private JButton button1;
    private JButton button2;
 
    // Erstellt diesen Frame und fügt das Panel mit den Buttons hinzu
    public ButtonsFAQ01a()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300,100);
        getContentPane().add(createButtonsPanel());
        setVisible(true);
    }
 
    // Erzeugt ein Panel, das die Buttons enthält
    private JPanel createButtonsPanel()
    {
        JPanel buttonsPanel = new JPanel(new GridLayout(1,0));
 
        // Erstelle die Buttons
        button0 = new JButton("button0");
        button1 = new JButton("button1");
        button2 = new JButton("button2");
 
        // Füge 'this' als ActionListener zu allen Buttons hinzu
        button0.addActionListener(this);
        button1.addActionListener(this);
        button2.addActionListener(this);
 
        // Lege die Buttons in das Panel
        buttonsPanel.add(button0);
        buttonsPanel.add(button1);
        buttonsPanel.add(button2);
 
        return buttonsPanel;
 
    }
 
    // Wird aufgerufen, wenn irgendeiner der Buttons geklickt wurde
    public void actionPerformed(ActionEvent event)
    {
        // ßberprüfe, welcher der Buttons die Quelle des Events war
        if (event.getSource() == button0)
        {
            System.out.println("button0");
            button0.setEnabled(false);
        }
        else if (event.getSource() == button1)
        {
            System.out.println("button1");
            button1.setEnabled(false);
        }
        else if (event.getSource() == button2)
        {
            System.out.println("button2");
            button2.setEnabled(false);
        }
    }
 
}

Häufig liegen die Components, um die es geht, in einem Array. In diesem Fall kann man das Erzeugen der Components, und die Abfrage, von welcher Component der Event ausgelöst wurde, in einer Schleife machen:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
class ButtonsFAQ01b extends JFrame implements ActionListener
{
    public static void main(String args[])
    {
        new ButtonsFAQ01b();
    }
 
    // Die Buttons in dieser Klasse
    private JButton buttons[];
 
    // Erstellt diesen Frame und fügt das Panel mit den Buttons hinzu
    public ButtonsFAQ01b()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300,100);
        getContentPane().add(createButtonsPanel());
        setVisible(true);
    }
 
    // Erzeugt ein Panel, das die Buttons enthält
    private JPanel createButtonsPanel()
    {
        JPanel buttonsPanel = new JPanel(new GridLayout(1,0));
 
        // Erstelle die Buttons, füge 'this' als ActionListener zu allen
        // Buttons hinzu, und lege die Buttons in dieses Panel
        buttons = new JButton[3];
        for (int i=0; i<buttons.length; i++)
        {
            buttons** = new JButton("button"+i);
            buttons**.addActionListener(this);
            buttonsPanel.add(buttons**);
        }
 
        return buttonsPanel;
 
    }
 
    // Wird aufgerufen, wenn irgendeiner der Buttons geklickt wurde
    public void actionPerformed(ActionEvent event)
    {
        // ßberprüfe, welcher der Buttons die Quelle des Events war
        for (int i=0; i<buttons.length; i++)
        {
            if (event.getSource() == buttons**)
            {
                System.out.println("button["+i+"]");
                buttons**.setEnabled(false);
            }
        }
    }
 
}

Wenn die Components in einer Liste liegen, kann man statt der Schleife auch die Methode ‘List#indexOf’ verwenden:

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

class ButtonsFAQ01c extends JFrame implements ActionListener
{
    public static void main(String args[])
    {
        new ButtonsFAQ01c();
    }

    // Die Buttons in dieser Klasse
    private List<JButton> buttons;

    // Erstellt diesen Frame und fügt das Panel mit den Buttons hinzu
    public ButtonsFAQ01c()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300,100);
        getContentPane().add(createButtonsPanel());
        setVisible(true);
    }

    // Erzeugt ein Panel, das die Buttons enthält
    private JPanel createButtonsPanel()
    {
        JPanel buttonsPanel = new JPanel(new GridLayout(1,0));

        // Erstelle die Buttons, füge 'this' als ActionListener zu allen
        // Buttons hinzu, und lege die Buttons in dieses Panel
        buttons = new ArrayList<JButton>();
        for (int i=0; i<3; i++)
        {
            JButton button = new JButton("button"+i);
            buttons.add(button); 
            button.addActionListener(this);
            buttonsPanel.add(button);
        }

        return buttonsPanel;

    }

    // Wird aufgerufen, wenn irgendeiner der Buttons geklickt wurde
    public void actionPerformed(ActionEvent event)
    {
        // Überprüfe, welcher der Buttons die Quelle des Events war
        int index = buttons.indexOf(event.getSource());
        System.out.println("button["+index+"]");
        buttons.get(index).setEnabled(false);
    }

}
  1. Möglichkeit:

Man verwendet anonyme Listener, die jeweils nur auf eine Component hören. Der Vorteil dabei ist die höhere Flexibilität, und dass man nicht die Quelle des Events abfragen muss: Jeder Listener kennt automatisch “seine” Component.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
class ButtonsFAQ02 extends JFrame
{
    public static void main(String args[])
    {
        new ButtonsFAQ02();
    }
 
    // Erstellt diesen Frame und fügt das Panel mit den Buttons hinzu
    public ButtonsFAQ02()
    {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300,100);
        getContentPane().add(createButtonsPanel());
        setVisible(true);
    }
 
    // Erzeugt ein Panel, das die Buttons enthält
    private JPanel createButtonsPanel()
    {
        JPanel buttonsPanel = new JPanel(new GridLayout(1,0));
 
        for (int i=0; i<3; i++)
        {
            // Erstelle den Button und merke dir den index 'i'
            // Beides müssen 'final' Variablen sein, damit aus
            // der anonymen inneren Klasse darauf zugegriffen
            // werden kann.
            final JButton button = new JButton("button"+i);
            final int number = i;
 
            // Füge einen anonymen ActionListener zu dem Button hinzu
            button.addActionListener(new ActionListener()
            {
                // Wird aufgerufen, wenn der Button 'button' geklickt wurde
                public void actionPerformed(ActionEvent event)
                {
                    System.out.println("button"+number);
                    button.setEnabled(false);
                }
            });
            buttonsPanel.add(button);
        }
 
        return buttonsPanel;
    }
 
}

(Dieser Beitrag entspricht dem Beitrag aus dem java-forum.org. Dort war der Name des Autors entfernt - und dieser Autor war ursprünglich ich)

Ich bevorzuge eindeutig die Moeglichkeit 2.

Erstens entfaellt dieses haessliche if-else-if Konstrukt und zweitens sieht Moeglichkeit 2 einfach schoener aus :smiley:

Ja, die empfehle ich auch immer. Aber … diejenigen, an die sich das richtet, werden wohl eher vom ersten ausgehen. D.h. die haben im Zweifelsfall schon einen
JButton array[];
(wenn nicht gar das berühmte
JButton btn1;
JButton btn2;

JButton btn45;
:wink: ) und da ist das vielleicht erstmal „zugänglicher“.

Ich würde noch ein break an das Ende der Schleife setzen, jedes weitere Suchen im Array ist ja damit überflüssig.
Und in JavaFX gibt es noch eine weitere Möglichkeit, Komponenten zu identifizieren, nämlich über ein Objekt, das man jeder Komponente mit setUserData() mitgeben kann. Nur der Vollständigkeit halber.