I have a customized JButton in a Java Swing application.
it changes it appearances according to mouse event.
MouseEvent.MOUSE_ENTERED - will trigger the hover image for the button. MouseEvent.MOUSE_PRESSED - will trigger the pressed image.
MouseEvent.MOUSE_RELEASED - will change the foreground to gray and render the button disabled.
This is working fine with actual mouse clicks.
I want to add a support for pressing the ENTER key.
simply calling button.doClick() did not go through the hover-press-release cycle, but simply jumped to the release event.
So I have this short and effective code for doing this.
InputMap im = workspacePnl.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = workspacePnl.getActionMap();
im.put(KeyStroke.getKeyStroke("ENTER"), "connect");
am.put("connect", new ConectAction());
private class ConectAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent ev) {
simulateClick();
}
and the problematic code:
public void simulateClick() {
MouseEvent evt;
evt = new MouseEvent(connectBtn,
MouseEvent.MOUSE_ENTERED, 1, 0, 0, 0, 1, false);
connectBtn.dispatchEvent((AWTEvent) evt);
//CommonUtil.sleep(300);
evt = new MouseEvent(connectBtn,
MouseEvent.MOUSE_PRESSED, 8, 0, 0, 0, 1, false);
connectBtn.dispatchEvent((AWTEvent) evt);
//CommonUtil.sleep(300);
evt = new MouseEvent(connectBtn,
MouseEvent.MOUSE_RELEASED, 20, 0, 0, 0, 1, false);
connectBtn.dispatchEvent((AWTEvent) evt);
}
I am trying to make the ENTER press go through the same route: trigger a MOUSE_ENTERED event which will alter the button's appearance for hover, followed by MOUSE_PRESSED and MOUSE_RELEASED.
But I only see the last event effect. it is as if i'm only firing the last event alone which lacks the liveliness for an interactive software.
I tried (as can be seen commented out) to have the thread go to sleep after each event firing, but it has no effect.
If I try to fire each of the other two events they are noticeable on the screen by themselves. it's the batching together that messes things up.
How can I fire a series of dispatchEvents one by one which will all be noticed by the user? how can i make the program wait for the current dispatchEvent to work it's magic before striding on to the next on?
Any help or insights would be greatly appreciated.
How can I fire a series of dispatchEvents one by one which will all be
noticed by the user? how can i make the program wait for the current
dispatchEvent to work it's magic before striding on to the next on?
Mouse and Key Event are correctly implemented in ButtonComponents, don't use MouseListener, to use events from ButtonModel, by using ChangeListener, for example
MouseEvent.MOUSE_ENTERED - will trigger the hover image for the button.
Make use of the roll over support supplied by the button, see JButton#setRolloverEnabled & JButton#setRolloverIcon
MouseEvent.MOUSE_PRESSED - will trigger the pressed image.
Is a little more difficult, but you can use listener to the ButtonModel for changes and update the icon based on you requirements
MouseEvent.MOUSE_RELEASED - will change the foreground to gray and render the button disabled.
Should probably be achieved through the use a ActionListener
I am trying to make the ENTER press go through the same route:
JButton#doClick will go through the isArmed and isPressed states of the model automatically, which will trigger the state changes provided by the previous comments...
With the mouse...
With the keyboard...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
JButton btn = new JButton();
try {
btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash01.png"))));
btn.setRolloverIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash02.png"))));
btn.setRolloverEnabled(true);
// btn.setSelectedIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash03.png"))));
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
if (btn.getModel().isPressed()) {
try {
btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash03.png"))));
} catch (IOException ex) {
ex.printStackTrace();
}
} else {
try {
btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Trash01.png"))));
} catch (IOException ex) {
ex.printStackTrace();
}
}
System.out.println("Armed: " + btn.getModel().isArmed());
System.out.println("Enabled: " + btn.getModel().isEnabled());
System.out.println("Pressed: " + btn.getModel().isPressed());
System.out.println("Rollover: " + btn.getModel().isRollover());
System.out.println("Selected: " + btn.getModel().isSelected());
}
});
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(btn, gbc);
add(new JTextField("Stealer of focus"), gbc);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setEnabled(false);
}
});
InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke("ENTER"), "connect");
am.put("connect", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ev) {
System.out.println("click");
btn.doClick();
}
});
}
}
}
It's batching together because your whole code is running on the EventDispatchingThread (EDT).
When you call simulateClick() from actionPerformed() call it on a new thread instead of the same thread (which will be EDT).
Related
I have a java application that uses Swing graphics which has a 2D array of textfields, each of which are updating their text properly throughout the application. On each iteration of change, I change the text of the textfield then I make it's background green for half a second and turn it back to white. The issue is that, after the first iteration of the change, the textfields no longer flash green. When I comment out the portion that converts the background back to white, the rest works and the cells progressively turn green one by one (correctly), which indicates that it is working and executing properly. I tried to address this by repainting and revalidating the UI but it's not working. What is going on?
Below is my code that updates the UI.
try {
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
textfieldArray[row][col].setBackground(Color.green);
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
Thread.sleep(300);
textfieldArray[row][col].setBackground(Color.white);
textfieldArray[row][col].repaint();
textfieldArray[row][col].revalidate();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Swing is a single threaded framework, it also NOT thread safe. This means two things.
First, you should never perform any long running or blocking operations within the context of the Event Dispatching Thread and...
Secondly, you should never update/modify the UI, or anything the UI depends on, from outside the context of the Event Dispatching Thread.
This means, your Thread.sleep is blocking the EDT, preventing from process paint requests.
Instead, you need some way you can trigger an update to occur after a specified delay. If that doesn't scream Swing Timer, I don't know what does
I would highly recommend taking the time to read through Concurrency in Swing for the background of why your code isn't working and possibly How to Use Swing Timers for a solution
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextField field;
public TestPane() {
setLayout(new GridBagLayout());
field = new JTextField("All your bases beloging to us", 20);
JButton blink = new JButton("Blink");
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
field.setForeground(null);
field.setBackground(null);
}
});
timer.setRepeats(false);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(field, gbc);
blink.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
field.setForeground(Color.WHITE);
field.setBackground(Color.GREEN);
timer.start();
}
});
add(blink, gbc);
}
}
}
The first pass appears to either invalidate the drawable area or draws the background. And the second pass renders the menu. If there is any delay (as the example below exaserbates) then you get a grey square flickering effect.
This is JDK8 on Linux.
How can I stop this flicker effect?
public class MenuTester {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setBounds(100, 100, 300, 200);
final JButton button = new JButton("Show Menu");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem("aaaa"));
popupMenu.add(new JMenuItem("bbbb"));
popupMenu.add(new JMenuItem("cccc"));
popupMenu.setLocation(100, 100);
popupMenu.setVisible(true);
try {
Thread.sleep(2000); // Leave enough time to clearly see the ?invalidated/background? area.
} catch (InterruptedException ex) {
// Nothing to do
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// Hide after 1 second
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
// Nothing to do
}
popupMenu.setVisible(false);
}
});
}
});
frame.add(button);
frame.setVisible(true);
}
}
Swing has, for as long as I remember it, had a "delay" when showing windows, this might have do with the time between the frame been realised by the OS and connection of the native message and event queues, but this is pure observation
I took you code and by simply by wrapping the frame's creation into a EventQueue.invokeLater was able to get a similar behaviour
You will get different results on different systems depending on there system up and configurations
What is the event that causes the window to be rendered in the first pass?
All I did was took your code and wrapped the creation the UI in an EventQueue.invokeLater, for example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MenuTester {
public static void main(String[] args) {
new MenuTester();
}
public MenuTester() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
final JFrame frame = new JFrame();
frame.setBounds(100, 100, 300, 200);
final JButton button = new JButton("Show Menu");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem("aaaa"));
popupMenu.add(new JMenuItem("bbbb"));
popupMenu.add(new JMenuItem("cccc"));
popupMenu.setLocation(100, 100);
popupMenu.setVisible(true);
// try {
// Thread.sleep(2000); // Leave enough time to clearly see the ?invalidated/background? area.
// } catch (InterruptedException ex) {
// // Nothing to do
// }
// SwingUtilities.invokeLater(new Runnable() {
// #Override
// public void run() {
// // Hide after 1 second
// try {
// Thread.sleep(1000);
// } catch (InterruptedException ex) {
// // Nothing to do
// }
// popupMenu.setVisible(false);
// }
// });
}
});
frame.add(button);
frame.setVisible(true);
}
});
}
}
And is there a way to render the window as transparent during that first pass and opaque during the 2nd?
This is not a new problem, this has been the state of affair since I started with Swing at Java 1.3. What you're asking would mean you knew when the paint pass was done AND was complete. Swing isn't altogether stupid, it can make some clever decisions in order to optimise the rendering process (like not painting components which are visible)
The other problem is, with a JPopupMenu, you don't actually know if it's been displayed in a window or not (or just been displayed as a component on the glass pane for example) so the whole thing is woefully complicated
For some reason my jFrame no longer pops up after I add the menu. Is there something I am missing? I'm trying to make a menu that pops up before the beginning of the game and has buttons "play" as well as a text box that allows the user to input a username.
Any suggestions for how I could fix my code? Thank you!
this is my Menu class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JPanel;
public class Menu extends JPanel{
private static final long serialVersionUID = 1L;
public Menu() {
JPanel buttonPanel = new JPanel(new GridLayout());
JButton play = new JButton();
JButton help = new JButton();
buttonPanel.add(play);
buttonPanel.add(help);
setFocusable(true);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Game.started = true;
}
});
}
public void paint (Graphics g) {
super.paint(g);
g.setColor(Color.black);
g.fillRect(400, 400, Game.WIDTH, Game.HEIGHT);
}
}
and this is my Main class from which I run my program:
import java.awt.BorderLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main implements Runnable{
public void run() {
final JFrame frame = new JFrame();
frame.setTitle("Flying Square");
frame.setSize(Game.WIDTH, Game.HEIGHT);
//The menu
final Menu menu = new Menu();
final Game game = new Game();
frame.add(menu, BorderLayout.CENTER);
menu.setVisible(true);
try {while (Game.started == false) {
Thread.sleep(10);
}} catch (InterruptedException e){
e.printStackTrace();
}
frame.remove(menu);
//Main playing area
frame.add(game, BorderLayout.CENTER);
game.setVisible(true);
frame.revalidate();
// Put the frame on the screen
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// add listeners
frame.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
flyingObject.jump();
}
});
frame.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE)
{
flyingObject.jump();
}
}
});
// Start game
Game.reset();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Main());
}
}
So, this...
try {while (Game.started == false) {
Thread.sleep(10);
}} catch (InterruptedException e){
e.printStackTrace();
}
Is blocking the Event Dispatching Thread, preventing it from processing any events and basically causing your program to hang.
This is not how you want to process responses from the user. Your Menu should be monitoring for input from the user, probably through one or more ActionListeners, when an action is triggered, it should be notifying some kind of controller, the controller can then make decisions about what it needs to do, like switch the panels for example
You're going to want to break your code down into at least three chunks, the "game" the "menu" and the "controller", this way it will be easier to manage, rather than trying to retrofit the functionality into an existing code
It would recommend having a look at
How to Use CardLayout to help you facilite the switching of the view
How to Use Key Bindings instead of KeyListener
Model-View-Controller
Observer Pattern
in a java aplication I have a Jlabel which i want to assign a new image to every time i click a button, using a for loop i can get it to just display the last image skipping all in between images, i know there is a error in my logic here maybe i should not be using a for loop?? any advice
private String imageList[];
ImageIcon image;
imageList = new String[] {"src\\Tour_Eiffel_Wikimedia_Commons.jpg","src\\Ben.jpg", "src\\Rio.jpg", "src\\Liberty.jpg", "src\\Pyramid.jpg"};
//constructor setting first image to display on load
public GeographyGameGUI() {
image = new ImageIcon(imageList[0]);
imageLbl.setIcon(image);
}
//button method
private void nextBtnActionPerformed(java.awt.event.ActionEvent evt) {
for (imgCount = 1; imgCount < imageList.length; imgCount++) {
image = new ImageIcon(imageList[imgCount]);
imageLbl.setIcon(image);
}
if i dont use a for loop and simply use a counter (displayed below) which i declare outside of the button method it loops correctly displaying the images but runs into a ArrayIndexOutOfBoundsException. what is the best practice here? thanks
image = new ImageIcon(imageList[imgCount]);
imageLbl.setIcon(image);
imgCount++;
You're, essentially, blocking the Event Dispatching Thread, prevent it from updating the UI. See Concurrency in Swing for more details
Instead, you should use a javax.swing.Timer to loop over the images, allowing the UI to update before changing to the next one...
See How to use Swing Timers for more details.
Java arrays are zero indexed, this means that the first element in the array is a position 0, not 1
Don't reference src directly within your code, the src directory will not exist once the application is built and packaged
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private String[] imageList = new String[] {"/Tour_Eiffel_Wikimedia_Commons.jpg","/Ben.jpg", "/Rio.jpg", "/Liberty.jpg", "/Pyramid.jpg"};
public TestPane() {
setLayout(new BorderLayout());
label = new JLabel();
add(label);
JButton btn = new JButton("Play");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setEnabled(false);
Timer timer = new Timer(1000, new ActionListener() {
private int count;
#Override
public void actionPerformed(ActionEvent e) {
if (count < imageList.length) {
try {
label.setIcon(
new ImageIcon(
ImageIO.read(
TestPane.this.getClass().getResource(imageList[count]))));
} catch (IOException exp) {
exp.printStackTrace();
}
count++;
} else {
((Timer)e.getSource()).stop();
}
}
});
timer.stop();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Your counter reaches the end of the array so you get out of bounds exception. After each increment you should check whether the end of array has been reached, and if so, set the counter to 0.
If you want to iterate over a few images with a delay on single click you need to use SwingWorker. Using delays in your action listener will suspend event dispatch thread, which means that no other updates or interactions with swing components will be available (it is likely that refreshes will not be done correctly too).
If you do a few updates (setIcon) in a very short time, Swing usually refreshes the component after the last of them, which means that only last image will be visible.
Have a look here: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
I have a Swing application that deals with date and time, so a lot of tests are done changing the system's date and time settings.
During the tests, we noticed that after decreasing the clock, the first click is ignored by the application.
Is it a bug of Swing/Java/Windows? Is there a workaround to this?
Interestingly, this issue only happens when decreasing the date/time settings. If I increase it, the application behaves normally.
Situation:
Swing application running.
Decrease Windows date and time settings (e.g. change time from 15:00 to 14:00).
Notice that the first click in the Swing application does not fire any action.
Code example (you can use it to testify the situation):
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
final JFrame frame = new JFrame("frame");
final JButton button = new JButton("button");
button.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
System.out.println("Button Pressed!");
}
});
frame.add(button);
frame.setSize(200, 200);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent e) {
System.exit(0);
}
});
}
}
As seen here the Swing uses a date to check when the event occoured. So, in some way, probally a handler is acting here, by discarting your action, since it happened "before" the last action. I can't confirm you this, but probally some Layout Manager or another handler is messing with something here to prevent delayed events to mess up with the current flow.
I've debugged it via Eclipse and found out what is happening.
Clock at 15:00h.
Click at the button. Swing record last event time to 15:00.
Change the clock to 14:00h.
Click at the button. Swing ignores the event because it looks like a multi-click.
The problem here is that the comparison made by Swing checking for multi-click is this:
if (lastTime != -1 && currentTime - lastTime < multiClickThreshhold) {
shouldDiscardRelease = true;
Here, currentTime - lastTime yields a negative value. It's less than 0 (my multiClickThreshhold), so it does not fire the action event:
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
// Support for multiClickThreshhold
if (shouldDiscardRelease) {
shouldDiscardRelease = false;
return;
}
AbstractButton b = (AbstractButton) e.getSource();
ButtonModel model = b.getModel();
model.setPressed(false);
model.setArmed(false);
}
}
All the source listed above is in javax.swing.plaf.basic.BasicButtonListener.
The Button class does have a setMultiClickThreshhold, but it throws IllegalArgumentException in case the threshhold is less than 0.
So, as a workaround, I did this:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.lang.reflect.Field;
import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) throws Exception {
final JFrame frame = new JFrame("frame");
final JButton button = new JButton("button");
removeMulticlickThreshold(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
System.out.println("Button Pressed!");
}
});
frame.add(button);
frame.setSize(200, 200);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent e) {
System.exit(0);
}
});
}
private static void removeMulticlickThreshold(final JButton button) throws Exception {
final Field multiClickThreshhold = AbstractButton.class.getDeclaredField("multiClickThreshhold");
multiClickThreshhold.setAccessible(true);
multiClickThreshhold.set(button, Long.MIN_VALUE);
}
}