When the actionPerformed method is invoked I would like it to display the desired icon on the first button, delay for 1 second and then display the icon on the second button. The icons always display simultaneously? Not sure how to correct this.
#Override public void actionPerformed(ActionEvent e)
{
btnTest1.setIcon(img2);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
btnTest2.setIcon(img2);
}
I'm assuming this is for JavaFX / Java Swing. I'm not too familiar with it, but I believe the threads aren't working is because there are concurrency rules related to using threads in swing apps. I've had similar issues and wrote a question about how to delay something using threads. I found sources for using a timer object. It might be better to implement the timer by doing:
import javax.swing.Timer;
I'm sure other more advanced users may give more detail. This is maybe another option. Oracle might help too:
https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
The buttons' icons are set with setIcon, but that doesn't mean they are painted immediately. Both icons will only be painted after you return from your actionPerformed method. What you need to do is set the icon for the first button, then use Timer (see zOrigin_'s answer) and after the timer elapses, set the second button's icon.
Related
I know many people have asked this question before but I couldn't find any answer that solved my problem. My code is like this:
public void mouseClicked(MouseEvent arg0) {
TEXT.setText("ON");
myfunction(); //runs for a very long time
}
The original text of JLabel is "OFF". Now I want to change the text to "ON" when the mouse is clicked but the text doesn't set until myfunction() is complete (which may take several minutes).
I have tried the invalidate function, making a separate function for setting the text but nothing is working.
Please help me with this problem!
The problem is that mouseClicked(...) is executed on the UI Thread. That is the Thread that is responsible for handling all sorts of user actions (like a mouse click) and also the drawing of components (like updating the text of the label on screen). If you execute your long running method call on the UI thread, it will be blocked and can't draw anything until execution is complete. You'll have to use multi threading to get around this problem.
The following might not be the most elegant solution, but if you are new to multi threading it will get the job done:
public void mouseClicked(MouseEvent arg0) {
TEXT.setText("ON");
(new Thread() {
public void run() {
myfunction();
}
}).start();
}
It will spawn a new Thread that handles your method, which will let the UI Thread continue doing its thing. consider deactivating the button that has just been clicked, so the user can't start the execution while it is already in progress (which usually is what you want..)
This question already has an answer here:
Swing, Java and multithreading, and coloring buttons
(1 answer)
Closed 8 years ago.
I´ve been trying to make a multiple choice question game using swing elements, I am just using a JTextArea to show the questions and four JButtons for the options.
I would like that every time the user answer a question the correct answer is shown by changing the background color of the JButton with the correct answer.
I am using a MVC design pattern so each time the user clicks the options JButtons the actionPerformed method calls a method in the interface that sets the background color of the JButton, sleeps the thread for one second and then it sets the background to the default color.
Everything seems to be right however when a run the program the graphic interface doesn't change the background color, although you can see the thread sleep.
If someone could help me solve this problem i would be very grateful, I include the code of all the described methods.
public void showCorrectAnswer (int index)
{
//JButtons array
options[index].setBackground(Color.green);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
options[index].setBackground(defaultColor);
}
ActionPerformed code of option buttons:
public void actionPerformed(ActionEvent e)
{
JButton source = (JButton) e.getSource();
int index =controller.getTheModel().getIndexWereTheRightAnswerIs();
controller.getTheMainView().showCorrectAnswer(index);
}
Using Thread.sleep from within the context of the Event Dispatching Thread is preventing the EDT from processing new paint requests (the changing of the background color).
Swing is also not thread safe, this means that you should only modify the UI from the context of the EDT.
Instead, you should consider using a javax.swing.Timer instead. This will allow you to schedule a callback at some time in the future and, when triggered, take action that will be executed from within the context of the EDT.
Take a look at Concurrency in Swing and How to Use Swing Timers for more details
What I would do is set up a non-repeating timer with a one second delay, set the background color of the button and start the timer. When the timer triggers, reset the background color of the button
Also, depending on the look and feel, setting the background color of the button may not have any (or only a small) effect, just so you know...
I am building a grpahic interface with different buttons,
and when user clicks a button I use :
actionPerformed(ActionEvent e) {
Object source = e.getSource();
if
else if
else if
...
}
The problem is that when the user clicks a button, actionPerformed(ActionEvent e) is called, and it enter in the if corresponding to e.getSource();.
And it executes all instructions in the corresponding if. But I want to make possible that the user clicks different buttons, so that a actionPerformed(ActionEvent e) { is called, even if the instructions of the previous actionPerformed(ActionEvent e) { is not finish yet.
I don't know if you understand, but thank you if you can help me !
You will need to use background threads if you want the GUI to be responsive while a long-running process is occurring. A SwingWorker works well for this in Swing GUI's. Please check out Concurrency in Swing for all the gory details on this.
Examples:
mvc-progress-bar-threading
why-does-swingworker-stop-unexpectedly
Google Search
You need to use SwingWorker . It does the work in background without freezing the UI.
Here is the question you can have a look for how to use them.
Or you can use official oracle documentation for SwingWorker.
I'm trying to code a simple game in Java. The basic structure is a single JFrame with different JPanels that I add/remove at different times. At startup, there is a JPanel that's a basic menu (start game, high scores, etc). Once the "Start" button is pressed it switches to a level selector panel with three buttons to select the difficult level of the game. Once any of the three buttons is pressed, it switches to another panel that will displays a three second countdown, then the actual game. All three buttons call the same method, just with a different difficulty value passed in.
I have all the separate pieces working fine, but I'm having troubles with the transition from the level selection panel to the countdown. If I don't use threads the screen freezes on button press and does not switch to the new panel. I've tried messing around with threads, but I don't know that much about them and have only had limited success (I've got it so it will successfully switch some of the time, but not consistently).
In terms of code, in the level selection panel I have something like this listening for button clicks:
private class ButtonClickedListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
gui.newLevel(1);
}
}
where in place of just gui.newLevel(1) I've messed around with starting new threads and calling the method from them.
The newLevel() method look like:
getContentPane().removeAll();
levelPanel = new LevelPanel(levelNum, this);
add(levelPanel);
validate();
levelPanel.start();
I use very similar code when switching from the start menu JPanel to the level selector panel (again, with an ActionListener on the buttons), which works just fine.
LevelPanel's start() method initializes values for the new JPanel and displays the countdown on screen (currently with the following code, although I messed with putting something like this in the newLevel() method instead) before displaying the actual game:
try {
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
} catch (Exception e) {
System.out.println(e);
}
//start game
I would really appreciate any help getting this code to work, and I'm pretty sure some sort of threading is the way to go but I'm not quite sure where/how. Any suggestions and/or code samples would be great!
Thanks in advance!
EDIT: I ended up rewriting the countdown using a timer instead of Thread.sleep(), which fixed part of the problem and the rest of it I eventually figured out and was entirely unrelated to GUI stuff, which is why I didn't think to check it in the first place.
never really never use Thread.sleep(1000); during EDT, this code caused freeze on GUI is un_resposible, untill a new event invoke EDT or mouse hover over can alive this container too
1) there are two ways how to dealy any event(s) in the Swing GUI, by implements
Swing Timer
delaying by using Thread.sleep(1000); in the SwingWorker
The layout and painting must be done in EDT. Use SwingUtilities.invokeAndWait to call the validate() and repaint()
You can start some code with a time delay using TimerTask:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
invokeLater(); // This starts after [delay] ms
// and - if given - will run every [period] ms.
}
}, delay, period);
You could solve your problem with this, though it won't be a pretty solution.
// edit: (see comments) you should synchronize accesses to the gui properly, else it will give you errors.
Apologies for the somewhat unclear question - couldn't think of a better way of putting it.
I use a JXTaskPane (from the Swing labs extension API) to display some information.
The user can "click" the title to expand the panel. The JXTaskPane is in a container JPanel, which is then added to a JFrame, my main application window.
I want my application window to resize to the size of the expanded task pane. To achieve this, I added a component listener to my container JPanel which would set size to the now expanded panel.
panel.addComponentListener(new ComponentListener()
{
public void componentResized(ComponentEvent e)
{
Dimension newSize = ((JXTaskPane)e.getSource()).getSize();
reSizeFrame(newSize);
}
}
private void reSizeFrame(Dimension newSize)
{
if ((newSize.height < maxSize.height) && (newSize.width < maxSize.width))
{
containerPanel.setSize(newSize);
appFrame.setSize(containerPanel.getSize());
appFrame.pack();
}
}
The problem is that the componentResized method is called as the task pane expands, as a result the resizeFrame method is called lots of times, and looks really awful on the screen.
How can I detect when the JXTaskpane has finished resizing? I thought of two approaches:
Put the resizeFrame() method in a SwingUtilities.invokeLate(..) call.
Put in a timer resizeFrame call, so any subsequent calls do not do anything until the timer fires. This should give enough time for the panel to resize.
What is the best way forward?
Also - This is my first serious Java GUI app after years of server side program. StackOverflow has been very helpful. So thanks!
I know you've already selected an answer, but overriding the paint method is definitely not correct, and while you may be able to hack something in place, it won't be ideal.
Looking at the source for JXTaskPane and specifically looking in setExpanded() (line 387), you can see it calls JXCollapsiblePane.setCollapsed(...) and then fires a property change event for expanded. A listener on that property won't be correct, because it'll fire before the animation is complete. So, if you go into JXCollapsiblePane and look at setCollapsed(...) (line 470) you'll see that if it's animated, it sets the paramaters and starts a timer. We want to know when the animation ends, so in that file, look at the animator (line 620, and specifically 652-667), which shows that when the animation ends, it fires a property change for ANIMATION_STATE_KEY with a value of "collapsed" or "expanded". This is the event you actually want. However, you don't have access to JXCollapsiblePane, so go back to JXTaskPane and search for ANIMATION_STATE_KEY, and you find line 208, which shows that JXTaskPane creates a listener on JXCollapsiblePane.ANIMATION_STATE_KEY and refires it as it's own event.
Since you do have access to JXTaskPane, you can listen for that event, so doing ...
taskPane.addPropertyChangeListener(JXCollapsiblePane.ANIMATION_STATE_KEY, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if(e.getNewValue().equals("expanded") {
...
}
else if(e.getNewValue().equals("collapsed") {
...
}
}
}
should get your event exactly when you want it.
The correct way to listen for events in Swing is through property listeners. Unfortunately, the only way to find out what the correct properties and values are is by digging through source code.
As a suggestion, have you tried overriding the paint method, first calling super and then putting your resize code at the end of that if (and only if) the size has changed significantly.
I'm not familiar with JXTaskPane, but my first reaction is that maybe you're handling the wrong event. You want the frame to resize when the user clicks on the header - so why not handle that event (perhaps using EventQueue.invokeLater() to resize the frame after the task pane has been resized)?
But if that doesn't work and you need to use the approach you've outlined above, using a javax.swing.Timer is probably best. Set it for 200 milliseconds or so and just restart() it every time componentResized() fires.