The Java Tutorials' Weblog
Custom Painting: Is This Fast Enough?
The following program is from the latest version of the "Custom Painting in Swing" lesson, which will be released sometime near the end of the month. For those who might think that Swing GUI rendering is slow, this lesson should show that does not have to be the case! The following program draws an object to the screen; it follows your mouse as you drag it around. When you try this out, maximize the application (we want to occupy as much of the screen as possible), drag the mouse around, and note its performance. How is the rendering speed? Is it quick? Does it "feel" similar to running a GUI written in a native language? Would this performance be fast enough for your own applications? If you look closely at the source you will notice that we are using the multi-arg version of repaint to intelligently repair only the bits that have changed. Also, can you tell why we are invoking repaint two times in the same method? If not, uncomment the first invocation and run it again. Do you understand what is happening here? Useful comments to post here would be 1) your perception of the GUI's performance and 2) details about the system you are running it on.
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;
public class SwingDemo {
public static void main(String[] args) {
JFrame f = new JFrame("Swing Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
}
class MyPanel extends JPanel {
RedSquare redSquare = new RedSquare();
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
moveSquare(e.getX(),e.getY());
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
}
private void moveSquare(int x, int y){
final int CURR_X = redSquare.getX();
final int CURR_Y = redSquare.getY();
final int CURR_W = redSquare.getWidth();
final int CURR_H = redSquare.getHeight();
final int OFFSET = 1;
if ((CURR_X!=x) || (CURR_Y!=y)) {
repaint(CURR_X,CURR_Y,CURR_W+OFFSET,CURR_H+OFFSET);
redSquare.setX(x);
redSquare.setY(y);
repaint(redSquare.getX(), redSquare.getY(),
redSquare.getWidth()+OFFSET,
redSquare.getHeight()+OFFSET);
}
}
public Dimension getPreferredSize() {
return new Dimension(250,200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);
redSquare.paintSquare(g);
}
}
class RedSquare {
private int xPos = 50;
private int yPos = 50;
private int width = 20;
private int height = 20;
public void setX(int xPos){
this.xPos = xPos;
}
public int getX(){
return xPos;
}
public void setY(int yPos){
this.yPos = yPos;
}
public int getY(){
return yPos;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public void paintSquare(Graphics g){
g.setColor(Color.RED);
g.fillRect(xPos,yPos,width,height);
g.setColor(Color.BLACK);
g.drawRect(xPos,yPos,width,height);
}
}
July 12 Update
Here is the latest version, based on some comments received by Swing engineering. This change uses SwingUtilities to build the GUI on the Event Dispatch Thread (EDT). Since AWT and Swing event handlers are by default notified on the EDT, we can leave the moveSquare method as it was. You can check this by invoking SwingUtilities.isEventDispatchThread inside the method and seeing that it returns true. This version also rolls the RedSquare code back into the MyPanel class. If you have lots of objects to maintain in your own app, you may want to factor that code into its own class (like in the first version); I'll probably include that as an exercise at the end of the lesson.
-- Scott Hommel
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;
public class SwingDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame f = new JFrame("Swing Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
}
class MyPanel extends JPanel {
private int squareX = 50;
private int squareY = 50;
private int squareW = 20;
private int squareH = 20;
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
moveSquare(e.getX(),e.getY());
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
}
private void moveSquare(final int x, final int y){
final int OFFSET = 1;
if ((squareX!=x) || (squareY!=y)) {
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
squareX=x;
squareY=y;
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
}
}
public Dimension getPreferredSize() {
return new Dimension(250,200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);
g.setColor(Color.RED);
g.fillRect(squareX,squareY,squareW,squareH);
g.setColor(Color.BLACK);
g.drawRect(squareX,squareY,squareW,squareH);
}
}
Posted at 08:06PM Jul 09, 2007 by The Java Tutorial Team | Comments[13]
Monday Jul 09, 2007
Posted by Ralf on July 10, 2007 at 12:05 AM PDT #
Posted by sdecima on July 10, 2007 at 08:20 AM PDT #
Posted by Mikael Grev on July 10, 2007 at 09:53 AM PDT #
Posted by Mikael Grev on July 10, 2007 at 09:58 AM PDT #
Posted by Scott Hommel on July 10, 2007 at 10:53 AM PDT #
Please don't forget to call all Swing code from EventDispatchThread
the recommended way is to add a static createGui method
and call it from main() with help of SwingUtilities.invokeLater
alexp
Swing team
Posted by Alexander Potochkin on July 10, 2007 at 11:56 AM PDT #
Posted by Mike on July 10, 2007 at 12:08 PM PDT #
Posted by Mikael Grev on July 10, 2007 at 12:52 PM PDT #
Posted by Scott Hommel on July 10, 2007 at 01:33 PM PDT #
Posted by Tom on July 10, 2007 at 11:56 PM PDT #
if ((CURR_X!=x) || (CURR_Y!=y)) { redSquare.setX(x); redSquare.setY(y); repaint(); }I have 1.7Ghz CPU, RADEON 9000, Windows XP. Isn't the sample too simple to make any conclusions about performance? For human it does not make sense if painting was done in 0.0003 seconds or 0.03 seconds hovewer the difference is 100 times. To feel the difference you probably need to demonstrate more sophisticated samples and even better if your had 2 samples: one which uses "advanced" technique and another witch not. But in case you wish to compare with native painting some native painting's measurement's are necessary.Posted by Maxim Zakharenkov on July 11, 2007 at 01:35 AM PDT #
Posted by Scott Hommel on July 11, 2007 at 02:57 PM PDT #
Ah, that's clever – only repairing the bits that have changed. Why didn't I think of that? Sheesh, of course. This is why I love the internet – when I've got a problem, I simply search for the problem and have a solution. Or, I come here to these blogs, and I find a solution. It's brilliant – Kudo's to you guys who are so happy to publish this and information and these resources on the internet!
Posted by online shopping on July 03, 2008 at 02:13 AM PDT #