Java ActionListener buttonPress() restriction - java

Is there a way to restrict this button to only being impressed once? The reason I ask is because for some reasons every time the button is pressed it disrupts the rest of my code. So in effort to save a massive amount of time debugging, it would be much easier to just somehow restrict the number of times it can be pressed. Thanks in advance.
ActionListener pushButton = new buttonPress();
start.addActionListener(pushButton);

To prevent clicking a button you can use JButton.setEnabled(false). So you could do this as the first statement in your ActionListener.
An alternative would be to set a flag in your ActionListener like so:
final ActionListener pushButton = new ActionListener()
{
private boolean clicked;
public void actionPerformed(final ActionEvent e)
{
if(clicked)
{
JOptionPane.showMessageDialog(null, "Action already started");
return;
}
clicked = true;
// ... rest of the action to do ...
}
}
Note that you should not execute long running tasks in your event handler, see design considerations to keep in mind when implementing event handlers in The Java Tutorials.

Related

Code execution from Button without ActionListener

So i have a button in my program (made with java.awt.Button) and I want to execute some code when i press the button. But i don't want to do this with an ActionListener. Is there a way to avoid the ActionListener?
You could override the processActionEvent(ActionEvent e) method, to execute the code.
Button btn = new Button("No Listener Button") {
void processActionEvent(ActionEvent e) {
// "some code" here
}
};
Note that doing this will create a new anonymous class for every button you create this way, so it is not really a Good Idea™. But it will do what you are asking; buyer beware.

Make button available after certain time?

So I have a method to create a button. I'd like that button to be activated every 120 (2 min) seconds. Once the button is clicked, the timer is reset.
addButton( new RedButton(TXT_SATURNUP) {
#Override
public void onClick() {
Game.instance.showAd();
Game.instance.wasAnswerCorrect();
}
});
If possible, I'd also like to be able to have a log message. Say...
if ( timerFinished ) {
// log message to screen informing user
} else {
// Button cannot be clicked at this time
}
You can use a Timer. You will have to set it up with an ActionListener that enables your button, and you button click will have to reset your timer.
Given the interconnections I would make a new button class using the template below:
class TimedButton extends JButton implements ActionListener {
private Timer T;
// Override constructors to set up timer
// and disable button (if/as desired)
public void onClick(){
super.onClick();
setEnabled(false);
T.restart();
}
public void actionPerformed(ActionEvent e){
setEnabled(true);
}
}
Then you need a method to start the timer externally, or a way to start the timer internally. You might want to avoid doing this in your constructor, as the timer may run out before you make the button is visible. It all just depends on when the button is first available.
As for the logging you can add it in the above methods or use isEnabled().

Mouse Clicks being cached?

