SwingX Tutorial – Painters

Posted by

Introduction

SwingX is an extension to the Swing API by the folks who work on the Swing team. The code is released as open source on Sun SwingLabs. Parts of the API that are mature are supposed to show up in Java 7. It’s a great way to get a sneak peek at new components and technologies that the Swing team is working on, but it’s not ready for an official release, which takes time, etc.

One of the biggest changes with the SwingX API is it’s use of painters. Typically, if you wanted to create new components in Swing, you would have to mess with the Look and Feel API. Painters give you the ability to use Java2D code to very quickly define the look of your component.

About developerlife.com

Once you are familiar with painters, after reading this tutorial, we have lots more in-depth material that will show you how to use SwingX in the real world on this site. We cover topics such as creating multithreaded Swing apps to consume services, using glasspane for animations, animated transitions, details on canceling SwingWorker tasks in flight, and many more. We also host tutorials on lots of other technologies, like XML – SAX, DOM, Using Java to manipulate XML, database access using XML, parsing XML from web services, etc. You can find them all here. We have tutorials on GWT, multithreading in Java, as well as SOA related topics, and JavaME/J2ME content as well.

JXPanel

SwingX comes with it’s own custom repaint manager (which replaces the default implementation used by Swing; there can only be one active at any given time). This custom repaint manager respects the transparency of a top level SwingX container – eg, JXPanel. Unlike a JPanel, when you place a JButton on a JXPanel, and set the button’s transparency, it will retain it’s transparency even when you click on the button. In the case of JPanel, when the JButton first appears, it will be transparent, but as soon as you click on it, it will become opaque. You can do things like change the transparency of all components in a JXPanel for example, and lots of other neat effects. You can tie this into the TimingFramework and very quickly create really cool animations in your user interface (like change the transparency of your components by changing the “alpha” property from 0.0f to 1.0f to fade in a panel).

Painters

The Painters API is refreshingly simple. All the Java2D code that you want to use to paint your component, that would otherwise go in a subclass of a Swing component who’s paint() or paintComponent() method you’ve overridden, is placed in a class which implements the Painter<T> interface, which is defined thusly:

 1: public void paint(Graphics2D g, T object, int width, int height);

There are lots of Painter implementations provided for you, and you can even composite various existing painter implementations to create new ones. You are limited by your imagination and proficiency with the Java2D API 🙂 . All the SwingX components are Painter enabled – they all have a setBackgroundPainter(Painter<T>) method.

The Painters API

Out of the box, SwingX comes with a rich set of Painter implementations, some of which are listed here:

  1. GlossPainter – this paints a gloss effect on your component, you can pick the gloss Paint object and gloss position (top/bottom). The gloss effect is painted as a filled arc that highlights the component with whatever color you provided. The Paintobject that’s used to paint the gloss effect can be a simple Color, or a gradient or a texture.
  2. ImagePainter – this paints a BufferedImageon your component. You have control over how you want the image painted. You can choose to have it scale to the site of the area that needs to be painted, or you can have it tiled, or centered. There are lots of layout options that you can pick.
  3. MattePainter – this uses a Paintobject to paint your component. This can be used to fill the component area with a color, gradient, or texture.
  4. PinstripePainter – this paints pinstripes on your component, and you can control the angle, width, distance apart, and color (Paintobject) of these stripes. Instead of using a color, you can use a gradient, or texture, etc.
  5. CompoundPainter – this allows you to add multiple painter implementations to a component. The painters will be rendered in the order in which they are added to the CompoundPainter. It can be used to quickly create cool effects.
  6. RectanglePainter – this paints a rectangle on your component. You can select a fill Paint object and background Paint object. There are lots of other options like border size, and rounded borders.

There are other painters like ShapePainter, TextPainter, URLPainter, CheckerboardPainter, and BusyPainter. You can extend AbstractPainter to create your own. The code examples below will get you started using Painters; experiment with the API and create your own effects using the provided painters, and composite them using CompoundPainter.

Source code example 1

In the following example, I’m going to create a JXLabel and set a compound background painter on it. Here’s what it looks like:

