How to temporarily disable event listeners in Swing?

38,301

Solution 1

You could use a common base class for your listeners and in it, have a static method to turn the listeners on or off:

public abstract class BaseMouseListener implements ActionListener{

    private static boolean active = true;
    public static void setActive(boolean active){
        BaseMouseListener.active = active;
    }

    protected abstract void doPerformAction(ActionEvent e);

    @Override
    public final void actionPerformed(ActionEvent e){
        if(active){
            doPerformAction(e);
        }
    }
}

Your listeners would have to implement doPerformAction() instead of actionPerformed().

(This would be awful in an enterprise scenario, but in a single-VM model like in Swing, it should work just fine)

Solution 2

Normally I use a flag indicating API changes or user changes. For each of the listeners I would check the flag and if it's API changes just return.

Solution 3

While searching stackoverflow, I found this question. I thought to add my opinion/answer.

It is really^inf bad idea to temporarily disable event listeners in Swing. If your code is broken (or something else goes wrong), you may not be able to bring your application back to life - respond to user and other events.

If you want to discard (respond but do nothing) to user events, you may use glass pane which can just ignore the events.

If your EDT is busy (which again you must avoid as much as possible) and you wanted to discard user action for that period, you may still use a glasspane and remove it using invokeLater to remove the pane after all the events have been responded (ignored by the glasspane) to.

Full details including an SSCE can be found in this question.

java wait cursor display problem

Solution 4

One option that might work for you is just to put a glass pane up while loading in order to block events during that time: http://download.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane

Solution 5

As mentioned above, the GlassPane is helpful in this regard. Here is a simple example:

import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.SwingWorker;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;


public class GlassPaneExample extends JFrame implements ActionListener {

private JButton btnDisable;
private JButton btnTestOne;
private JButton btnTestTwo;
private MyGlassPane glass;
private boolean actionAllowed = true;

public GlassPaneExample() {

    // init JFrame graphics
    setBounds(300, 300, 300, 110);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLayout(new FlowLayout());
    setVisible(true);

    // init buttons
    btnTestOne = new JButton("Button one");
    add(btnTestOne);
    btnTestTwo = new JButton("Button two");
    add(btnTestTwo);
    btnDisable = new JButton("Disable ActionListeners for 2 seconds");
    add(btnDisable);

    // create Glass pane
    glass = new MyGlassPane();
    setGlassPane(glass);

    // add listeners
    btnTestOne.addActionListener(this);
    btnTestTwo.addActionListener(this);
    btnDisable.addActionListener(this);

}

public static void main(String[] args) {
    new GlassPaneExample();
}

@Override
public void actionPerformed(ActionEvent e) {
    JButton src = (JButton)e.getSource();
    if (src.equals(btnDisable)) {

        // setting glasspane visibility to 'true' allows it to receive mouse events
        glass.setVisible(true);
        setCursor(new Cursor(Cursor.WAIT_CURSOR));

        SwingWorker sw = new SwingWorker() {

            @Override
            protected Object doInBackground()
                    throws Exception {
                Thread.sleep(2000);
                return null;
            }

            @Override
            public void done() {
                // set cursor and GlassPane back to default state
                setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                glass.setVisible(false);
                // allow actions to be received again
                actionAllowed = true;
            }
        };
        sw.execute();

    } else if (actionAllowed) {
        if (src.equals(btnTestOne)) {
            JOptionPane.showMessageDialog(this, "BUTTON ONE PRESSED");
        } else if (src.equals(btnTestTwo)) {
            JOptionPane.showMessageDialog(this, "BUTTON TWO PRESSED");
        }
    }
}

class MyGlassPane extends JPanel {

    public MyGlassPane() {

        setOpaque(false);

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                actionAllowed = false;
            }
        });
    }

    //Draw an cross to indicate glasspane visibility 
    public void paintComponent(Graphics g) {  
      g.setColor(Color.red);  
      g.drawLine(0, 0, getWidth(), getHeight());  
      g.drawLine(getWidth(), 0, 0, getHeight());
   }
}

}

Share:
38,301
MicSim
Author by

MicSim

Jack of all trades

Updated on September 25, 2020

Comments

  • MicSim
    MicSim over 3 years

    I've got a Swing application with a model and a view. In the view (GUI) there are a lot of components, each of them mapping to some property of a model object and displaying it's value.

    Now there are some UI components that automatically trigger the updating of some model properties when their value changes in the UI. This requires me to reload the complete model in the UI. This way I'm entering an infinite update loop, as every model reload in the UI triggers another model reload.

    I have a flag indicating the load process, which I'd like to use to temporarily suppress the listener notifications, while the UI fields are being set from the model. So my question is:

    Is there a way to globally temporarily disable some component's listeners in Swing without removing and reattaching them?