I have a java swing application with a login screen. The login screen has a submit button for pressing after the user's credentials have been entered. When the button is pressed, the a wait cursor is thrown up over the window using its glass pane. There is also a default mouse adapter that does nothing for any mouse action.
private final static MouseAdapter mouseAdapter =
new MouseAdapter() {};
/** Sets cursor for specified component to Wait cursor */
public static void startWaitCursor(JComponent component) {
log.debug("startWaitCursor()");
RootPaneContainer root =
((RootPaneContainer) component.getTopLevelAncestor());
Component glass = root.getGlassPane();
glass.setCursor(WAIT_CURSOR);
glass.addMouseListener(mouseAdapter);
glass.setVisible(true);
//force repaint of glass pane in 20ms for more responsive GUI
glass.repaint(20);
}
public static void stopWaitCursor(JComponent component) {
log.debug("stopWaitCursor()");
RootPaneContainer root =
((RootPaneContainer) component.getTopLevelAncestor());
Component glass = root.getGlassPane();
glass.setCursor(DEFAULT_CURSOR);
glass.removeMouseListener(mouseAdapter);
//force repaint of glass pane in 20ms for more responsive GUI
glass.repaint(20);
glass.setVisible(false);
}
I had assumed that this setup protected me against multiple clicks/keypresses while the backend methods were taking place. I found out that this was not the case. So in the ButtonListener.actionPerformed, I put some logic like the following:
static boolean waiting = false;
class ButtonListener implements ActionListener {
ButtonListener() {
super();
}
public void actionPerformed(ActionEvent e) {
log.info("LoginWindow.ButtonListener.actionPerformed()");
LoginWindow.this.repaint(50);
if (!waiting) {
try {
waiting = true;
verifyLogin();
} finally {
waiting = false;
}
}
}
}
I found that this protected me against keypresses, but not mouse clicks! If I repeatedly press the submit button while verifyLogin() is executing, the mouse clicks seem to be being cached somewhere, and after verify login finishes, each mouse click is processed!
I am extremely puzzled about what is going on here. Does someone have an idea?
Update:
Hmm, by following the methodology suggested by Cyrille Ka: i.e. executing the verifyLogin() method in a separate thread and disabling the button, I now only get TWO events after multiple mouse clicks but the second one still annoys.
Code is now:
public void actionPerformed(ActionEvent e) {
loginButton.setEnabled(false);
log.infof("LoginWindow.ButtonListener.actionPerformed(). Event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
LoginWindow.this.repaint(50);
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
verifyLogin();
loginButton.setEnabled(true);
}});
}
but the second event still gets in. My log shows me that the second event took place about 280 ms after the first, but did not execute until 4 seconds later, in spite of the fact that setEnabled() was the first thing the actionPerformed() event did.
2013-11-13 10:33:57,186 [AWT-EventQueue-0] INFO
c.a.r.s.c.g.LoginWindow -
LoginWindow.ButtonListener.actionPerformed(). Event occurred at Nov 13
2013 10:33:57.175 2013-11-13 10:34:01,188 [AWT-EventQueue-0] INFO
c.a.r.s.c.g.LoginWindow -
LoginWindow.ButtonListener.actionPerformed(). Event occurred at Nov 13
2013 10:33:57.453
I suppose I could do a hack and discard events over a second old or something, but that feels ugly. This should not be so difficult, I keep thinking.
Update 2:
comment from JComponent.java for setEnabled()
* <p>Note: Disabling a lightweight component does not prevent it from
* receiving MouseEvents.
Since all of the Swing components are lightweight, and setEnabled does not prevent the component from receiving mouse events, what does prevent this?
I had assumed that this setup protected me against multiple clicks/keypresses while the backend methods were taking place. I found out that this was not the case.
The section from the Swing tutorial on The Glass Pane gives an example of how you might do this. Don't remember if it only handles MouseEvents or KeyEvents as well.
In any case you can also check out Disabled Glass Pane, which does handle both events.
I presume verifyLogin() is blocking until the login is done. By doing this, you are just blocking the Swing event dispatcher thread. The events from the OS still are queuing to be sent to your GUI when the thread will be available.
There are two ways to prevent your user clicking repeatidly:
Just disable the button: button.setEnabled(false); and enable it back when the process is finished.
Launch a modal dialog (for example with a wait animation) and remove it when the process is finished.
Edit: In general, you should return quickly from event listeners, since you don't want to block all your GUI, only certain part, and in any case it makes your app feel sluggish (the window won't repaint in the meantime if it is moved or other stuff). Use Thread to launch a task running a verifyLogin() and disable your button in the meantime.
This works:
class ButtonListener implements ActionListener {
long previousEventEnd;
public void actionPerformed(ActionEvent e) {
if (e.getWhen() <= previousEventEnd ) {
log.tracef("discarding stale event, event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
return;
}
log.infof("LoginWindow.ButtonListener.actionPerformed(). Event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
LoginWindow.this.repaint(50);
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
verifyLogin();
previousEventEnd = System.currentTimeMillis();
}
});
}
}
I have to admit I'm astonished. I usually defend Java to its detractors. Here I have no defense at this point. This should not be necessary.

Can I call ActionPerformed method from an Event Handler class for JButton?