image2

Here’s the code which produced this painter:

 1: /**
 2:  * this compound painter composites a bunch of different painters.
 3:  */
 4: private Painter getPainter() {
 5:   // set the background painter
 6:   MattePainter mp = new MattePainter(Colors.LightBlue.alpha(0.5f));
 7:   GlossPainter gp = new GlossPainter(Colors.White.alpha(0.3f),
 8:                                      GlossPainter.GlossPosition.TOP);
 9:   PinstripePainter pp = new PinstripePainter(Colors.Gray.alpha(0.2f),
 10:                                              45d);
 11:   return (new CompoundPainter(mp, pp, gp));
 12: }

In this example, I create 3 painters and composite them and apply them to the JXLabel (which is the SwingX label class):

  1. MattePainter – I fill the component with a translucent light blue color (50% transparent).
  2. GlossPainter – I apply a white gloss (30% transparent) to the top of the label.
  3. PinstripePainter – I apply a gray (50% transparent) color to the label.

These painters are applied in the order in which they are added to the CompoundPainter – first MattePainter, then PinstripePainter, and finally GlossPainter. If you look at the screenshot, you will see that the Pinstripes are BEHIND the Gloss effect. Also, notice that the text “Painter Example 1” is in front of the painters, since they are all background painters. If you want to add text behind a painter, then use the TextPainter and make sure to add it in the right order in your CompositePainter.

Here’s the full source code listing for this example:

 1: import org.jdesktop.swingx.*;
 2: import org.jdesktop.swingx.painter.*;
 3:
 4: import javax.swing.*;
 5: import java.awt.*;
 6:
 7: /**
 8:  * PainterExample1
 9:  *
 10:  * @author Nazmul Idris
 11:  * @version 1.0
 12:  * @since Dec 25, 2007, 3:54:13 PM
 13:  */
 14: public class PainterExample1 {
 15:
 16: /**
 17:  * simple main driver for this class
 18:  */
 19: public static void main(String[] args) {
 20:   SwingUtilities.invokeLater(new Runnable() {
 21:     public void run() {
 22:       new PainterExample1();
 23:     }
 24:   });
 25: }
 26:
 27: /**
 28:  * creates a JFrame and calls {@link #doInit} to create a JXPanel and adds the panel to this frame.
 29:  */
 30: public PainterExample1(){
 31:   JFrame frame = new JFrame("Painter Example 1");
 32:
 33:   // add the panel to this frame
 34:   frame.add(doInit());
 35:
 36:   // when you close the frame, the app exits
 37:   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 38:
 39:   // center the frame and show it
 40:   frame.setLocationRelativeTo(null);
 41:   frame.pack();
 42:   frame.setVisible(true);
 43: }
 44:
 45: /**
 46:  * creates a JXLabel and attaches a painter to it.
 47:  */
 48: private Component doInit() {
 49:   JXPanel panel = new JXPanel();
 50:   panel.setLayout(new BorderLayout());
 51:
 52:   JXLabel label = new JXLabel();
 53:   label.setFont(new Font("Segoe UI", Font.BOLD, 14));
 54:   label.setText("Painter Example 1");
 55:   label.setHorizontalAlignment(JXLabel.CENTER);
 56:   label.setBackgroundPainter(getPainter());
 57:
 58:   panel.add(label, BorderLayout.CENTER);
 59:
 60:   panel.setPreferredSize(new Dimension(250,100));
 61:
 62:   return panel;
 63: }
 64:
 65: /**
 66:  * this compound painter composites a bunch of different painters.
 67:  */
 68: private Painter getPainter() {
 69:   // set the background painter
 70:   MattePainter mp = new MattePainter(Colors.LightBlue.alpha(0.5f));
 71:   GlossPainter gp = new GlossPainter(Colors.White.alpha(0.3f),
 72:                                      GlossPainter.GlossPosition.TOP);
 73:   PinstripePainter pp = new PinstripePainter(Colors.Gray.alpha(0.2f),
 74:                                              45d);
 75:   return (new CompoundPainter(mp, pp, gp));
 76: }
 77: }//end class PainterExample1

