Before going any further you should read the previous entry, without clicking the WebStart link, SwingX Painters Demo. In this entry I introduced the new painters API that is available in SwingX right now (at least if you grab the source from the CVS repository). The demo I presented was kinda cool but cheated by using a picture to display the mid-section, the part with the wooden background and big fancy lock.
Shortly after releasing this first version of the demo, Richard and I started working on a new graphics pipeline in SwingX which let you add effects on a given painter. This allow you, for instance, to draw a gradient with a painter and then blur it with an effect. Best of all, I checked an image filters library that I knew of to see if there was any changes. This library is wonderful, it offers dozens of professional quality image filters, but used to be way to slow as based on Java 1.1 images. Lucky as we are, its author just released the Java2 version. So Richard wrote an effect,
ImageEffect, to let us plug those filters in our pipeline and here is the result after 20 minutes of coding and Matisse-ing:
Remember this design is a copycat of Neometric's web site so please do not use it outside of this demo.
The window on the left shows the old demo, the ones that uses a picture in the middle of the window. The window on the right is pure Swing and Java2D, save for the lock and shield icons. I never had so much pleasure implementing a nice-looking UI since I just had to write the same operations I used in Photoshop to create the picture used in the first window. The result doesn't look as good as the original, I have to admit I did not spend the time required to fix the spacing, dimensions and colors, but it's still darn fine. If you run the demo with 1.5, you will notice some nasty rendering artifacts when the window gets repainted. Those are due to lack of optimization in our code and they don't happen in Mustang, thanks to the true double buffering. Anyway, let's see some code.
First we'll begin with the text. I just create three panels in Matisse, one for the title, Password Locker and two for the sub-title. In an ideal world I would have used only one panel for the sub-title but our TextPainter still needs some work. By combining text painters and drop shadows we obtain the desired effect:
TextPainter text = new TextPainter(); text.setFont(new Font("Tahoma", Font.BOLD, 32)); text.setText("Password Locker"); text.setPaint(Color.WHITE); text.setLocation(new Point2D.Double(0.0, 0.0)); text.setAntialiasing(RenderingHints.VALUE_ANTIALIAS_ON); ShadowFilter shadow = new ShadowFilter(); shadow.setAngle(-90.0f); shadow.setDistance(3.0f); shadow.setRadius(3.0f); shadow.setOpacity(0.75f); text.setEffect(new ImageEffect(shadow)); appTitle.setBackgroundPainter(text); text = new TextPainter(); text.setFont(new Font("Tahoma", Font.BOLD, 14)); text.setText("The easiest way to store, protect and"); text.setPaint(Color.WHITE); text.setLocation(new Point2D.Double(0.0, 0.0)); text.setAntialiasing(RenderingHints.VALUE_ANTIALIAS_ON); shadow = new ShadowFilter(); shadow.setAngle(-90.0f); shadow.setDistance(2.0f); shadow.setRadius(2.0f); shadow.setOpacity(0.75f); text.setEffect(new ImageEffect(shadow)); appDesc2.setBackgroundPainter(text); text = new TextPainter(); text.setFont(new Font("Tahoma", Font.BOLD, 14)); text.setText("find all your passwords."); text.setPaint(Color.WHITE); text.setLocation(new Point2D.Double(0.0, 0.0)); text.setAntialiasing(RenderingHints.VALUE_ANTIALIAS_ON); text.setEffect(new ImageEffect(shadow)); appDesc.setBackgroundPainter(text);
Don't be scared, the code seems lengthy but it's actually very easy to understand. This is only configuration code, there is no painting logic involved here. As you can see, we first create a painter and then we set an effect, in this case a shadow filter, on it. That's it. Creating the wood background is as easy but requires some experience with imaing tools to come up with the right effect:
BasicGradientPainter gradient = new BasicGradientPainter( new GradientPaint(new Point2D.Double(0.0, 0.0), new Color(0xd67801), new Point2D.Double(0.0, 1.0), new Color(0xb35b01))); NoiseFilter noise = new NoiseFilter(); noise.setDistribution(NoiseFilter.GAUSSIAN); noise.setMonochrome(true); MotionBlurFilter blur = new MotionBlurFilter(); blur.setDistance(20.0f); BrushedMetalFilter metal = new BrushedMetalFilter(); metal.setMonochrome(false); gradient.setEffect(new CompoundEffect(new ImageEffect(noise), new ImageEffect(blur))); ShapePainter shape = new ShapePainter(); shape.setFilled(true); shape.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.55f)); shape.setShape(new Rectangle(0, 0, getWidth(), 6)); GaussianFilter gaussian = new GaussianFilter(); gaussian.setRadius(10.0f); shape.setEffect(new ImageEffect(gaussian)); title.setBackgroundPainter(new CompoundPainter(gradient, shape));
The first step here is to define a gradient painter that fakes the background lighting. The colors are chosen to match those of a polished wood surface. The next step is to create the effects pipeline. The first effect is a gaussian, monochromatic noise generator. If you try with this only filter you will propably cry out your eyes. The magic happens with the next filter, a motion blur, that, applied to the noise, fakes a wood surface. This is not the most realistic wood texture ever created but it works well. The technique described here can also be used to create a brushed metal surface. The final step in the creation process of our background is the addition of a shadow at the top, to give some depth to the header. In this case I used a shape painter to draw a black rectangle at the top that I finally blurred with a nice gaussian blur. The result looks like the shadow cast by the menu bar.
I hope this new example convinced you that painters are very powerful tools. Not only do they let you create nice effects without writing much code, we are here to do this work for you, but they also let you customize a component appearance without subclassing it. And that is very valuable.
If you didn't try, you can run the WebStart demo to see it for real or download the NetBeans project. To build and run the project you will need the latest version of SwingX from the CVS. Check out the java.net project page to get access to the CVS. And remember to check out Richard's tutorial on painters.
P.S: if you use the gradient painter, take a look at its static members, we included some nice looking gradients ready to use just for you.