I have a JButton titled "select"
In the class that creates that JButton and other classes, I want to use an if condition with ActionPerformed method.
Something like(pseudo-code)
if(_selectListener.actionPerformed(ActionEvent)) { //i.e., if select Button is clicked,
//do something
}
Is this possible?
I want to call this method because I have to handle a situation in which a player should be able to choose something by clicking "select" button, or another "scroll" button, and I want to control it using something similar to a bunch of if statements like the one above.
If it is possible, what is the syntax for it? What is the argument ActionEvent?
Thank you!
The easiest and cleanest way is to add a dedicated, specific action listener to each button. That way, when the actionPerformed() method is called, you're sure that the associated button has been clicked, and don't need to test which button has been clicked:
selectButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// handle click on select button
}
});
scrollButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// handle click on scroll button
}
});
Another way is to use a common ActionListener, and use the getSource() method of ActionEvent to know which component triggered the event. Compare the result with each potential button to determine which is the one that has been clicked:
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == selectButton) {
// handle click on select button
}
else if (e.getSource() == scrollButton) {
// handle click on scroll button
}
}
What is the argument ActionEvent?
The answer is in the documentation. Read it.
no you cant call, if needs boolean expression/value, but this method returns void.

Remove the listener for defaultButton in java

i have a Jframe application with the defaultbutton set to btnClose_ (Close Button: this button closes the window).
I have 2 textfields that must also fire an event when user clicks the Enter key on the textfields. What happens is that when I press the Enter key while the cursor is on the textfield, the event on the Close button is fired causing the window to close.
Is it possible to remove the listener of the default button if the Enter key is pressed on the textfield? Here's my code for the textfield listener
/**
* Receives the two textfield instance
*/
private void addFilterListener(JTextField txf) {
txf.addKeyListener(new KeyAdapter() {
/**
* Invoked when a key has been pressed.
*/
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
ActionListener al = btnClose_.getActionListeners()[0];
btnClose_.removeActionListener(al);
btnFilter_.doClick();
e.consume();
btnClose_.addActionListener(al);
}
}
});
}
private JButton getBtnClose(){
if(btnClose == null){
btnClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
getWindow().dispose();
}
});
}
return btnClose;
}
}
Where to start?
The first thing that springs out at me is the bad variable names. txf? What's wrong with proper words? textField or field, say. Or much better, a name descriptive of its purpose, not what it is.
Secondly, the first comment is wrong (not uncommon) and the second comment is redundant (already specified in the KeyListener interface, you don't need to try and half-heartedly specify it again).
Next up, low level key listeners tend not to work so well on Swing components (JComboBox being the most notorious example - it typically is implemented with child components). In general you can use JComponent.registerKeyboardAction (the API docs says this is obsolete but not deprecated, and to use more verbose code). For text components, you often want to play with the document (typically through DocumentFilter). In this particular case, looks like you just want to add an ActionListener.
Now doClick. It's a bit of an evil method. For one thing it blocks the EDT. It is probably the easiest way to make it look as if a button is pressed. From a programming logic point of view, it's best to keep away from modifying Swing components, when you can keep everything in your abstracted code.
Removing and adding listeners from components is generally a bad idea. Your code should determine what to do with an event including whether to ignore it. Do that at an appropriate point when handling the event. Don't duplicate state unnecessarily.
A potential issue is that the code seems to assume that there is precisely one action listener. There could be others. The code is not robust under unexpected behaviour. Set your components up at initialisation time, and you shouldn't need to refer to them again.
As far as I understood your question, you want that buttonClick should not get fired if Enter is pressed .
This won't fire doClick() if enter is pressed
if (e.getKeyCode() != KeyEvent.VK_ENTER) {
btnFilter_.doClick();
}
In the ActionListener of the close button, assuming you can change its code, don't close if one of the text fields have the focus.
public void actionPerformed(ActionEvent e) {
if (field1.hasFocus() || field2.hasFocus())
return; // don't close if text field has focus
frame.dispose();
}
If you can not change the ActionListener of the close button, add a FocusListener to the text fields. If one of them gets the focus, remove the default button. If the text field lost the focus, reset the default button.
FocusAdapter listener = new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
frame.getRootPane().setDefaultButton(null);
}
#Override
public void focusLost(FocusEvent e) {
frame.getRootPane().setDefaultButton(close);
}
};
field1.addFocusListener(listener);
field2.addFocusListener(listener);
This should be better than depending on the listeners being called in the correct sequence - it is of no avail to remove the listener if it was already called...

Categories

Resources