The other interesting class is the Color enumeration, that makes it easy to work with colors. I will write a tutorial on Java5 enumerations and how they make it really easy to create type safe constants, and much more. It allows you to easily work with custom colors that you’ve created, and you can get translucent colors conveniently, and you can get HEX encoded strings to use in HTML. Here’s a listing of the Color enum:

 1: import java.awt.*;
 2:
 3: /**
 4:  * Colors is an enumeration class that makes it easier to work with colors. Methods are provided for
 5:  * conversion to hex strings, and for getting alpha channel colors.
 6:  *
 7:  * @author Nazmul Idris
 8:  * @version 1.0
 9:  * @since Apr 21, 2007, 12:55:24 PM
 10:  */
 11: public enum Colors {
 12:
 13: //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 14: // various colors in the pallete
 15: //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 16:   Pink(255, 175, 175),
 17:   Green(159, 205, 20),
 18:   Orange(213, 113, 13),
 19:   Yellow(Color.yellow),
 20:   Red(189, 67, 67),
 21:   LightBlue(208, 223, 245),
 22:   Blue(Color.blue),
 23:   Black(0, 0, 0),
 24:   White(255, 255, 255),
 25:   Gray(Color.gray.getRed(), Color.gray.getGreen(), Color.gray.getBlue());
 26:
 27: //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 28: // constructors
 29: //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 30:
 31: Colors(Color c) {
 32:   _myColor = c;
 33: }
 34:
 35: Colors(int r, int g, int b) {
 36:   _myColor = new Color(r, g, b);
 37: }
 38:
 39: Colors(int r, int g, int b, int alpha) {
 40:   _myColor = new Color(r, g, b, alpha);
 41: }
 42:
 43: Colors(float r, float g, float b, float alpha) {
 44:   _myColor = new Color(r, g, b, alpha);
 45: }
 46:
 47: //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 48: // data
 49: //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 50:
 51: private Color _myColor;
 52:
 53: //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 54: // methods
 55: //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 56:
 57: public Color alpha(float t) {
 58:   return new Color(_myColor.getRed(), _myColor.getGreen(), _myColor.getBlue(), (int) (t * 255f));
 59: }
 60:
 61: public static Color alpha(Color c, float t) {
 62:   return new Color(c.getRed(), c.getGreen(), c.getBlue(), (int) (t * 255f));
 63: }
 64:
 65: public Color color() { return _myColor; }
 66:
 67: public Color color(float f) {
 68:   return alpha(f);
 69: }
 70:
 71: public String toString() {
 72:   StringBuilder sb = new StringBuilder();
 73:   sb.append("r=")
 74:       .append(_myColor.getRed())
 75:       .append(", g=")
 76:       .append(_myColor.getGreen())
 77:       .append(", b=")
 78:       .append(_myColor.getBlue())
 79:       .append("n");
 80:   return sb.toString();
 81: }
 82:
 83: public String toHexString() {
 84:   Color c = _myColor;
 85:   StringBuilder sb = new StringBuilder();
 86:   sb.append("#");
 87:   sb.append(Integer.toHexString(_myColor.getRed()));
 88:   sb.append(Integer.toHexString(_myColor.getGreen()));
 89:   sb.append(Integer.toHexString(_myColor.getBlue()));
 90:   return sb.toString();
 91: }
 92:
 93: }//end enum Colors

Source code example 2

In this second example, I will show you how to use a different painter to draw a gradient fill on a component, and change it’s transparency using JXLabel. Here’s a screenshot:

image3

Here’s the code for the painter:

 1: /** this painter draws a gradient fill */
 2: public Painter getPainter() {
 3:   int width = 100;
 4:   int height = 100;
 5:   Color color1 = Colors.White.color(0.5f);
 6:   Color color2 = Colors.Gray.color(0.5f);
 7:
 8:   LinearGradientPaint gradientPaint =
 9:       new LinearGradientPaint(0.0f, 0.0f, width, height,
 10:                               new float[]{0.0f, 1.0f},
 11:                               new Color[]{color1, color2});
 12:   MattePainter mattePainter = new MattePainter(gradientPaint);
 13:   return mattePainter;
 14: }

