I have extended JTextField. There are multiple ActionListeners attached to my class, but I need one in particular to always fire first. Is there a way to ensure that a particular ActionEvent always first first?
Note that I do have a reference to my ActionListener. I am assuming I need to override a method, but I am not sure which one that is.
Is there a way to ensure that a particular ActionEvent always first first?
The Java spec does not guarantee the order in which events are fired.
However, I believe the default implementation is that the last ActionListener added to a component is first first.
Edit:
I use the mouse listener
Why are you using a MouseListener? What happens when the user tabs to/from the field? Don't assume mouse usage. A FocusListener will handle either keyboard or mouse activity in this case.
The problem comes into play when the user presses enter on the text field.
What happens if the user doesn't press enter and they just tab to the next field? That's what I would do.
This needs to happen before other action listners do their thing.
What other ActionListeners? A tgext field would only have a single listener.
Anyway do maybe answer your question you can effectively change the order of execution by using SwingUtilities.invokeLater(...) to place your code on the end of the Event Dispatch Thread so it executes after all other events:
public void actionPerformed(ActionEvent e)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// add your code here
}
});
}
Again, any solution that depends on the order of events is not a good solution. The order of event could be different on different platforms.
Try to override fireActionPerformed from JTextField. Add custom ActionListener in new class and call him before call super.fireActionPerformed()
Ps: sorry for bad english.
Edit:
public class CustomTextField extends JTextField {
private List<ActionListener> listeners;
public synchronized void addPriorityActionListener(ActionListener l) {
if(l == null) {
return;
}
if(listeners == null) {
listeners = new ArrayList<>();
}
listeners.add(l);
}
public synchronized void removePriorityActionListener(ActionListener l) {
if(l == null || listeners == null) {
return;
}
listeners.remove(l);
}
protected void firePriorityActionPerformed() {
if(listeners == null) {
return;
}
ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, getText());
for(ActionListener listener: listeners) {
listener.actionPerformed(event);
}
}
#Override
protected void fireActionPerformed() {
firePriorityActionPerformed();
super.fireActionPerformed();
}
}
Related
I have a GUI that has one output text box and one input text box. I would like, if reasonably attainable, to only use the one input box for all user inputs. I have a situation where I ask the user a question, they input their answer, and then inside the method, I'd like to ask a sub-question using that same input text box. However, in the method I created to handle this interaction, so far I am unable to change the text box or press any keys. How should I fix my code?
EDIT: Taking into account comments stating I should not use Thread.sleep(), I attempted to use a Timer instead. However, now instead of waiting, the method immediately fails and returns "N". Forgive me for being relatively new to GUIs and Swing Timers. What do I need to do to have the program wait while still allowing me to type and press enter?
public static String pauseUntilKey(JTextField tf)
{
pause = true;
tf.removeKeyListener(tf.getKeyListeners()[0]);
KeyAdapter pauseForInput = new KeyAdapter() { //Get rid of the old keyAdapter and put in the new one just for this function
#Override
public void keyPressed(KeyEvent arg0) {
if(arg0.getKeyCode() == KeyEvent.VK_ENTER) //When the enter key is pressed, this should trigger
{
pause = false; //Set local variable pause to be false to let us know we need to stop the while loop
answer = tf.getText();
tf.setText("");
}
}
};
timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(pause == false)
timer.stop();
}
});
timer.start();
KeyAdapter enterMain = new KeyAdapter() { //Put the old key adapter back in
#Override
public void keyPressed(KeyEvent arg0) {
if(arg0.getKeyCode() == KeyEvent.VK_ENTER)
{
roster = textInput(tf.getText(), roster, names, true, tf); //Analyze the line using textInput function, update the roster with any changes
tf.setText("");
}
}
};
tf.addKeyListener(enterMain);
if(pause == false)
return answer; //If we left the while loop the way I wanted, then return whatever the user wrote before pressing enter.
return "N"; //Otherwise, just return N for No.
}
The variable pause need to be declared volatile, otherwise the worker thread will never be notified by the changes done in the Event Dispatcher Thread (that will call the keyPressed method).
Also, if you have not already done so, you need to use a SwingWorker instance that will call the pauseUntilKey, otherwise you lock up the entire swing subsystem since it is single-threaded.
I have a Question on performing other buttons action with single button click. Some example code for three buttons:
JButton a = new JButton("a");
a.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Action of a is Here
}
});
JButton b = new JButton("b");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Action of b is Here
}
});
Those should come together, like:
JButton c = new JButton("c");
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Action of c is Here
// Action of a
// Action of b
}
});
In the above example i have three buttons a,b,c with its own action; but as you can see, C also has to run the actions of A and B. What are good ways to address this?
The other answers are all correct, but there is one important aspect missing here: be careful about dong "too many things" on the AWT event dispatcher thread.
Meaning: when a button is clicked, an event gets created, and the UI framework uses that special thread to trigger the registered listeners. If one of the listeners now decides to do a intensive computation ... the UI event threads stays busy doing "that". And while doing "that thing"; this thread isn't available to dispatch any other UI event.
So, this is "not only" about creating methodA(), methodB(), methodC() and invoking them in your third action listener. It is also about understanding if combining multiple calls becomes subject to "I should better run those things in a separate thread; to not block the event dispatcher thread".
In that sense: the other answers tell you where to go from here; but be really careful about the "amount of activity" that your "joined actions" button is about to create!
1) Methods
Use methods for each action and call those in the ActionListener.actionPerformed
public void methodA(){}
public void methodB(){
methodA();
}
2) Action instance
You could create your own classes of ActionListener to perform the actions
First action :
class ActionA implements ActionListener{
public void actionPerformed(ActionEvent e) {
...
}
}
An improved action
class ActionB extends ActionA{
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e); //Will call the first action
...
}
}
This is limited since you can't have multiple extends but is also a nice solution
3) Click
Last but I don't like it, use AbstractButton.doClick to dynamicly click on other buttons.
4) Add multiple action
Just notice that the methods is not a setActionListener but a addActionListener meaning that it will accept multiple ActionListener.
So define create two instances
ActionListener listenerA = new ActionLisener ..
ActionListener listenerB = new ActionLisener ..
buttonA.addActionListener(listenerA);
buttonB.addActionListener(listenerB);
buttonC.addActionListener(listenerA);
buttonC.addActionListener(listenerB);
With a small test, I notice that the actions are execute in the order B -> A (might not be a generality).
As said in comment, this should be us knowing the risk, this will . If an action failed because of an exception, should the next one be executed ? By default it won't because the process will not hide exceptions.
I would restrict this solution to GUI management like reseting fields, disabling, ... that could be use in different buttons.
Whatever you want to do on Button click a, you can put in a method and call it from wherever you want.
public void methodForA(){
// do here what you want
}
You can call this now in the methods you want it to call from. In your case from button click A and button click C
JButton a = new JButton("a");
a.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
methodForA();
}
});
// and also in your c-Button
JButton c = new JButton("c");
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Action of c is Here
methodForA();
}
});
Create 3 methods for each button indepently from the actionListeners action Perform method and call them from the actionPerfomed methods:
private void btnAClicked(){};
private void btnBClicked(){};
private void btnCClicked(){};
JButton c = new JButton("c");
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
btnCClicked();
btnAClicked();
btnBClicked();
}
});
I need to keep focus on JTextField. Application uses Swing library. I need set focus on that field from time to time in order to avoid user mistakes that would change focus to other comonents. I suppose I need to use SwingWorker. Set focus is an operation on Swing
component so it should be invoked in EDT. My question is how to write SwingWorker to do that?
I know that method done() pass tasks to be invoked in EDT but I need this task to be invoked every let's sey 2 seconds. Method done() is called one time.
So maybe sth like this will be ok?
public class myWorker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//here set focus on JTextField
return null;
}
});
}}
Edit:
I noticed that method process() that is a part of SwingWorker may be appropriate beacuse it is invoked in EDT. I'm not sure but this method is probably invoked always when I call publish() metod. So could you tell me if this code is valid to do this task?
private class KeepFocusWorker extends SwingWorker<Void, Void>
{
#Override
protected Void doInBackground() throws Exception
{
while(true)
{
publish();
}
}
#Override
protected void process(List<Void> chunks)
{
codeBar.requestFocusInWindow();
}
}
Use javax.swing.Timer instead of SwingWorker. In this case actionPerformed will be executed in EDT. Also to set focus in a component, you need to call requestFocus. As the name suggests, it is a request only and not guaranteed. So you may change you approach.
Timer timer = new Timer(2000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
codeBar.requestFocus();
}
});
timer.setRepeats(true);
timer.start();
Surely it's better to limit the user's ability to take focus away from the textfield in the first place? Personally I don't see why it's an issue but I suppose it's better to keep focus in the one component rather than letting the user shift focus only for it to be shifted back every few seconds.
Therefore you could add a FocusListener to the component, override the focusLost method and basically requestFocus() again.
codeBar.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
codeBar.requestFocus();
}
});
NB I've not actually tried this myself but can't see why it wouldn't work.
Alternatively you can use an InputVerifier which always returns false to prevent focus being taken away.
How should i implement an MVC controller with multiple JButtons on the view?
for example: i have a start button, stop button and many others as well.
I tried to do this for the start button and it works fine but then how do i implement it for a stop button trigger?
Controller Code:
public MVCAuctionController(Auction a, MVCAuctionView v) {
auction = a;
view = v;
view.addProcessBidsListener(new ProcessBidsController());
view.addStopProcessListener(new StopBidsController());
}
class ProcessBidsController implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
view.disableProcessButton();
Thread thread = new Thread (auction);
thread.start();
}
}
addProcessBidsListener - is associated with the START/Process button,When i click on the button - the thread starts running and fills the JTextArea with data.
Now my Stop button should stop the thread. For this if i do something like this it doesnt actually stop the Thread:
class ProcessStartController implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == view.start){
view.disableStartButton();
new Thread (rest).start();
//thread.start();
System.out.println("inside action performed of start button");
view.kitchen.append("Orders to kitchen");
}
else if (e.getSource() == view.stop)
{
new Thread (rest).interrupt();
}
}
}
Use an Action for each button, they are self contained controllers
See How to use Actions for more details
You can then set up some kind of relationship between the Actions and the main controller should you need it, via some kind listener for example
How can I know the button that was pressed from within a mouseDragged event?
I'm having an issue in mouseDragged() because the received MouseEvent returns 0 for getButton(). I have no problem with the mouse location, or even detecting mouse clicks. The mouseClicked() event returns the appropriate button for getButton().
Any suggestions on how I can do this? I assume I could do a work-around using mouseClicked, or mousePressed, but I would prefer to keep this all within mouseDragged.
Thanks for your time and answers.
As pointed out in comments and other answers, SwingUtilities provides three methods for cases like this, which should work for all MouseEvents:
SwingUtilities.isLeftMouseButton(aMouseEvent);
SwingUtilities.isRightMouseButton(aMouseEvent);
SwingUtilities.isMiddleMouseButton(aMouseEvent);
As for what the problem with your approach is, the javadoc of getButton() says:
Returns which, if any, of the mouse buttons has changed state.
Since the state of the button doesn't change while it is being held down, getButton() will usually return NO_BUTTON in mouseDragged. To check the state of buttons and modifiers like Ctrl, Alt, etc. in mouseDragged, you can use getModifiersEx(). As an example, the below code checks that BUTTON1 is down but BUTTON2 is not:
int b1 = MouseEvent.BUTTON1_DOWN_MASK;
int b2 = MouseEvent.BUTTON2_DOWN_MASK;
if ((e.getModifiersEx() & (b1 | b2)) == b1) {
// ...
}
Jacob's right that getButton() doesn't get you the button by design. However, I found a cleaner solution than bit operations on getModifiersEx(), that you can also use within mouseDragged:
if (SwingUtilities.isLeftMouseButton(theMouseEvent)) {
//do something
}
Similar methods exist for the middle button and the right button.
int currentMouseButton = -1;
#Override
public void mousePressed(MouseEvent e) {
currentMouseButton = e.getButton();
}
#Override
public void mouseReleased(MouseEvent e) {
currentMouseButton = -1;
}
#Override
public void mouseDragged(MouseEvent e) {
if (currentMouseButton == 3) {
System.out.println("right button");
}
}
This could be possibly a problem of your java sandbox.
The following code works well all the time (almost, as you can see).
#Override
public void mouseDragged(MouseEvent e) {
e.getButton();
}
Please try your code on a different machine.