Custom Painting Approaches
Posted by Rob Camick on May 8, 2009
The basics of custom painting are explained in the section from the Swing tutorial on Custom Painting (see link below). The main idea is that you can customize a component by overriding its paintComponent() method. Typically, JComponent or JPanel will be overridden to do custom painting. A concern of many people is adding too much painting code to the paintComponent method which might result in excessive CPU usage or slow painting. Is this a valid concern and if so, then is there anything that can be done to minimize these problems?
First, lets look at the normal approach of painting which I’m going to refer to as “active” painting for the purposes of this blog. I call this “active” because painting is done directly on the component from scratch every time the paintComponent() method is invoked. Typically you would store information about the object to be painted in a List. Then, whenever the paintComponent() method is invoked you would iterate through the List and paint each object. Some advantages of this approach:
- easy to use as all the painting code is contained in one place
- allows for easy changing of the painting (ie. animation)
- allows for easy undo of existing painting
- the painting area can dynamically change in size
Some drawbacks of this approach:
- can be slow depending on the complexity of the painting (ie. when painting requires lots of calculations or maybe animation of many objects)
I’m going to refer to the following alternative approach to painting as the “passive” approach. This is because the “active” painting will be done to a BufferedImage as required. Then, the paintComponent() method, whenever it is invoked, will “passively” draw the BufferedImage onto the component. Some advantages of this approach:
- will result in faster painting. The relative performance increase will depend on the complexity of the painting.
Some drawbacks of this approach:
- slightly harder to use as you need to create the BufferedImage and manage the painting to it
- painting is done in multiple places
- harder to undo painting
- the painting area is fixed in size
The best way to understand the differences between the two approaches is to look at code and see each approach in action, so I have included code for each approach below. The functionality of each class is the same and attempts to address “how to”:
- do static painting, in this case, by drawing a line of text.
- change the painting dynamically, in this case, by adding rectangles of a different color.
- reset the painting to its original state.
The differences between each approach are contained in the following three methods:
- paintComponent – responsible for the custom painting whenever repaint is invoked on the panel. For the active approach you need to draw the static text and loop through the list to paint all the rectangles. For the passive approach you simply draw the buffered image.
- mouseReleased – responsible for adding the dynamic rectangles. For the active approach you just add a rectangle to the list. For the passive approach you draw the rectangle on the buffered image right away.
- clear – responsible for resetting the panel to its initial state. For the active approach you just clear the list. For the passive approach you need to recreate (or clear) the buffered image and then draw the static text.
Take the time to understand the code differences in each of the above methods and hopefully you will understand the differences in the two approaches.
Now, for fun, while using each of the programs try to duplicate my happy face drawing from the image below:
Did you notice any performance difference between either of the programs? I doubt you will. It has been my experience that you would need to draw hundreds or thousands of rectangles before you would notice any difference. Therefore my suggestion would be to start with the normal or “active” approach to painting. Then, if you experience a problem you can switch to the alternate or “passive” approach of using a BufferImage.
If you want to have move fun try adding animation to each program. Something simple like randomly changing the location of each rectangle.