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 userepaint
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
Post a Comment