2D Painting
Opacity
In order to draw using opacity, you must use Graphics.setGlobalAlpha(). This will alter the entire Graphics object, so if you only want it to affect certain parts then you must push a graphics context first, and then set global alpha.
// push a new context so we don't affect the rest of the Graphics object graphics.pushContext( graphics.getClippingRect(), 0, 0 ); // opacity is 0 - 255 graphics.setGlobalAlpha( 128 ); // do something graphics.setColor( Colors.RED ); graphics.fillRect( 0, 0, getWidth(), getHeight()); // pop the context and return to normalsy graphics.popContext();
Graphics Context
A graphics context will preserve the current state of a Graphics object. Pushing a context is similar to cloning the Graphics object and pushing it onto a stack so any changes you make to it (color, opacity, clipping rect, etc) won't affect the previous state once you pop the current context.
Performance
The performance of an app or widget will be greatly reduced the longer paint() takes to execute. Several reasons can cause paint() related performance problems.
- Logger.debug()
- If you intend on leaving debug statements inside paint(), they should be wrapped in a Logger.isDebugEnabled() statement. For the best results, do not leave any log statements uncommented when you are done using them. Calling log statements may cause the creation of StringBuffer objects to be created. Even if the log properties are set to higher than what you are calling and wouldn't even be printed out, a StringBuffer would still be created before the log statement ever receives the text. This issue goes for more than just paint(), and should be considered when debugging throughout your app.
- invalidate()
- NEVER call invalidate() from paint(). It will cause an infinite painting loop (of doom). You may think that it is the solution to your weird painting issues, but really you are just causing more problems.
- Vector.iterator()
- Whenever possible, if you can avoid using a Vector as storage for a painting related widget, you will be increasing performance. Arrays are much faster and will help speed up performance if you have to iterate inside paint().
- If you must iterate inside paint(), avoid using data that is not visible. The data could be hidden because of scrolling or some other reason that would cause painting outside the clipping rect. Before you try to use whatever you are iterating through, check to see if it is visible. If your data is linear (sorted), you have a better chance of ignoring what you do not need. If your data is before the clipping rect you can call continue; to skip an iteration. If your data is after the clipping rect you can call break; to escape the loop. This comes in very handy when painting lists or graphs.
- Caching
- If you need to do heavy work inside paint(), you should try caching values that won't be changing (or at least not changing often). If you can cache values, and only recache when you know they are going to be different, then that will be less work for consecutive paint() calls to do.
- You can cache more than just values. A significant increase in performance can be achieved by caching the paint() results to a bitmap and just redrawing that bitmap until you need to recache values. If your widget is larger than the screen and can be scrolled, you might be better off painting the entire field onto a bitmap, and then just painting the bitmap so it can be scrolled also.
Debugging
If something is not showing up, chances are there is a layout issue. The first step you can take is to make it easier for you to see how much area each field is using. To do this the simplest way is to paint the background or border.
protected void paint(Graphics graphics) { graphics.setBackground( Colors.RED ); graphics.clear(); super.paint( graphics ); }
protected void paint(Graphics graphics) { super.paint( graphics ); graphics.setColor( Colors.RED ); graphics.drawRect( 0, 0, getWidth(), getHeight() ); }
