Introduction
The aim of this work is to create this cool window:
We have a copy of JFrame behind, but with cool transparency effect and a rounded shape form. The difficulty is to recreate the window header, with title and button for window_state
. But this article can clarify any doubts.
Graphic Package
The UML representation of simple package for our purpose is:
RoundedWindow
is the object shown in previews screenshot. The basic component of package is ShapedFrame
, this is an inheritance of JFrame with the ability to modify its shape (from simple rect shape of every window, even to a circle form) and can apply a transparency effect , on platforms where it is possible.
The JAVA utility to do that is AWTUtilities
, but this is not an official release so I create a Wrapper for this AWTUtilities
with a maximum level of abstraction so that if AWTUtilities
will be changed to another package, you have only to modify AWTUtilitiesWrapper
references and not all occurrences in the source code.
Since ShapedFrame
is the basic inheritance, we have to create an extension like "SHAPEX"Frame, where in our case is RoundedRectFrame
. This object extends ShapedFrame
able to set a transparency and a shape form, and just have an alpha value and a basic shape form management. See below.
Finally there is an utility object: VisualDragHandle
. What is that??? First, I say that a custom shape JFrame has to be "indecorated", so it does not have a handle for dragging all around the screen. So this is an object that has the ability to drag parent window when we click with mouse on itself. The only "must" is that we have to add it directly to contentPane
of JFrame.
Now follows a simple explanation of each class.
ShapedFrame
public abstract class ShapedFrame extends JFrame {
protected Shape shape;
protected float alpha = 1f;
public ShapedFrame() {
setUndecorated(true);
setVisible(true);
setListenersForEffects();
}
private void setListenersForEffects() {
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent evt) {
updateFrameEffects();
}
});
}
public void updateFrameEffects() {
updateShape();
try {
AWTUtilitiesWrapper.setWindowShape(this, shape);
if (shape != null) {
AWTUtilitiesWrapper.setWindowOpacity(this, alpha);
}
} catch (Exception ex) {
Logger.getLogger(ShapedFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
This is not the entire class but only the most important methods. There is the base constructor for setting "UNDECORATE" state of JFrame and for adding Listeners. updateFrameEffects
is the core of advanced graphic of this package: it invokes two important methods of AWTUtilities
for transparency and shaping. This method has to be called everytime when JFrame will be resized. setListenersForEffects
links previous method to resize event.
The only properties are just alpha and shape.
RoundedRectFrame
Whereas the previous class is abstract
(because every shape type has a different behaviour), we have to implement a real shape class like RoundedRectFrame
.
public class RoundedRectFrame extends ShapedFrame {
protected Dimension arcSize = new Dimension(50, 50);
public RoundedRectFrame() {
super(300, 200);
setShapeToDefault();
}
public final void setShapeToDefault() {
shape = new RoundRectangle2D.Double
(0d, 0d, getWidth(), getHeight(), arcSize.width, arcSize.height);
}
@Override
public void updateShape() {
setShapeToDefault();
}
}
Omitting getters and setters, this is the entire class. It is very simple because it has only to explain to father abstract
class the behaviour of a RoundedRect
shape when we want to draw it, resize it, and so on... and then? It only resizes the base RoundedRect
by width and height of source JFrame. The peculiarity is to declare updateShape abstract
in parent
class; this method will be invoked every time JFrame updates itself; so overriding this method we will be sure that displayed shape is what we want.
RoundedWindow
This class is the end of workflow. The class is more comlex so I can't display all the source code.
public class RoundedWindow extends RoundedRectFrame {
private JLabel titleLabel;
private JPanel container;
public RoundedWindow(JPanel defaultPanel) {
super();
super.setLayout(new BorderLayout());
container = defaultPanel;
addHeader();
setTitle("Rounded Window");
}
private void addHeader() {
titleLabel = new JLabel(getTitle());
titleLabel.setForeground(Color.white);
VisualDragHandle dragHandle = new VisualDragHandle(this);
dragHandle.add(titleLabel);
dragHandle.add(getIconifyComponent());
dragHandle.add(getCloseComponent());
super.add(dragHandle, BorderLayout.NORTH);
super.add(container, BorderLayout.CENTER);
}
}
This is only the base of the class. The default constructor gets as parameter a JPanel
because it creates a custom JFrame
where contentPane
is not the effective pane where we work on. As you can see in the first screenshot, the RoundedWindow
is made of a DarkGray
header with title, and a JPanel
with image as container. That is a JPanel
I mean. Infact the rest of class is an override of JFrame
methods for component management. An example is:
@Override
public Component add(Component comp) {
if (container != null) {
return container.add(comp);
}
return super.add(comp);
}
Why? Some users have been understood: after we have added a custom JPanel
as container, when we will add a component to JFrame
we want that component will be added to JPanel
instead. Write comments if you don't understand.
addHeader
method is the creation of header. Basically, we create a VisualDragHandle
as seen before, and we add to this Handle
, that is a simple JPanel
, several elements like title and two object for Iconify
and Close
. The objects are retrieved by two methods: getIconifyComponent
and getCloseComponent
, simple methods that create two custom components, in this case two simple JLable
with relative events, and return them to addHeader
method for adding into header.
Others
In the main class of source code, there is that core code:
CustomPanel panel = new CustomPanel();
CustomPanel panel2 = new CustomPanel();
panel.setImage("glass.jpg");
panel2.setImage("glass.jpg");
JFrame old = new JFrame();
old.setSize(700, 500);
old.setLayout(new BorderLayout());
old.add(panel2, BorderLayout.CENTER);
old.setVisible(true);
final RoundedWindow roundedFrame = new RoundedWindow(panel);
roundedFrame.setSize(700, 500);
roundedFrame.setAlpha(0.9f);
roundedFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
roundedFrame.center();
CustomPanel
is a simple panel (with Dr.House photo & data: ), I have created this with netbeans so you ignore code. The peculiarity is the inheritance from ImagePanel
that is a simple JPanel
with image background, see article on DraggableImageComponent
that is nearly the same thing. The inheritance is made because I want a cool glass background for my window.
The main code then creates with this custom component two frames: a simple JFrame
and a RoundedWindow
frame. You can see the visual differences among two JFrames
in the first screenshot.
Conclusions
You now have a reusable component like RoundedWindow
to create your simple small windows with transparency effects and rounded shape. Although you can extend ShapedFrame
for your custom shapes like : Ovals, Circles, or Polyline; simply follow the RoundedRectFrame
way. If you want (..with a simple vote!!! : ) I can post my work about several Shapes for JFrames
for create Widget like translucent Clock or Calendar and so on...
Prerequisites
Now I have tested this work on:
The build release of JRE is jre1.6.0_17 and currently dosen't work on Linux gnome desktop. I haven't tried it on KDE. So write to me if you try.