问题
I need to make a method that returns only when a JButton
is pressed. I have a custom JButton
class
public class MyButton extends JButton {
public void waitForPress() {
//returns only when user presses this button
}
}
and I want to implement waitForPress
. Basically, the method should only return when the user presses the button with their mouse. I have achieved similar behavior for JTextField
(to return only when user presses Space
):
public void waitForTriggerKey() {
final CountDownLatch latch = new CountDownLatch(1);
KeyEventDispatcher dispatcher = new KeyEventDispatcher() {
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == KeyEvent.VK_SPACE) {
System.out.println("presed!");
latch.countDown();
}
return false;
}
};
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(dispatcher);
try {
//current thread waits here until countDown() is called (see a few lines above)
latch.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(dispatcher);
}
but I would like to do the same thing with JButton
.
In advance: Please, if you wish to comment saying that this is not a good idea and that one should simply wait for actionPerformed
event on a JButton
and then do some action, please realize I already know that and have a good reason for doing what I'm asking here. Please try to only help with what I've asked. Thanks!!
In advance: Please, also realize that implementing actionPerformed also will not directly solve the problem. Because the code will progress even without the button being pressed. I need the program to stop, and only return when the button has been pressed. Here is a terrible solution if I were to use actionPerformed:
public class MyButton extends JButton implements ActionPerformed {
private boolean keepGoing = true;
public MyButton(String s) {
super(s);
addActionListener(this);
}
public void waitForPress() {
while(keepGoing);
return;
}
public void actionPerformed(ActionEvent e) {
keepGoing = false;
}
}
回答1:
As you asked for an implementation with a mutex, here's what it would be like.
I'm using an ActionListener
though, but there's no busy wait in it. If that isn't what you desire, you atleast saw what Burkhard meant ;)
public class MyButton extends JButton implements ActionListener
{
private Semaphore sem = new Semaphore(1);
public MyButton(String text) throws InterruptedException
{
super(text);
addActionListener(this);
sem.acquire();
}
@Override
public void actionPerformed(ActionEvent e) {
sem.release();
}
public void waitForPress() throws InterruptedException {
sem.acquire();
//do your stuff
sem.acquire();
//or just
//waitForPress()
//if you dont want it to end.
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame();
MyButton m = new MyButton("test");
frame.add(m);
frame.pack();
frame.setVisible(true);
m.waitForPress();
//another time, if you only want it to block twice
m.waitForPress();
}
}
But I don't think this is a clean approach, but it doesn't consume CPU-time like a while(isStatementTrue)-implementation.
An important thing here is: you're blocking the main thread with m.waitForPress()
but as you wrote you're quite experienced and you know how to handle that.
回答2:
For what it's worth, here is how you can do it with wait()
and notify()
but yet I feel that there is a deeper problem here. I would not consider this as a satisfying solution:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class TestBlockingButton {
boolean clicked = false;
private Object toNotify;
private void initUI() {
JFrame frame = new JFrame(TestBlockingButton.class.getSimpleName());
JButton button = new JButton("Click me");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
clicked = true;
if (toNotify != null) {
synchronized (TestBlockingButton.this) {
toNotify.notify();
}
}
}
});
frame.add(button);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void waitForProcess() {
toNotify = this;
while (!clicked) {
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
System.out.println("continuing work");
}
public static void main(String[] args) {
final TestBlockingButton test = new TestBlockingButton();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
test.initUI();
}
});
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("I was doing something and now I will wait for button click");
test.waitForProcess();
System.out.println("User has now cliked the button and I can continue my work");
}
});
}
}
来源:https://stackoverflow.com/questions/14187235/make-method-that-returns-when-jbutton-is-pressed