Before i start discussing the different implementations of validation overlays that illustrate the power and flexibility of the Swing rendering pipeline, i want to mention some of the common classes that will be used throughout this series. First, here is the screenshot of the sample UI that will be used for all the techniques:
Note that the code focuses on the painting itself, and leaves the custom validation logic (based on the contents of the text field, selected combobox entry etc) to the application code (which is not relevant for discussing the Swing painting pipeline). The common classes are located in the
org.pushingpixels.validation.common packages that contain the following:
SampleUiclass is the UI itself. It uses the FormLayout to create a simple two-column layout with a few controls.
common.ValidationUtilsprovides a very simple implementation of two helper methods. The first is
hasError(Component c)that returns boolean indication whether the specified component should show the error overlay. The second is the
paintErrorIcon(Component c, Graphics g, int x, int y)that paints small error icon at the specified location. More on these two methods below.
ComponentVisitorinterface follows the usual visitor pattern to allow some of the discussed implementations to visit all components starting from a specific root.
- The Visitor class implements the visitor pattern itself, with the
static void visit(ComponentVisitor visitor, Component c, Graphics g)method that recursively visits the entire hierarchy of the specified component, calling the custom visitor logic on each one of the children components.
Note that the first two would be application-specific, and the last two are relevant only for some techniques.
In addition, using the visitor pattern reveals one of the weaker sides of Swing. The basic
JComponent class extends
Container, which means that every core Swing component can have children components. While it allows some nifty tricks like putting a JOGL-powered icon on a button, it unnecessarily exposes the application code to the implementation details of some more complex components. For example, an editable
JComboBox is implemented with a text field and a button. A
JSpinner is implemented as a text field and two buttons. Unfortunately, these are counted in the
getComponentCount() and returned in
getComponent(int index), which makes it rather annoying for the visitor pattern.
Subsequently, the sample implementation of the
hasError(Component c) method in
common.ValidationUtils makes special checks when it is passed a text field. If the text field has either combobox or spinner parent, it is ignored.
The next entry will show the first extension point in the Swing rendering pipeline – the repaint manager.