65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (15投票s)

2010年3月1日

CPOL

8分钟阅读

viewsIcon

78728

这是一个循序渐进的指南,以初学者的角度,有效地创建 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 类。下面是创建自定义组件的方法:

列表 1.1
import javax.swing.JComponent;

public class FancyButton extends JComponent
{
  public FancyButton()
  {
    // .. component construction code comes here
  }
}

简而言之,上面的代码创建了一个名为 FancyButton 的自定义组件,该组件仅通过继承 javax.swing.JComponent 类来标识自己。但是,您将无法运行该代码。原因如下:

  1. 您没有 main 方法;
  2. 您没有窗口来显示您的自定义组件。

现在让我们来做这件事。

列表 1.2
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”,这对于本教程来说足够了。但是,请注意一件事:我们还没有构建自定义组件,也没有将其放置在框架上。现在让我们来做。

清单 1.3
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.awtjavax.swing 包下的所有类都能被导入,而无需显式导入到程序代码中。为了清晰起见,我们还将截断 main(String[]) 方法,但您应该将其包含在您上一个示例的程序中。

清单 1.4
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 中得到正确重写。

Output-002.jpg

图 1.1:清单 1.4 的截图

因为我们的组件横跨了整个窗口框架,所以我们看到整个窗口框架变成了一整块红色。

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:构建您的组件设计清单

这听起来可能是一项枯燥的任务,但归根结底,没有比跟踪进度更好的方法了。最好的起点是创建一个简单的“是”和“否”清单。这个简单的清单将帮助我们确定哪些需要做,哪些可以忽略。

Component-Design-Checklist-001.jpg

图 1.2:简单的组件设计清单

最初尝试创建组件设计清单。这有助于将我的创造力限制在简单的“是”和“否”列表中。

根据我创建的清单来约束自己,我对清单 1.4 进行了以下修改:

清单 1.5
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(...),并将 arcWidtharcHeight 设置为 30 像素。这个简单的效果使按钮看起来更漂亮、更圆润。此外,我还通过更改 fillRoundRect(...) 方法中的值,为组件添加了 2 像素的边框。这将有助于我稍后可视化我清单中提到的边框设置。如果您看下面的截图,您会立即注意到区别。

Output-003.jpg

图 1.3:清单 1.5 的截图

此截图显示了在实现组件设计清单中的一些操作后,对初始代码所做的修改。但很快,您就会看到一些新的、即时出现的问题。其中一个问题是圆角的锯齿状显示。显然,发生这种情况是因为默认情况下,Java 2D 绘制例程不渲染抗锯齿输出。这应该很容易解决。更重要的是,这个问题直接添加到我的清单中:启用抗锯齿渲染。

在后续的修订中,我们将探讨如何解决这些个别问题,以创建一个美观的自定义 Swing UI 组件。敬请期待更多内容。

历史

  • 创建日期:2010 年 3 月 1 日。
© . All rights reserved.