views:

271

answers:

4

Say I have a swing GUI, and I want to listen MouseEvents. Who do you think should be the Listener class, who should be responsible? What is the best-or preferred way to implement it? Any opinions? I usually go like this:

public class MyPanel extends JPanel implements MouseListener{
    private JTable table;
    public void foo(){
         table.addMouseListener(this);
    }
    /* MouseListener */
    //Implement MouseListener here.
}

Is there a better way?


EDIT: Thank you everyone for the wisdom and help. I appreciate it.

+2  A: 

I would suggest to put the listener in its own class, to make it re-usable, and to have a clear separation of concerns

bertolami
(Also, don't extend `JPanel` (unless you mean it).)
Tom Hawtin - tackline
+1 as I second this, and see my example below.
Milan Ramaiya
+2  A: 

Look at Java's Swing code. That's really the best place to see good standards.

In your case, I'd have something like:

public class MyPanel extends JTable {

    public void foo() {
         addMouseListener(new MouseHandler() );
    }

    private class MouseHandler implements MouseListener {
       ...
    }
}

This way you have clear seperation of functionality. When your main class starts implementing fifteen different interfaces, your code will become completely unmaintainable.

Milan Ramaiya
A: 

anonymous classes are mainly used for listeners. If you know that you'll have a particular listener registered just once and it's rather short, then you can use anonymous class.

Luno
+2  A: 

There are a few common ways to do event listeners (the only one that I can think of that I left off in the code below is static inner classes). The code below uses ActionListener since it is simplest, but you can apply the idea to any listener.

Notice that the "this" way (having the class implement the listener) can lead to a huge if/else set of statements. I'd say this is the worst possible way because of that. I dislike having "clearing house" methods for two reasons:

1) they are large 2) it is tempting to do the work inside of the method rather than having each if/else call a method to do the work (which, as you can see, is what I did here... oops :-)

I also do not like the Anonymous way for two reasons:

1) you cannot easily re-use the code so you may find that you have duplicate code after a while 2) I find it breaks up the reading of the code (others disagree... personal taste). I think everyone would agree that if you are doing more than 5-10 lines that an anonymous inner class is not a good idea (I would say more than 2 is too much).

That leaves the inner and the outer ways. I would use the outer way when I am writing a listener that is not tightly tied to the GUI that it is listening. If the listener doesn't need information (member variables/methods) that are part of the class (TestFrame in this case) I would go for the outer class. In the example below I passed in the "this" so that the outer listeners could access the GUI... if I were to write code like that I would instead make it an inner class since it requires something from the GUI.

So, my order of preference is:

  • inner class (static if possible, but if you are making it static I'd go for the outer class)
  • outer class
  • anonymous inner class (rare)
  • have the class implement it itself (never would I do this. Never!)

And here is the code

import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;


public class Main
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(
            new Runnable() 
            {
                public void run() 
                {
                    createAndShowGUI();
                }
            });
    }

    private static void createAndShowGUI()
    {
        final TestFrame frame;

        frame = new TestFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(new Rectangle(10, 10, 300, 300));
        frame.init();
        frame.setVisible(true);
    }
}

class TestFrame
    extends    JFrame
    implements ActionListener
{
    private final JButton aBtn;
    private final JButton bBtn;

    public TestFrame()
    {
        super("Test");

        aBtn = new JButton("A");
        bBtn = new JButton("B");
    }

    public void init()
    {
        setLayout(new FlowLayout());
        add(aBtn);
        add(bBtn);

        // the class, since it implements ActionListener
        aBtn.addActionListener(this);
        bBtn.addActionListener(this);

        // outer classes
        aBtn.addActionListener(new OuterAListener(this));
        bBtn.addActionListener(new OuterBListener(this));

        // inner class
        aBtn.addActionListener(new InnerAListener());
        bBtn.addActionListener(new InnerBListener());

        // anonymous classes
        aBtn.addActionListener(
            new ActionListener()
            {
                public void actionPerformed(final ActionEvent e)
                {
                    System.out.println ("Hi from Anonymous A");
                }
            });

        bBtn.addActionListener(
            new ActionListener()
            {
                public void actionPerformed(final ActionEvent e)
                {
                    System.out.println ("Hi from Anonymous B");
                }
            });
    }

    public void actionPerformed(final ActionEvent evt)
    {
        final Object source;

        source = evt.getSource();

        if(source == aBtn)
        {
            System.out.println ("Hi from this A");
        }
        else if (source == bBtn)
        {
            System.out.println ("Hi from this B");
        }
        else
        {
            // ???
        }
    }

    private class InnerAListener
        implements ActionListener
    {
        public void actionPerformed(final ActionEvent e)
        {
            System.out.println ("Hi from Inner A");
        }
    }

    private class InnerBListener
        implements ActionListener
    {
        public void actionPerformed(final ActionEvent e)
        {
            System.out.println ("Hi from Inner B");
        }
    }
}

class OuterAListener
    implements ActionListener
{
    private final TestFrame frame;

    public OuterAListener(final TestFrame f)
    {
        frame = f;
    }

    public void actionPerformed(final ActionEvent e)
    {
        System.out.println ("Hi from Outer A");
    }
}

class OuterBListener
    implements ActionListener
{
    private final TestFrame frame;

    public OuterBListener(final TestFrame f)
    {
        frame = f;
    }

    public void actionPerformed(final ActionEvent e)
    {
        System.out.println ("Hi from Outer B");
    }
}
TofuBeer