The MattePainter uses a Paint object, which draws a gradient fill (using LinearGradientPaint class).

The following line of code makes the JXPanel and everything inside it 50% transparent:

 1: // set the transparency of the JXPanel to 50% transparent
 2: panel.setAlpha(0.5f);

Here’s the complete code listing for this example:

 1: import org.jdesktop.swingx.*;
 2: import org.jdesktop.swingx.painter.*;
 3:
 4: import javax.swing.*;
 5: import java.awt.*;
 6:
 7: /**
 8:  * PainterExample2
 9:  *
 10:  * @author Nazmul Idris
 11:  * @version 1.0
 12:  * @since Dec 25, 2007, 3:54:13 PM
 13:  */
 14: public class PainterExample2 {
 15:
 16: /** simple main driver for this class */
 17: public static void main(String[] args) {
 18:   SwingUtilities.invokeLater(new Runnable() {
 19:     public void run() {
 20:       new PainterExample2();
 21:     }
 22:  });
 23: }
 24:
 25: /** creates a JFrame and calls {@link #doInit} to create a JXPanel and adds the panel to this frame. */
 26: public PainterExample2() {
 27:   JFrame frame = new JFrame("Painter Example 2");
 28:
 29:   // add the panel to this frame
 30:   frame.add(doInit());
 31:
 32:   // when you close the frame, the app exits
 33:   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 34:
 35:   // center the frame and show it
 36:   frame.setLocationRelativeTo(null);
 37:   frame.pack();
 38:   frame.setVisible(true);
 39: }
 40:
 41: /** creates a JXLabel and attaches a painter to it. */
 42: private Component doInit() {
 43:   JXPanel panel = new JXPanel();
 44:   panel.setLayout(new BorderLayout());
 45:
 46:   JXLabel label = new JXLabel();
 47:   label.setFont(new Font("Segoe UI", Font.BOLD, 14));
 48:   label.setText("Painter Example 2");
 49:   label.setHorizontalAlignment(JXLabel.CENTER);
 50:   label.setBackgroundPainter(getPainter());
 51:
 52:   // set the transparency of the JXPanel to 50% transparent
 53:   panel.setAlpha(0.5f);
 54:
 55:   panel.add(label, BorderLayout.CENTER);
 56:   panel.setPreferredSize(new Dimension(250, 100));
 57:
 58:   return panel;
 59: }
 60:
 61: /** this painter draws a gradient fill */
 62: public Painter getPainter() {
 63:   int width = 100;
 64:   int height = 100;
 65:   Color color1 = Colors.White.color(0.5f);
 66:   Color color2 = Colors.Gray.color(0.5f);
 67:
 68:   LinearGradientPaint gradientPaint =
 69:      new LinearGradientPaint(0.0f, 0.0f, width, height,
 70:                              new float[]{0.0f, 1.0f},
 71:                              new Color[]{color1, color2});
 72:   MattePainter mattePainter = new MattePainter(gradientPaint);
 73:   return mattePainter;
 74: }
 75:
 76: }//end class PainterExample2

Download the code

You can download the 0.9.1 release of SwingX here:

  1. Binaries – https://swingx.dev.java.net/files/documents/2981/76227/swingx-0.9.1.zip
  2. Sources – https://swingx.dev.java.net/files/documents/2981/76229/swingx-0.9.1-src.zip
  3. JavaDocs – https://swingx.dev.java.net/files/documents/2981/76228/swingx-0.9.1-javadoc.zip

You can download the source code for this tutorial, which includes the SwingX libraries, along with IDEA projects for the code here.

Next steps

We are planning on releasing a whole suite of tutorials that will show you how to use more SwingX components along with TimingFramework and AnimatedTransitions to create rich effects in your applications. Here are other tutorials that cover more SwingX components:

  1. Task Panes – JXTaskPane, JXTaskPaneContainer.
  2. Indeterminate Progress – JXBusyLabel.