I am creating a two player Simon game, one player(client) clicks the colored squares, and the client will send the clicking sequence (as an ArrayList) to the server, and the server sends it to the other player.
Right now, the ArrayList arrives at the other client with no problem, but when I loop through the ArrayList, and inside the loop, I want the colored squares to flash. Only the last color will flash.
public void flashArray(){
for(int i=0;i<blist.size();i++){
int toFlash = blist.get(i);
setFlashed(toFlash);//for example, if toFlash==1, after repaint, the green square will be lighter, and after the timer, it will be back to normal
repaint();
Timer timer = new Timer(200, timerAction);
timer.setRepeats(false);
timer.start();
}
}
the timerAction code
private ActionListener timerAction = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
setFlashed(F_DEFAULT);
repaint();
}
};
the flashing works when I manually click the squares, they will flash. but when I put the code into the flashArray (loop) it will not work, it will only flash the last color.
Thank you #MadProgrammer.
Using Thread.sleep(sleeptime) is worked perfectly for me.
Here is the method:
//a method to make simon flash based on the arraylist
public void flashArry(){
for(int i=0;i<blist.size();i++){
int toFlash = blist.get(i);
setFlashed(toFlash);
repaint();
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(Simon.class.getName()).log(Level.SEVERE, null, ex);
}
setFlashed(F_DEFAULT);
repaint();
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(Simon.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
to explain the code: loop through the integer arraylist(the integers represent the colors to flesh), set the color-> repaint (make it brighter)-> sleep->set back to default color-> repaint ->sleep ->loop to next color to flash...
Related
I'm having trouble with this code I am using to create a roulette wheel. The goal is to spin the wheel when I click the "SPIN!" button. I have done this by creating a for loop that should change the status of the wheel from true to false, which changes the orientation. This, when done fast enough, should create the illusion of movement.
THE PROBLEM I AM HAVING: is that my wheel is only repainting after the whole for loop is done, despite my placement of the repaint(). So, it only spins one tick.
Here is some sample code of my ActionListener:
public class spinListener implements ActionListener
{
RouletteWheel wheel;
int countEnd = (int)(Math.random()+25*2);
public spinListener(RouletteWheel w)
{
wheel = w;
}
public void actionPerformed(ActionEvent e)
{
for (int i = 0; i <countEnd; i++)
{
try
{
Thread.sleep(100);
if (wheel.getStatus() == true)
{
wheel.setStatus(false);
repaint();
}
if (wheel.getStatus() == false)
{
wheel.setStatus(true);
repaint();
}
}
catch (InterruptedException ex)
{
Logger.getLogger(WheelBuilder.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
UPDATE: I figured out the problem. Here are the changes I made for anyone having a similar problem.
public class spinListener implements ActionListener
{
Timer tm = new Timer(100, this);
int count = 0;
public void actionPerformed(ActionEvent e)
{
tm.start();
changeWheel();
}
public void changeWheel()
{
int countEnd = (int)(Math.random()+20*2);
if (count < countEnd)
{
wheel.setStatus(!wheel.getStatus());
repaint();
count++;
}
}
}
Swing is a single threaded environment, anything that blocks the Event Dispatching Thread, will prevent it from been able to process new events, including, paint events.
The use of Thread.sleep within the actionPerformed method is blocking the EDT, preventing it from processing new events, including paint events, until the actionPerformed method is exited.
You should use a javax.swing.Timer instead.
Take a look at Concurrency in Swing and How to Use Swing Timers for more details
I am working on a swing project. There is a map, I have raster images of a given data for different times. Normally I change the time via a JSlider and it requests server for raster image. Then I add response image to map. There is a Play JButton, when pressed it will add those images one by one to raster layer of the map. It will be seen as an animation. In that JButton's actionPerfomed method I change the JSlider's value in a for loop.
My problem is when I press Play JButton, I can't see the data is played but I know code block works because I record each image(from server). I found out that it is becuse of JButton does not release Focus until its actionPerformed method ends. Because JButton looked like it was pressed until the end. So I only see the last image in the map.
First, I tried JButton.setFocusable(false) etc. but to no good.
Second, I tried using SwingWorker. I added it like this:
class PlayMvgmLayerWorker extends SwingWorker<Void, Void> {
public PlayMvgmLayerWorker(String title) {
super(title);
}
#Override
protected void done(Void aVoid) {
}
#Override
protected Void doInBackground() {
try{
BufferedImage[] image = new BufferedImage[24];
for(int i=0; i<24; i++) {
final int value = i - 48 + 24;
timeSlider.setValue( value );
Thread.sleep(10000l);
}
} catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
}
private JButton animation = new JButton("");
animation.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
new PlayMvgmLayerWorker("title").execute();
}
});
private JSlider timeSlider = new JSlider();
timeSlider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
// time consuming processes ( server call, add image to map, etc)
}
});
I tried to simplify it.
It is much better than before, but I still can not see the data played properly. Sometimes data is played after JSlider ticks. Could it be because my time consuming process is in the second components(JSlider) stateChanged event? Should I use a second SwingWorker in JSlider's event too? Any suggestions about what can I do?
Moreover, what would be the best way to disable all components before playing data, and enable them after playing data?
Thank you very much in advance
If you have two activities Activity A and Activity B which have to be run simultaneously, you need to create a thread for the second activity - the first activity will already be run in its own thread (the main program).
The scheme is as follows:
Program A:
create new Thread: Activity B
run allother tasks for Activity A
In more specific terms the following program will run your simulation and update the tick of the slider:
public P() {
animation.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doInBackgroundImp();
}
});
setSize(500, 500);
setLayout(new FlowLayout());
add(animation);
add(timeSlider);
setVisible(true);
}
protected void doInBackgroundImp() {
Thread th=new Thread() {
public void run() {
try{
for(int i=0; i<24; i++) {
final int value = i - 48 + 24;
timeSlider.setValue( i );
System.out.println(timeSlider.getValue()+" "+value);
Thread.sleep(1000);
}
} catch(Exception ex) {
ex.printStackTrace();
}
}
};
th.start();
}
private JButton animation = new JButton("");
private JSlider timeSlider = new JSlider();
}
public void playPanel() throws IOException{
for(int i = 0; i<listData.size(); i++){
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ascii.setText(listData.get(i));
}
}
What I'm trying to do is play through the listData ArrayList type, which was copied from the ascii JTextArea. Its supposed to be an animation, so when they hit play the function displays the first slide, waits a second, then the next slide, etc.
When I run this the only thing that happens is a pause with nothing on the screen changing until it displays only the final slide. I'm not sure what's wrong with it
You should never call Thread.sleep(...) on the Swing event thread unless your goal is to put the entire application to sleep, rendering it useless. Instead, get rid of the for loop, and "loop" using a Swing Timer. Something like this should work, or be close to a functioning solution (caveat: code has not been compiled nor tested):
int delay = 1000;
new Timer(delay, new ActionListener() {
private int i = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (i < listData.size()) {
ascii.setText(listData.get(i));
} else {
((Timer) e.getSource()).stop();
}
i++;
}
}).start();
Is there a way to easily convert thread.sleep to javax.swing.timer?
The reason why I would need to do this, is to stop the user-interface from freezing when you press a button, so that you can implement a pause button.
Code Example:
btnStartTiming.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent arg0) {
try{
inputA = Double.parseDouble(txtEnterHowLong.getText()); //Changes double to string and receives input from user
}catch(NumberFormatException ex){
}
while (counter <= inputA){
txtCounter.setText(counter + "");
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
System.out.println(counter);
counter++;
}
}
});
Some tips:
Take a look to How to use Swing
Timers
trail and come back with concrete problems. Describe what are you trying to accomplish and your work so far, show your attempts to solve the problem and make an answerable question.
Don't use MouseListener
to listen when a button is pressed. Use ActionListener
instead. Take a look to How to Use Buttons, Check Boxes, and Radio
Buttons trail.
Put the java.swing.Timer in your constructor. You can use the button to .start() the timer.
Also instead of the while, you can add an if statement in the timer code check when to .stop()
Something like this
int delay = 1000;
Timer timer = new Timer(delay, null);
public Constructor(){
timer = new Timer(delay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (counter >= inputA) {
timer.stop();
} else {
// do something
}
}
});
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
}
I want to move a image across the screen by 16 to the right when the arrow key is pressed. I want to move it with a speed(1px/10ms) until reaches the point. The image is created inside a class that is child of JPanel.
I wrote the next code but the image changes the position instatly instead making a movement:
public class Test extends JFrame implements KeyListener {
private int x=0;
private int y=0;
BufferedImage img;
...
...
public void paint(Graphics g){
g.drawImage(img,x,y,null);
}
// Move to a point 16 pixels to right
public void moveRight(){
for(int i=0;i<16;i++){
x++;
repaint();
try {
Thread.sleep(10); // Sleep 10 milliseconds until next position change
}catch (InterruptedException e) {}
}
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
moveRight();
}
}
}
The problem is your sleep inside the EDT (Event-Dispatching-Thread). repaint() triggers an event that will be dispatched by the EDT and will in turn perform the actual repainting of your component. Since you are blocking the EDT, the repaint does not perform directly (but after the end of all your code then a single repaint event occurs (because repaint events are grouped whenever possible). You will probably need to use SwingWorker to fix this issue.
What if you call moveRight() in another thread?
try this:
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_RIGHT){
new Thread(new Runnable(){
public void run(){
moveRight();
}
}).start();
}
}
I've not tested and I even don't know if this is a good aproach