Java Swing 中的任意形状透明 JFrame
为自定义 JFrame 创建形状,并在 Windows 平台上为整个窗口应用透明效果(0 到 1 的 alpha 值)
引言
这项工作的目的是创建这个炫酷的窗口

我们在后面有一个 JFrame 副本,但具有炫酷的透明效果和圆角形状。困难之处在于重新创建窗口标题栏,带有标题和用于 window_state
的按钮。但本文可以澄清任何疑问。
图形包
我们目的的简单包的 UML 表示是

RoundedWindow
是预览截图中的对象。包的基本组件是 ShapedFrame
,这是 JFrame 的一个继承类,它能够修改其形状(从每个窗口的简单矩形形状,甚至到圆形),并在可能的情况下应用透明效果。
实现此目的的 JAVA 工具是 AWTUtilities
,但这不是官方版本,因此我创建了一个 AWTUtilities
的包装器,具有最高级别的抽象,以便如果 AWTUtilities
被更改为另一个包,您只需修改 AWTUtilitiesWrapper
引用,而不是源代码中的所有出现。
由于 ShapedFrame
是基本继承,我们必须创建一个像“SHAPEX”Frame 这样的扩展,在我们的例子中是 RoundedRectFrame
。此对象扩展了 ShapedFrame
,能够设置透明度和形状,并且仅具有 alpha 值和基本形状管理。请参见下文。
最后有一个实用对象:VisualDragHandle
。这是什么??首先,我需要说明自定义形状的 JFrame 必须是“未装饰的”,因此它没有用于在屏幕上拖动的句柄。所以这是一个对象,当鼠标点击它时,它能够拖动父窗口。唯一的要求是必须将其直接添加到 JFrame 的 contentPane
。
现在将对每个类进行简单的解释。
ShapedFrame
public abstract class ShapedFrame extends JFrame {
protected Shape shape;
protected float alpha = 1f;
public ShapedFrame() {
//If is DECORATED effects are disabled
setUndecorated(true);
setVisible(true);
setListenersForEffects();
}
//.........................
private void setListenersForEffects() {
//It is important to upadate visual effect on form resize.
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);
}
}
//.........................
}
这不是整个类,而是最重要的方法。有一个设置 JFrame “UNDECORATE”状态和添加监听器的基本构造函数。updateFrameEffects
是此包高级图形的核心:它调用 AWTUtilities
的两个重要方法来实现透明度和形状。每次 JFrame 调整大小时都必须调用此方法。setListenersForEffects
将前一个方法链接到 resize 事件。
唯一的属性就是 alpha 和形状。
RoundedRectFrame
因为前一个类是 abstract
(因为每种形状类型都有不同的行为),我们必须实现一个真实的形状类,如 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();
}
}
省略 getter 和 setter,这是整个类。它非常简单,因为它只需要向父 abstract
类解释 RoundedRect
形状在我们要绘制它、调整它等方面时的行为……然后呢?它仅通过源 JFrame 的宽度和高度来调整 RoundedRect
的大小。特殊之处在于在 parent
类中声明 updateShape abstract
,这个方法将在 JFrame 更新自身时被调用;因此,通过重写此方法,我们将确保显示的形状是我们想要的。
RoundedWindow
此类是工作流程的终点。此类比较复杂,因此无法显示所有源代码。
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);
}
//............................
}
这只是类的基础。默认构造函数将 JPanel
作为参数,因为它创建一个自定义 JFrame
,其中 contentPane
不是我们实际工作的窗格。正如您在第一个屏幕截图中看到的,RoundedWindow
由一个带有标题的 DarkGray
标题栏和一个带有图像作为容器的 JPanel
组成。实际上,这就是一个 JPanel
。类其余部分是对 JFrame
组件管理方法的重写。例如:
@Override
public Component add(Component comp) {
if (container != null) {
return container.add(comp);
}
return super.add(comp);
}
为什么?一些用户已经理解了:在我们添加了一个自定义 JPanel
作为容器后,当我们向 JFrame
添加组件时,我们希望该组件被添加到 JPanel
中,而不是直接添加到 JFrame 中。如果您不理解,请写注释。
addHeader
方法是标题栏的创建。基本上,我们创建一个 VisualDragHandle
,如前所述,并向这个 Handle
(一个简单的 JPanel
)添加几个元素,如标题和两个用于 Iconify
和 Close
的对象。这些对象通过两个方法检索:getIconifyComponent
和 getCloseComponent
,这些方法创建两个自定义组件,在本例中是两个简单的 JLable
及其相关事件,并将它们返回给 addHeader
方法以添加到标题栏中。
其他
在源代码的主类中,有这个核心代码:
CustomPanel panel = new CustomPanel();
CustomPanel panel2 = new CustomPanel();
panel.setImage("glass.jpg");
panel2.setImage("glass.jpg");
//Standard JFrame creation
JFrame old = new JFrame();
old.setSize(700, 500);
old.setLayout(new BorderLayout());
old.add(panel2, BorderLayout.CENTER);
old.setVisible(true);
//New Cool Frame cration
final RoundedWindow roundedFrame = new RoundedWindow(panel);
roundedFrame.setSize(700, 500);
roundedFrame.setAlpha(0.9f);
roundedFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
roundedFrame.center();
CustomPanel
是一个简单的面板(带有 Dr.House 照片和数据:),我是用 netbeans 创建的,所以请忽略代码。特殊之处在于它继承自 ImagePanel
,这是一个带有图像背景的简单 JPanel
,请参阅有关 DraggableImageComponent
的文章,它几乎是相同的。之所以继承,是因为我想要一个炫酷的玻璃背景为我的窗口。
主代码然后使用这个自定义组件创建两个框架:一个简单的 JFrame
和一个 RoundedWindow
框架。您可以在第一个屏幕截图中看到这两个 JFrames
之间的视觉差异。
结论
现在您有了一个可重用的组件,如 RoundedWindow
,用于创建具有透明效果和圆角的简单小型窗口。虽然您可以扩展 ShapedFrame
来创建自定义形状,如:椭圆、圆形或折线,只需遵循 RoundedRectFrame
的方式即可。如果您想要(……只需简单投票!!!:)我可以发布我关于多种 JFrames
形状的工作,用于创建类似半透明时钟或日历等的窗口小部件。
必备组件
现在我已在以下平台上测试了此工作:
- Windows Vista
- Windows 7
JRE 的构建版本是 jre1.6.0_17,目前在 Linux GNOME 桌面下无法正常工作。我没有在 KDE 上测试过。所以如果您测试过,请告诉我。