If you are not redirected to JXLayer 4.0 demonstration by Piet Blok please press the link.

JXLayer, a first impression

This blog is out dated. Please go to JXLayer demonstration by Piet Blok for a new version with completely restructured code.

Note: per 2008-08-26 the sources are slightly modified to comply with the latest version of JXLayer (2008-08-25).

A second blog on JXLayer, describing a generic ZoomUI, can be found here: JXLayer and a generic ZoomUI


Some time ago I noticed the new JXLayer forum on java.net that draw my attention. Mostly because it is being maintained by Alexander Potochkin , also known as alexp or alexfromsun , of whom I've read many very good articles. The forum is dedicated to his new java.net project JXLayer.

What is this project all about? Well, you should read the project page to get the correct answer to this question. But in general it is about a lot of things that you can apply to ordinary JComponents.

Here I will try to give an impression what I so far have been able to do with JXLayer.

A first use: hiding the cursor

For a private project I was playing with images. I implemented a pseudo full screen mode that can be toggled to and from. I decided that I wanted a means to hide the cursor when viewing the image. Well, there was my opportunity to try JXLayer. And indeed, it proved to be very simple to create a LayerUI that, when enabled, let the cursor disappear and reappear for a short while on mouse activity.

So far, so good. However, there was one problem: I had implemented the Scrollable interface in my component to get some choosable behavior like resize to fit in a smaller area, and or resize to fit in a larger area or to have a JScrollPane do its work. This didn't work any more. When I asked the forum for a solution, within hours Alex updated the JXLayer software to also implement Scrollable. My problem solved.

Further explorations

Because of this first succes, I got really curious. But first, to understand the following, a reader must have a basic idea how JXLayer works, or rather, how you work with it. In general there are two mayor classes that you have to deal with:

  1. The JXLayer class. It extends from JComponent, so you can use it as a JComponent. You can wrap a component hierarchy in one JXLayer. JXLayer is a final class, so you have no worries how to extend it.
  2. The LayerUI class. You can set a LayerUI to a JXLayer to get the desired effect. Also, a LayerUI can be enabled or disabled. It is the LayerUI that needs to be extended to implement the desired effects.

One of the first things that came to my mind was: what if I write two LayerUIs, each one with its own specific behavior, and I want to apply both effects on one component hierarchy? Must I write a new LayerUI that combines the functionality of the two? Or, is there a way to apply two different LayerUIs to one JXLayer?

Since a LayerUI can only be set to a JXLayer, not added, I shortly contemplated writing a compound LayerUI. A sort of MultiLayerUI to which other LayerUIs can be added and that delegates its work to these added LayerUIs.

I thought about it, and even started to work on it, but soon discovered that this was way to difficult for me. What methods should delegate and what methods should act on their own? Before I could ever accomplish this task, I needed a much deeper insight in the internals of JXLayer.

The solution

The solution proved to be much easier. What about wrapping one JXLayer into another JXLayer, each of them with its own LayerUI? After all, JXLayer is an ordinary JComponent.

Meanwhile I had implemented three different LayerUIs:

  1. A LayerUI that enables a user to draw lines on the surface with the mouse (like a LayerUI demo made by Alex, but with some differences).
  2. A LayerUI that can hide the cursor
  3. A LayerUI that implements a magnifier glass over a component.

Now I created a component that should have all three layerUIs applied and wrapped this component into three JXLayers as follows (this code is a bit simplified but works):

private JComponent wrap(JComponent component, LayerUI... uis) { 
	JComponent wrappingTarget = component; 
	for (LayerUI layerUI : uis) {
		wrappingTarget = new JXLayer(wrappingTarget, layerUI);
	} 
	return wrappingTarget; 
}

And yes, all three effects work perfectly on my component!

Web Start Wrapped JXLayer

