java - Swing animation flickers and makes GUI slow to respond -


i'm trying write simple program: bouncing ball appears , starts bouncing after press "start" button on screen. program should closed pressing "x".

for reason, runs slowly. ball blinking, , have wait long time after press "x" program close.

here code:

import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; import javax.swing.*;  public class bounce {     public static void main(string[] args)     {         jframe frame = new bounceframe();         frame.setdefaultcloseoperation(jframe.exit_on_close);         frame.show();     } }  class bounceframe extends jframe {     public bounceframe()     {         setsize(width, height);         settitle("bounce");         container contentpane = getcontentpane();         canvas = new ballcanvas();         contentpane.add(canvas, borderlayout.center);         jpanel buttonpanel = new jpanel();         addbutton(buttonpanel, "start", new actionlistener()         {             public void actionperformed(actionevent evt)             {                 addball();             }         });         contentpane.add(buttonpanel, borderlayout.south);     }     public void addbutton(container c, string title, actionlistener listener)     {         jbutton button = new jbutton(title);         c.add(button);         button.addactionlistener(listener);     }     public void addball()     {         try         {             ball b = new ball(canvas);             canvas.add(b);             (int = 1; <= 10000; i++)             {                 b.move();                 thread.sleep(10);             }         }         catch (interruptedexception exception)         {         }     }     private ballcanvas canvas;     public static final int width = 300;     public static final int height = 200; }  class ballcanvas extends jpanel {     public void add(ball b)     {         balls.add(b);     }     public void paintcomponent(graphics g)     {         super.paintcomponent(g);         graphics2d g2 = (graphics2d)g;         (int = 0; < balls.size(); i++)         {             ball b = (ball)balls.get(i);             b.draw(g2);         }     }     private arraylist balls = new arraylist(); }  class ball {     public ball(component c) { canvas = c; }     public void draw(graphics2d g2)     {         g2.fill(new ellipse2d.double(x, y, xsize, ysize));     }     public void move()     {         x += dx;         y += dy;         if (x < 0)         {             x = 0;             dx = -dx;         }         if (x + xsize >= canvas.getwidth())         {             x = canvas.getwidth() - xsize;             dx = -dx;         }         if (y < 0)         {             y = 0;             dy = -dy;         }         if (y + ysize >= canvas.getheight())         {             y = canvas.getheight() - ysize;             dy = -dy;         }         canvas.paint(canvas.getgraphics());     }     private component canvas;     private static final int xsize = 15;     private static final int ysize = 15;     private int x = 0;     private int y = 0;     private int dx = 2;     private int dy = 2; } 

the slowness comes 2 related problems, 1 simple , 1 more complex.

problem #1: paint vs. repaint

from jcomponent.paint docs:

invoked swing draw components. applications should not invoke paint directly, should instead use repaint method schedule component redrawing.

so canvas.paint() line @ end of ball.move must go.

you want call component.repaint instead... replacing paint repaint reveal second problem, prevents ball appearing.

problem #2: animating inside actionlistener

the ideal actionlistener.actionperformed method changes program's state , returns possible, using lazy methods repaint let swing schedule actual work whenever it's convenient.

in contrast, program inside actionperformed method, including all animation.

solution: game loop

a more typical structure start javax.swing.timer when gui starts, , let run "forever", updating simulation's state every tick of clock.

public bounceframe() {     // original code here.     // add:     new javax.swing.timer(         10,  // timeout `addball`.         new actionlistener()         {             public void actionperformed(final actionevent ae)             {                 canvas.moveballs();  // see below method.             }         }     ).start(); } 

in case, important (and missing) state "have started yet?" bit, can stored boolean in ballcanvas. that's class should animating, since owns canvas , balls.

ballcanvas gains 1 field, isrunning:

private boolean isrunning = false;  // new field  // added generic type `balls` --- see below. private java.util.list<ball> balls = new arraylist<ball>(); 

...and setter method:

public void setrunning(boolean state) {     this.isrunning = state; } 

finally, ballcanvas.moveballs new "update things" method called timer:

public void moveballs() {     if (! this.isrunning)     {         return;     }     (final ball b : balls)     {         // remember, `move` no longer calls `paint`...          // updates numbers.         b.move();     }     // visible state has changed, ask swing     // schedule repainting panel.     repaint(); } 

(note how simpler iterating on balls list list has proper generic type. loop in paintcomponent made straightforward.)

now bounceframe.addball method easy:

public void addball() {     ball b = new ball(canvas);     canvas.add(b);     this.canvas.setrunning(true); } 

with setup, each press of space bar adds ball simulation. able on 100 balls bouncing around on 2006 desktop without hint of flicker. also, exit application using 'x' button or alt-f4, neither of responded in original version.

if find needing more performance (or if want better understanding of how swing painting works), see "painting in awt , swing: painting code key app performance" amy fowler.


Comments

Popular posts from this blog

python Tkinter Capturing keyboard events save as one single string -

android - InAppBilling registering BroadcastReceiver in AndroidManifest -

javascript - VueJS2 and the Window Object - how to use? -