I am having some problems concerning starting javax.swing.Timer after a mouse click. I want to start the timer to perform some animation after the user clicks on a button but it is not working.
Here are the code snippets:
public class ShowMe extends JPanel{
private javax.swing.Timer timer;
public ShowMe(){
timer = new javax.swing.Timer(20, new MoveListener());
}
// getters and setters here
private class MoveListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// some code here to perform the animation
}
}
}
This is the class which contains a button so that when the user clicks on the button the timer starts to begin the animation
public class Test{
// button declarations go here and registering listeners also here
public void actionPerformed(ActionEvent e) {
if(e.getSource() == this.btnConnect){
ShowMe vis = new ShowMe();
vis.getTimer().start();
}
}
}
I want to start the timer to begin the animation but it is not working.
Need help how to make a timer start after button click.
Thanks.
You must call the start() method of the timer to start it.
public ShowMe(){
timer = new javax.swing.Timer(20, new MoveListener());
timer.start();
}
EDIT:
I have not seen that start() is being called in the Test class...
Next step would be to add some logging/printing to the MouseListener class to check if it is being called or not
private class MoveListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("MouseListener activated"); // TODO delete this line
// some code here to perform the animation
}
}
If it's running (I can't find any reason why not in the posted code), the problem is as Ash wrote above:
You created a new instance assigned to vis and started its Timer, but you have not added that instance to any visible container.
(maybe you added another instance of ShowMe earlier in the code...)
Some things to try:
Check that your panel is visible, e.g. make the background color red.
Check that the animation is being updated. For example, if you are animating by drawing different frames in a paint() method, then you will need to call repaint() in your timer, after updating the variables controlling animation. Alternatively, if animation is done by changing layout properties (e.g. to move a component around) then a call to validate() will be needed.
Using swing timer can get you started, but it's really the bare underpinnings. There are also libraries avaialbe that will allow you to go further with less effort:
animated transitions
Trident animation library
I know this question is a bit old, but I don't think you got an answer.
I believe the problem is that the ShowMe class and its Timer is being garbage collected, and hence fails to do what you think it should.
You are creating a new local ShowMe variable that goes out of scope as soon as the actionPerformed method completes. The Timer and its ActionListener are local to the ShowMe class instance, so when the actionPerformed method completes, they are also available for GC.
I'm not sure what the ShowMe class is doing. It appears to be a JPanel, so I assume it is something you want to display. It sounds like in your Test class (or real class), it might be better to have a ShowMe data member that you can just call start one when the button is clicked, instead of creating a new one every time.
Your usage of the Timer class seems to be correct. Maybe the problem lies in the MoveListener.
Did you remember to use the paintImmediately() method to repaint your animation?
If you use just repaint() you won't see a smooth animation, since repeated calls to repaint() are reduced to one call.
Related
Through some searching, I made about ActionListeners and GUI. I think I have come to a conclusion that all the changes you do that affect the GUI, in the ActionListener, actually happen when ActionListener comes to it's end
I am currently making a memory game. In the action listener of a card button I first flip the image (that changes it's icon) and the I check if a have a match and in the case I don't have one I close the card(change icon).
Since those two happen in the same actionListener as a result if I dont have a match I only see the first card I pick and he second seems to no react to flip.
Sorry if it's confusing how I explain it. I think that the problem is the actionListener but maybe I am wrong. What would you suggest?
c.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent ae){
selectedCard = c;
String imgName = "Images/"+c.getId()+".jpg";
URL imageURL = this.getClass().getResource(imgName);
if (imageURL != null) {
ImageIcon icons = new ImageIcon(imageURL);
c.setIcon(icons);
}
c.setEnabled(false);
c.setDisabledIcon(c.getIcon());
pl.singlePlay(c);
if(pl.getMatch()==false){
for (Cards cd : cards){
if(cd.getMatched()==false){
cd.setEnabled(true);
cd.setIcon(icon);
}
}
}
});
I have come to a conclusion that all the changes you do that affect the GUI, in the ActionListener, actually happen when ActionListener comes to it's end
Yes, all code invoked from any listener executes on the Event Dispatch Thread (EDT). This thread is responsible for updating the GUI and so the GUI can't be updated until the code has finished executing. Read the section from the Swing tutorial on Concurrency for more information.
Since those two happen in the same actionListener
If you want to schedule animation, that is show one Icon and then seconds later show another Icon, then you can use a Swing Timer. The Timer allows you to schedule another event. The tutorial link from above also has a section on How to Use Swing Timers to get you started.
I'm making a small game and I've already implemented a save function in which the game is saved (by writing information to a new XML file). The saving takes a couple of seconds and I want to do the following: while the program is saving the game, I want to change the look of the JPanel, and when it is done saving, I want to go back to another page(show another JPanel).
I have the following code:
confirm.addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e){
String fileNaam = saveGame.getText();
//This method changes the look of the panel
changePanel();
//This method saves the game
model.saveGame(fileNaam);
//This method takes the user back to a previous page
controller.viewTeamPage();
}
});
What happens is that the game is saved and the user is taken back to the teampage, but the panel is never changed. The changePanel() method does work, so that is not the problem but it seems like it is never executed. I was wondering if somebody knows how I can fix this.
EDIT:
private void changePanel () {
panel.removeAll();
panel.repaint();
panel.revalidate();
}
This is the method to change the look of the panel, for now I just remove everything on the panel to keep it simple.
Also, the saving is not done in a separate Thread, is that something I should look at?
EDIT 2: I fixed it by using a thread to save the game and return to the teampage after the saving is done. See the code below.
confirm.addActionListener(new ActionListener () {
#Override
public void actionPerformed(ActionEvent e){
final String fileNaam = saveGame.getText();
changePanel();
Thread t = new Thread (new Runnable () {
#Override
public void run() {
model.saveGame(fileNaam);
controller.viewTeamPage();
}
});
t.start();
}
});
If you are changing the same panel and not intializing a new panel then the problem i think is that you need to call the panel.revalidate or panel.repaint i think. I made a demo for a Procedural generation project and i had to do this to make my panel change.
Call your save game method from a new thread but don't "join" or "try" to wait for this thread to finish from inside the method actionPerformed();
Make the call to controller.viewTeamPage() after the save game thread is done saving the game. One simple way of doing that would be passing the "controller" object to the constructor of your custom thread so you can make that call after saving the game.
The step 1 is very important in this case because all the calls you are making in the method actionPerformed() are being made in the UI thread, preventing the entire UI from refreshing until the method returns. Even calling repaint() alone, in changePanel(), wont be enough because it just "schedules" a refresh on you panel that will only happen after actionPerformed() returns. If you put the most time consuming call in a separate thread however, the actionPerformed() returns quickly allowing the UI to be refreshed while the game saving thread is doing its job.
I have a JTable, where a user can select a single row. If that happens, i want to "highlight" another part of the page for a short time to indicate that this is the part of the page that changed after the user interaction.
So my question is: What's the best way to achieve this? At the moment i did it by setting the background color of that panel and starting a SwingWorker which sets the Color back after a short delay. It works as intended, but is it a good idea to use a SwingWorker like that? Are there any drawbacks to that approach? How would you solve this?
Thanks in advance.
I guess a Swing Timer would be a better option as it reuses a single thread for all scheduled events and executes the event code on the main event loop. So, inside your SelectionListener code you do:
// import javax.swing.Timer;
final Color backup = componentX.getBackground();
componentX.setBackground(Color.YELLOW);
final Timer t = new Timer(700, new ActionListener() {
public void actionPerformed(ActionEvent e) {
componentX.setBackground(backup);
}
});
t.setRepeats(false);
t.start();
I recommend a swing Timer (javax.swing.Timer). (do NOT use the Timer class in Java.util)
This is where you make the timer:
Timer t = new Timer(loopTime,actionListener)//loopTime is unimportant for your use of this
t.setInitialDelay(pause)//put the length of time between starting the timer and the color being reverted to normal
t.setRepeats(false);//by default, timer class runs on loop.
t.start();//runs the timer
It probably makes sense to hold on to a reference to the timer, and then just call t.start when you need it.
You need to implement an action listener to handle the timer events. I can edit this if you don't know how to do that, but as you are already doing stuff with Swing I figure it shouldn't be a problem.
I have a Blackjack game that I've made in Java and I want to signal the start of the game by clicking a button. All my action listeners work just fine and all that, but the problem lies in that I can't figure out how to start the game without it running completely within the actionPerformed method. Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI. Here's a code snippet....
go.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// START GAME SOMEHOW but must run outside of action listener
}
});
Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI.
This is a valid observation and shows that you have understand the fundamental rule when working with Swing.
Your game is most likely event driven (correct me if I'm wrong) so the action performed by the button should just set the program in a new state, waiting for further events. This is nothing that should be time consuming, and is typically done directly by the EDT.
Of course, if you want to do a fancy start-new-game animation, that needs to be performed in a separate thread, in which case you simply start the animation thread (I would recommend using a SwingWorker though) from within the actionPerformed method, and then return.
In code, I imagine it would look something like this:
go.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Remove the menu components
someJPanel.removeAll();
// Show the game table
someJPanel.add(new GamePanel());
someJPanel.revalidate();
someJPanel.repaint();
// done. Wait for further user actions.
}
});
You game should probably start in its own thread and manage that itself (hard to say), but to get you going you could start your game in a new "external" thread, something like this in your actionPerformed:
public void actionPerformed(ActionEvent e) {
Thread thread = new Thread("Game thread") {
public void run() {
startGame(); //or however you start your game
}
};
thread.start();
}
I believe that you want to extend javax.swing.SwingWorker.
The non-ui start-up functionality would run in doInBackground and the done method would be called when it finishes to update the ui.
There's even an example in the javadoc Class Description to update a progressbar with the status of what's happening in start-up.
Could someone teach me how to use a swing timer with the following purpose:
I need to have a polygon that begins being animated(simple animation such as rotating) when I click the mouse; and stops animating when I click again.
I do not have problems understanding the way the MouseListener works, but with the actual animation. I tried simulating the animation with a while block inside the paint() method where I would draw, erase and redraw the polygon(to simulate a rotation for example), but inside the while, the applet would not listen to the clicks. It would listen only after the while. I would need the swing timer to break the while when I click the mouse.
import javax.swing.Timer;
Add an attribute;
Timer timer;
boolean b; // for starting and stoping animation
Add the following code to frame's constructor.
timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
// change polygon data
// ...
repaint();
}
});
Override paint(Graphics g) and draw polygon from the data that was modified by actionPerformed(e).
Finally, a button that start/stop animation has the following code in its event handler.
if (b) {
timer.start();
} else {
timer.stop();
}
b = !b;
This example controls a javax.swing.Timer using a button, while this related example responds to a mouse click. The latter example reverses direction on each click, but start/stop is a straightforward alteration.
The applet won't listen to clicks because the main thread (the Event Dispatch Thread, EDT) is within the while-loop and isn't listening to your clicks.
You need another thread.
(try using SwingWorker http://download.oracle.com/javase/tutorial/uiswing/concurrency/worker.html)
So, the SwingWorker will do the while-loop in the background, publishing results to make your polygon move.
And the EDT can then focus on any events (like clicks). You can then just use the click-event to kill the SwingWorker if you want to stop it.
Good luck