Please note: when trying to find a way to let webstart use jars with different certificates (my jar with my certificate and the JXLayer jar with Alexander's certficate), I obviously did something wrong that messed up the webstart cache. If you get a SecurityException when running the demos, you may try to clear the webstart cache. On Windows XP (that is what I am working on) this is done as follows:

  1. Select Start
  2. Select control panel
  3. Open java
  4. At the general tab press "Temporary Internet Files" Settings
  5. Press Delete Files..
  6. Press OK
Now you may retry to launch the demo.

Sharing LayerUI instances by multiple JXLayer instances

Somewhere in the documentation I read that one LayerUI instance may be shared by more than one JXLayer instance. I could not directly think of a good use for it, because if the LayerUI is disabled, it will be disabled for all JXLayers that use it (and vice versa). But nevertheless, I wanted to give it a try, just out of curiousity.

The first thing that I considered was that a LayerUI that is shared by multiple JXLayers may not maintain state information in its own instance. So I created a new class GeneralLayerUI, extending from AbstractLayerUI, that offers the framework of storing StateObjects as client properties in the affected JXLayer. All my LayerUIs extend from this GeneralLayerUI.

Having done this, it was not difficult to prepare a new test application that is almost the same as the wrapping demo, but this time I create two wrapped components, both with the same LayerUIs.

Note: Alex informed me that sharing is only possible with LayerUIs that do not descent from AbstractBufferedLayerUI. That is quite understandable I guess, because the AbstractBufferedLayerUI maintains a BufferedImage that is of course not sharable by multiple JXLayer instances.

Web Start Wrapped JXLayer

The sources are available here:

SharedTest.html The text in the shared test demo
WrapTest.html The text in the wrap test demo
FlipFlop.java An interface for anything that has a true or false state
FlipFlopAction.java An AbstractAction implementing FlipFlop
GeneralLayerUI.java The base class for my LayerUIs. Implements a StateObject and some other stuff I found convenient.
HideCursorUI.java The LayerUI that lets the cursor disappear
MagnifierUI.java The LayerUI that shows a magnifying glass over its component
MouseDrawingUI.java The LayerUI that let one draw lines on the surface of a component
TestShared.java Tests the use of shared LayerUIs
TestWrapped.java Tests the use of wrapped LayerUIs

Magnification with a value less than one

Update: 2008-08-12.

I read the thread Generic ZoomPanel? on the java.net forums. This blog is referred to in this thread,

Curious what would happen with a magnification that zooms out, I added some factors less than one to the Magnifier. To prevent the original content to shine through, I also gave the glass some color.

I also changed to the newest version of JXLayer and implemented the new methods protected boolean isAWTEventListenerEnabled() and protected long getAWTEventListenerEventMask() in GeneralLayerUI.

Update: 2008-09-06.

The above line in red is a typo. It should read: removed protected long getAWTEventListenerEventMask() in GeneralLayerUI.

In my examples I use the default mask. If I wanted to change that mask, for example to exclude the MouseWheelEvent, I should now use the new method setLayerEventMask().

Normally, one should only ask for events that one really needs to process. In general one will make a selection in the constructor for a LayerUI something like this:

public MyLayerY() {
	super();
	// set the needed mask
	setLayerEventMask(
		AWTEvent.MOUSE_EVENT_MASK 
		| AWTEvent.MOUSE_MOTION_EVENT_MASK 
	//	| AWTEvent.MOUSE_WHEEL_EVENT_MASK 	// This will make JXLayer transparent for the mouseWheel events
		| AWTEvent.KEY_EVENT_MASK 
		| AWTEvent.FOCUS_EVENT_MASK);
	}		
}

However, as a test I wanted to see what happens if I dispatch MouseWheelEvents unconditionally to JXLayer's parent. You will find the override of processMouseWheelEvent in GeneralLayerUI. My implementations of LayerUIs have no constructor.

Today I updated the jxlayer.jar file with the version that Alex produced on 2008-08-29.

A second blog on JXLayer, describing a generic ZoomUI, can be found here: JXLayer and a generic ZoomUI

Author: Piet Blok. If you wish to contact me, please send email to email