Java Swing 中的 UI 组件开发 - 第 1 部分:设计






4.76/5 (15投票s)
这是一个循序渐进的指南,以初学者的角度,有效地创建 Java UI 组件。本教程将带您完成从初始化到美观 UI 组件的创建。
引言
Java 经过多年的发展,已经非常成熟。自诞生以来已经过去十几年半,Java 语言依然保持着强大的生命力,并积极探索新的技术范式和语言特性。自“一次编写,到处运行”("Write Once, Run Anywhere" - 昵称 WORA)这一著名口号问世以来,Java 打破了所有传统的编程障碍。时至今日,没有其他主流编程语言能够编译出能在各种平台上运行的代码。谁能想到,一个对 C++ 的简单改进,竟然会发展成为所有现代编程语言的母体?事实上,所有 .NET 编程语言的存在都归功于 Java 这一出色的语言。
Java 编程
尽管本文是为 Java 初学者量身定制的,但仍需要具备 Java 语言基础知识,并对 Java Swing 框架的工作原理有所了解。Swing 本质上是一个窗口框架,可帮助开发人员编写代码来创建 Windows 和桌面应用程序。在本文中,我将尽力解释 Swing 的一些设计原则。
在 Swing 中构建自定义 UI 组件
尽管 Swing 框架几乎提供了开发人员可用的所有 UI 组件,例如按钮、标签、文本字段等,但仍然有些东西是您自己想要的。事实证明,在 Swing 中创建此类自定义组件非常容易。而且,一旦您克服了最初的困惑,您就会开始享受这个过程。在撰写本文之前,我最大的顾虑之一可能是 CodeProject 上没有一篇像样的文章来解释如何在 Swing 中创建自定义组件。所以,现在就开始。
使用 Swing 框架构建的每个自定义组件都必须遵循一些非常重要的准则。其中第一条是继承 javax.swing.JComponent
类。下面是创建自定义组件的方法:
import javax.swing.JComponent;
public class FancyButton extends JComponent
{
public FancyButton()
{
// .. component construction code comes here
}
}
简而言之,上面的代码创建了一个名为 FancyButton
的自定义组件,该组件仅通过继承 javax.swing.JComponent
类来标识自己。但是,您将无法运行该代码。原因如下:
- 您没有 main 方法;
- 您没有窗口来显示您的自定义组件。
现在让我们来做这件事。
import javax.swing.JComponent;
import javax.swing.JFrame;
public class FancyButton extends JComponent
{
public FancyButton()
{
// .. component construction code comes here
}
public static void main(String[] args)
{
JFrame frame = new JFrame("Test Frame"); // Create a window to display component.
frame.setSize(400, 400); // Set a default window size.
frame.setLocationRelativeTo(null); // Brings the window to the center of
// the screen.
frame.setDefaultCloseOperation( // Tells the program to terminate if the
JFrame.EXIT_ON_CLOSE); // close [x] button is pressed.
frame.setVisible(true); // Show the window.
}
}
我们现在已经为实验的开始做好了准备。在上面的代码中,我们创建了一个 javax.swing.JFrame
,这基本上是 Java 的桌面窗口版本,带有标题以及最小化、恢复/最大化和关闭按钮。这里我们将窗口标题设置为“Test Frame”,这对于本教程来说足够了。但是,请注意一件事:我们还没有构建自定义组件,也没有将其放置在框架上。现在让我们来做。
import javax.swing.JComponent;
import javax.swing.JFrame;
public class FancyButton extends JComponent
{
public FancyButton()
{
}
public static void main(String[] args)
{
JFrame frame = new JFrame("Test Frame");
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FancyButton button = new FancyButton(); // Initialize the component.
frame.getContentPane().add(button); // Place the component on the application
// window such that it fills the whole
// window frame.
frame.setVisible(true);
}
}
如果您现在运行代码,肯定会感到失望。这是因为您会看到与上一个示例中相同的旧 JFrame
,而没有太大变化。原因之一是您的组件没有在屏幕上绘制。让我们现在来解决这个问题。此外,您应该注意到程序代码开头不断增长的导入语句。在下一版代码更新中,我们将使用 *
(星号)进行动态导入,以便 java.awt
和 javax.swing
包下的所有类都能被导入,而无需显式导入到程序代码中。为了清晰起见,我们还将截断 main(String[])
方法,但您应该将其包含在您上一个示例的程序中。
import java.awt.*; // Notice these dynamic imports
import javax.swing.*;
public FancyButton extends JComponent
{
public FancyButton()
{
}
@Override
public void paintComponent(Graphics graphics)
{
graphics.setColor(Color.red);
graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
}
public static void main(String[] args)
{
// .. use code from previous code listing 1.3
}
}
这次当您运行代码时,您会注意到整个窗口框架会变成一个红色的表面(请参阅下面的截图)。框架上所有变红的部分是我们新绘制组件的区域边界。通过覆盖 JComponent
类中的 paintComponent(Graphics)
方法来启用组件的绘制。注意在方法声明前显式使用 @Override
注释。这确保了方法在子类 FancyButton
中得到正确重写。
因为我们的组件横跨了整个窗口框架,所以我们看到整个窗口框架变成了一整块红色。
在 paintComponent(Graphics)
方法的体内部,我们使用获取的 java.awt.Graphics
对象进行绘制。graphics.setColor(Color)
方法将颜色缓冲区设置为红色,下一行中,再次使用 graphics
对象在窗口框架的 [0, 0] 点创建一个矩形,其宽度和高度为组件的宽度和高度。这是使用 graphics.fillRect(int x, int y, int width, int height)
方法实现的。现在,由于我们将组件放置在窗口框架的正中央,它占据了框架的所有空间,给人一种窗口框架整体变红的错觉。
理解 Graphics 类的基本知识
现在,在我们开始探索组件的绘制过程之前,我们需要更多地了解 java.awt.Graphics
类。Graphics
类提供了各种方法,用于在任何组件或容器上绘制不同的形状、线条和字体,从而使我们能够从头开始完全设计一个组件。因此,通过重写任何组件上的 paintComponent(Graphics)
方法,我们可以为组件设计出美观的界面。该类提供的一些方法如下:
形状填充方法
fillRect(Rectangle rectangle)
fillRect(int x, int y, int width, int height)
fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
fillOval(int x, int y, int width, int height)
fillPolygon(Polygon polygon)
fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
形状描边/绘制方法
drawRect(Rectangle rectangle)
drawRect(int x, int y, int width, int height)
drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
drawOval(int x, int y, int width, int height)
drawPolygon(Polygon polygon)
drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
drawLine(int x1, int y1, int x2, int y2)
面对如此多的选择,我们可以选择这些方法中的任何一种来绘制漂亮的形状,从而定义我们组件的设计和结构。形状填充和形状描边方法之间的区别非常简单。形状填充方法用颜色缓冲区(我们使用 Graphics
类的 setColor(Color)
方法设置的颜色)填充特定形状的区域。而形状描边/绘制方法仅为形状绘制边界线(边框)。在本教程的后续部分中,我们将找到几乎所有这些方法的用途。现在,我们需要定义我们想要创建的组件类型。
鉴于本教程的目的,我们已经将组件命名为 FancyButton
,您可能已经猜到它与按钮有关。但是,在我们继续之前,我们需要定义我们真正想要按钮做什么,或者至少我们希望我们的按钮看起来是什么样子。我称这个过程为**组件设计清单**。
实用技巧 # 1:构建您的组件设计清单
这听起来可能是一项枯燥的任务,但归根结底,没有比跟踪进度更好的方法了。最好的起点是创建一个简单的“是”和“否”清单。这个简单的清单将帮助我们确定哪些需要做,哪些可以忽略。
最初尝试创建组件设计清单。这有助于将我的创造力限制在简单的“是”和“否”列表中。
根据我创建的清单来约束自己,我对清单 1.4 进行了以下修改:
import java.awt.*;
import javax.swing.*;
public FancyButton extends JComponent
{
public FancyButton()
{
}
@Override
public void paintComponent(Graphics graphics)
{
graphics.setColor(Color.gray);
graphics.fillRoundRect(2, 2, this.getWidth() - 4,
this.getHeight() - 4, 30, 30);
}
// .. main method truncated - use as in code listing 1.3
}
修改包括将组件的颜色从闪烁的红色更改为不错的灰色,这样对眼睛更友好。我使用了 fillRoundRect(...)
代替 fillRect(...)
,并将 arcWidth
和 arcHeight
设置为 30 像素。这个简单的效果使按钮看起来更漂亮、更圆润。此外,我还通过更改 fillRoundRect(...)
方法中的值,为组件添加了 2 像素的边框。这将有助于我稍后可视化我清单中提到的边框设置。如果您看下面的截图,您会立即注意到区别。
此截图显示了在实现组件设计清单中的一些操作后,对初始代码所做的修改。但很快,您就会看到一些新的、即时出现的问题。其中一个问题是圆角的锯齿状显示。显然,发生这种情况是因为默认情况下,Java 2D 绘制例程不渲染抗锯齿输出。这应该很容易解决。更重要的是,这个问题直接添加到我的清单中:启用抗锯齿渲染。
在后续的修订中,我们将探讨如何解决这些个别问题,以创建一个美观的自定义 Swing UI 组件。敬请期待更多内容。
历史
- 创建日期:2010 年 3 月 1 日。