内容


您不知道的 5 件事……

增强 Swing

是的,您(仍)可以使用 Swing 构建漂亮的用户界面!

Comments

系列内容:

此内容是该系列 14 部分中的第 # 部分: 您不知道的 5 件事……

敬请期待该系列的后续内容。

此内容是该系列的一部分:您不知道的 5 件事……

敬请期待该系列的后续内容。

用户界面设计和开发已发生了许多变化,Java™平台也已跟上这些变化。2008 年发布的 JavaFX 提供了一组新工具来设计和开发桌面和富互联网应用程序。这组工具旨在取代作为标准工具包而占据了 10 多年统治地位的 Swing,它们解决了 Swing 开发人员面临的许多问题并提供了更广泛的特性。

但是,Swing 依然存在,许多开发人员继续选择它作为首选的 GUI 开发工具包。它通过了时间的考验,表现稳定,并提供了广泛的开源组件来提供新特性。

在这期 5 件事系列中,我将介绍 4 个免费的开源组件,您可以使用它们实现您的 Swing GUI 现代化,我会在讨论的最后介绍您可能不知道的有关 Swing 线程的一些事实。

1.Substance

将 Java 应用程序与原生操作系统相集成可能很麻烦,主要因为 Swing 的组件是人工绘制的。一个解决方法是 Java 外观,后者允许 JVM 将应用程序的组件外观委托给原生外观。因此,当使用 Windows®外观时,Swing 应用程序看起来像是 Windows 应用程序;当使用 Mac 外观时,它们看起来像是 Mac 应用程序。

Swing 附带了标准的原生外观,以及它自己的独立于平台的外观,称为 Metal。另外,Substance 是 Kirill Grouchnikov 开发的一个开源项目,它提供了十几种可更换皮肤的外观。要试用 Substance,请从 Java.net 下载 Substance,然后:

  1. 将 substance.jar 文件添加到您的 CLASSPATH
  2. 将以下系统属性添加到您的一个应用程序的启动项:
    -Dswing.defaultlaf=org.jvnet.substance.skin.lookandfeelname
  3. 在第 2 步中使用 lookandfeelname 变量的地方,尝试使用任何以下值:
         SubstanceAutumnLookAndFeel
         SubstanceBusinessBlackSteelLookAndFeel
         SubstanceBusinessBlueSteelLookAndFeel
         SubstanceBusinessLookAndFeel
         SubstanceChallengerDeepLookAndFeel
         SubstanceCremeCoffeeLookAndFeel
         SubstanceCremeLookAndFeel
         SubstanceDustCoffeeLookAndFeel
         SubstanceDustLookAndFeel
         SubstanceEmeraldDuskLookAndFeel
         SubstanceMagmaLookAndFeel
         SubstanceMistAquaLookAndFeel
         SubstanceMistSilverLookAndFeel
         SubstanceModerateLookAndFeel
         SubstanceNebulaBrickWallLookAndFeel
         SubstanceNebulaLookAndFeel
         SubstanceOfficeBlue2007LookAndFeel
         SubstanceOfficeSilver2007LookAndFeel
         SubstanceRavenGraphiteGlassLookAndFeel
         SubstanceRavenGraphiteLookAndFeel
         SubstanceRavenLookAndFeel
         SubstanceSaharaLookAndFeel
         SubstanceTwilightLookAndFeel

图 1 显示了一个包含默认的 Metal 外观的 Java 应用程序,图 2 显示了包含 Substance Raven 外观的相同应用程序:

图 1.Java 平台的 Metal 外观
一个 GUI 的屏幕截图,它具有 Java 平台的默认 Metal 外观。
一个 GUI 的屏幕截图,它具有 Java 平台的默认 Metal 外观。
图 2.Substance 的 Raven 外观
相同 GUI 的屏幕截图,它具有 Substance Raven 外观。
相同 GUI 的屏幕截图,它具有 Substance Raven 外观。

2.SwingX

Swing 框架包含您需要的大多数标准控件,包括树、表、列表等。但是它缺少一些更加现代的控件,比如树表。SwingX 项目是 SwingLabs 的一部分,它提供了一个丰富的组件集,其中包含以下控件:

  • 表、树和列表的排序、过滤和突出显示
  • 查找/搜索
  • 自动完成
  • 登录/身份验证框架
  • 树表组件
  • 可折叠面板组件
  • 日期选择器组件
  • Tip-of-the-Day 组件

要试用 SwingX JAR,可从 SwingLabs 下载它并将它添加到您的 CLASSPATH,或者仅将以下依赖项添加到您的 Maven POM 文件:

    <dependency>
      <groupId>org.swinglabs</groupId>
      <artifactId>swingx</artifactId>
      <version>1.6</version>
    </dependency>

图 3 中的树表是 SwingX 组件的一个示例:

图 3.SwingX 树表组件
此 GUI 屏幕截图显示了 SwingX 树表组件。
此 GUI 屏幕截图显示了 SwingX 树表组件。

构建一个 SwingX 树表

使用 SwingX JXTreeTable 控件可以非常轻松地构建树表。将表中的每行想象为可能包含列数据,以及选择性地包含子节点。 SwingX 提供了一个名为 org.jdesktop.swingx.treetable.AbstractTreeTableModel 的模型类,可以扩展该类来提供此功能。清单 1 给出了一个树表模型实现的示例:

清单 1.MyTreeTableModel.java
package com.geekcap.swingx.treetable;

import java.util.ArrayList;
import java.util.List;

import org.jdesktop.swingx.treetable.AbstractTreeTableModel;

public class MyTreeTableModel extends AbstractTreeTableModel 
{
	private MyTreeNode myroot;
	
	public MyTreeTableModel()
	{
		myroot = new MyTreeNode( "root", "Root of the tree" );
		
		myroot.getChildren().add( new MyTreeNode( "Empty Child 1", 
		  "This is an empty child" ) );
		
		MyTreeNode subtree = new MyTreeNode( "Sub Tree", 
		  "This is a subtree (it has children)" );
		subtree.getChildren().add( new MyTreeNode( "EmptyChild 1, 1", 
		  "This is an empty child of a subtree" ) );
		subtree.getChildren().add( new MyTreeNode( "EmptyChild 1, 2", 
		  "This is an empty child of a subtree" ) );
		myroot.getChildren().add( subtree );
		
		myroot.getChildren().add( new MyTreeNode( "Empty Child 2", 
		  "This is an empty child" ) );
		
	}

	@Override
	public int getColumnCount() 
	{
		return 3;
	}
	
	@Override
	public String getColumnName( int column )
	{
		switch( column )
		{
		case 0: return "Name";
		case 1: return "Description";
		case 2: return "Number Of Children";
		default: return "Unknown";
		}
	}

	@Override
	public Object getValueAt( Object node, int column ) 
	{
		System.out.println( "getValueAt: " + node + ", " + column );
		MyTreeNode treenode = ( MyTreeNode )node;
		switch( column )
		{
		case 0: return treenode.getName();
		case 1: return treenode.getDescription();
		case 2: return treenode.getChildren().size();
		default: return "Unknown";
		}
	}

	@Override
	public Object getChild( Object node, int index ) 
	{
		MyTreeNode treenode = ( MyTreeNode )node;
		return treenode.getChildren().get( index );
	}

	@Override
	public int getChildCount( Object parent ) 
	{
		MyTreeNode treenode = ( MyTreeNode )parent;
		return treenode.getChildren().size();
	}

	@Override
	public int getIndexOfChild( Object parent, Object child ) 
	{
		MyTreeNode treenode = ( MyTreeNode )parent;
		for( int i=0; i>treenode.getChildren().size(); i++ )
		{
			if( treenode.getChildren().get( i ) == child )
			{
				return i;
			}
		}

		return 0;
	}
	
	 public boolean isLeaf( Object node )
	 {
		 MyTreeNode treenode = ( MyTreeNode )node;
		 if( treenode.getChildren().size() > 0 )
		 {
			 return false;
		 }
		 return true;
	 }
	 
	 @Override
	 public Object getRoot()
	 {
		 return myroot;
	 }
}

清单 2 给出了一个自定义树节点:

清单 2. MyTreeNode.java
class MyTreeNode
{
	private String name;
	private String description;
	private List<MyTreeNode> children = new ArrayList<MyTreeNode>();
	
	public MyTreeNode() 
	{
	}
	
	public MyTreeNode( String name, String description ) 
	{
		this.name = name;
		this.description = description;
	}
	
	public String getName() 
	{
		return name;
	}
	
	public void setName(String name) 
	{
		this.name = name;
	}
	
	public String getDescription() 
	{
		return description;
	}
	
	public void setDescription(String description) 
	{
		this.description = description;
	}
	
	public List<MyTreeNode> getChildren() 
	{
		return children;
	}
	
	public String toString()
	{
		return "MyTreeNode: " + name + ", " + description;
	}
}

如果想要使用这个树表模型,需要创建它的一个实例,然后将该实例传递给 JXTreeTable 构造函数,如下所示:

private MyTreeTableModel treeTableModel = new MyTreeTableModel();
private JXTreeTable treeTable = new JXTreeTable( treeTableModel );

现在可以将 treeTable 添加到任何 Swing 容器,比如 JPanelJFrame 的内容窗格。

3.RSyntaxTextArea

Swing 中缺少的另一个组件是具有语法突出显示功能的文本编辑器。如果您编写过 XML 文档,那么就应该知道能够以可视方式区分标记、属性、属性值和标记值会有多大帮助。FifeSoft 的开发人员构建了一组富组件,您可以在基于 Swing 的 Java 应用程序中使用它们,其中一个组件是 RSyntaxTextArea 组件。

RSyntaxTextArea 直接支持大部分编程语言,包括 C、C++、Perl、PHP 和 Java 语言,以及 HTML、JavaScript、XML,甚至还支持 SQL。

图 4 是显示 XML 文件的 RSyntaxTextArea 组件的屏幕截图:

图 4.显示 XML 文件的 RSyntaxTextArea
显示 XML 文件的 RSyntaxTextArea 组件的屏幕截图。
显示 XML 文件的 RSyntaxTextArea 组件的屏幕截图。

向 Swing 应用程序添加语法突出显示功能

首先,从 Sourceforge 下载 RSyntaxTextArea JAR 文件。如果使用的是 Maven,您可能想要使用下面这样的命令将它安装到本地存储库:

mvn install:install-file -DgroupId=com.fifesoft -DartifactId=rsyntaxtextarea
 -Dversion=1.0 -Dpackaging=jar -Dfile=/path/to/file

将该 JAR 文件添加到您的项目后,可以在您的应用程序中创建一个 RSyntaxTextArea 实例,如果想要滚动功能,可以将它添加到一个 RTestScrollPane 中,并调用 setSyntaxEditingStyle() 方法,将它传递给一个 SyntaxConstants。例如,清单 3 创建了一个将文本呈现为 XML 的可滚动 RSyntaxTextArea

清单 3.Swing 中的语法突出显示
RSyntaxTextArea text = new RSyntaxTextArea();
add( new RTextScrollPane( text ) );
text.setSyntaxEditingStyle( SyntaxConstants.SYNTAX_STYLE_XML );

4.Java Look-and-Feel Graphics Repository

Microsoft 值得表扬的一个地方是,它确保了 Windows 应用程序拥有一致的外观。如果您曾编写过一段时间的 Java Swing 应用程序,或许遇到过 Oracle 的 Java Look-and-Feel Graphics Repository。如果尚未遇到,那么您会喜欢上它。Java Look-and-Feel Graphics Repository 包含一组用于标准应用程序行为(比如 File->New 和 Edit->Copy)的图标,以及用于更难懂的命令的图标,这些命令包括媒体控件、浏览器导航功能和 Java 开发人员的编程操作。图 5 显示了在 Oracle 网站上捕获的图标的屏幕截图:

图 5.Java Look-And-Feel Graphics Repository 图标
显示 XML 文件的 RSyntaxTextArea
显示 XML 文件的 RSyntaxTextArea

Java Look-And-Feel Graphics Repository 仅仅提供预先构建的图形就已经很不错了,但它还提供了标准约定,在构建和命名菜单、工具栏和快捷键时会使用这些约定。例如,应该使用 Ctrl-C 快捷键实现一个复制函数,将它命名为 Copy,并提供一个 Copy 工具提示。在菜单内部时,复制函数的助记符应该是 CP 或在万不得已情况下使用的 Y

使用 Java Look-And-Feel Graphics Repository 图标

要试用图 5 中显示的一些预先构建的图形,可以从 Oracle 下载 Java Look-and-Feel Graphics Repository JAR,将它添加到您的 CLASSPATH。然后,您需要从该 JAR 文件加载这些图标作为资源。这些图标具有以下格式:

...
toolbarButtonGraphics/general/Copy16.gif
toolbarButtonGraphics/general/Copy24.gif
toolbarButtonGraphics/general/Cut16.gif
toolbarButtonGraphics/general/Cut24.gif
toolbarButtonGraphics/general/Delete16.gif
toolbarButtonGraphics/general/Delete24.gif
...

所有图标都包含在 toolbarButtonGraphics 目录中,按图 5 所示的类别进行分区。在本例中,我们将查看 General 类别中的复制、剪切和删除。名称中引用的“16”和“24”反映了图标的大小:16x16 或 24x24。可以采用以下方式为文件创建一个 ImageIcon

Class class = this.getClass();
String urlString = "/toolbarButtonGraphics/general/Cut16.gif"
URL url = class.getResource( urlString );
ImageIcon icon = new ImageIcon( url );

5.Swing 线程

启动本文中的示例时,您可能遇到过一些奇怪的运行时错误。如果是这样,您可能在 Swing 应用程序的线程上犯了一个常见错误。许多 Java 开发人员都不知道,应该在自己的线程中而不是在主执行线程中运行 Swing 应用程序。Swing 会忽略这方面的错误,但目前为止引入的许多组件不会忽略该错误。

为了帮助您在自己的线程中启动 Swing 应用程序,Java 平台提供了一个名为 SwingUtilities 的类,该类有一个 invokeLater() 方法,您应该使用此方法来启动 Swing 应用程序。清单 4 给出了使用 SwingUtilities.invokeLater() 方法启动 JXTreeTable 的代码:

清单 4.SwingXExample.java
package com.geekcap.swingx;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

import org.jdesktop.swingx.JXTreeTable;

import com.geekcap.swingx.treetable.MyTreeTableModel;

public class SwingXExample extends JFrame 
{
	private JTabbedPane tabs = new JTabbedPane();
	
	private MyTreeTableModel treeTableModel = new MyTreeTableModel();
	private JXTreeTable treeTable = new JXTreeTable( treeTableModel );
	
	public SwingXExample()
	{
		super( "SwingX Examples" );
		
		// Build the tree table panel
		JPanel treeTablePanel = new JPanel( new BorderLayout() );
		treeTablePanel.add( new JScrollPane( treeTable ) );
		tabs.addTab( "JXTreeTable", treeTablePanel );
		
		// Add the tabs to the JFrame
		add( tabs );
		
		setSize( 1024, 768 );
		Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
		setLocation( d.width / 2 - 512, d.height/2 - 384 );
		setVisible( true );
		setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
	}
	
	
	public static void main( String[] args )
	{
		AppStarter starter = new AppStarter( args );
		SwingUtilities.invokeLater( starter );
	}
}

class AppStarter extends Thread
{
	private String[] args;
	
	public AppStarter( String[] args )
	{
		this.args = args;
	}
	
	public void run()
	{
		SwingXExample example = new SwingXExample();
	}
}

该构造函数将 JFrame 的可视性设置为 true,在应用程序的主线程中运行时,Swing 不允许这么做。所以清单 4 创建了一个名为 AppStarter 的单独类,该类扩展 Thread 并创建 SwingXExample 类。main() 方法创建了 AppStarter 类的一个实例并将它传递给 SwingUtilities.invokeLater() 方法,以便正确启动该应用程序。试着养成以这种方式运行 Swing 应用程序的习惯 —这不仅是正确运行方式,而且如果不这么做,一些第三方组件可能无法运行。

结束语

Swing 是一个强大的库,它允许您在 Java 平台上构建用户界面,但它缺少一些您可能希望合并到应用程序中的现代组件。在本文中,我介绍了一些美化(和现代化)Swing 应用程序的技巧。Substance、SwingX 和 Java Look and Feel Graphics Repository 等开源项目使得在 Java 平台上构建富用户界面变得更容易。请参阅相关主题部分,以便进一步了解这些项目和 Swing 编程。


相关主题

  • IBM Bluemix 云平台上开发和部署您的下一个应用程序。
  • Swing 简介”(Michael Abernethy,developerWorks,2005 年 6 月):该教程分两部分,前半部分介绍了 Swing 库中的基本组件。
  • 您不知道的 5 件事...Maven”(Steven Haines,developerWorks,2010 年 9 月):Maven 是许多 Java 开发人员最喜欢的构建工具,但它也提供了一全套工具来管理应用程序生命周期 —这篇文章中剖析了其中一些工具。
  • Swing 线程和事件调度线程”(John Zukowski,JavaWorld,2007 年 8 月):从 Java 1.0 中的 Swing 的单线程事件模型追溯到它在 JavaBeans 组件模型和 AWT 中的起源,然后解释常用启动模式中的一个初始化错误并展示如何修复它。
  • Substance (Swing) Sightings 第 1 卷”(Kirill Grouchnikov,Pushing Pixels,2007 年 8 月):具有 Substance 的可换肤外观的应用程序的精选屏幕截图,由 Substance 创建者 Kirill Grouchnikov 汇编。
  • Java 参考指南:SwingX”(Steven Haines,InformIT,2009 年 11 月):进一步了解 SwingX 和使用它为 Java 应用程序 UI 构建树表。
  • 下载 Substance:扩大您可以在 Swing 应用程序中实现的外观种类。
  • 下载 RSyntaxTextArea:可用在基于 Swing 的 Java 应用程序中的众多组件之一,由 FifeSoft 开发。
  • 下载 Java Look-and-Feel Graphics Repository:一个专为实现 Java 外观而设计的工具栏按钮图形集合,由 Oracle 的 Java 软件人机接口小组开发和发布。
  • JavaFX:客户端技术这篇 Oracle 教程介绍了目前的客户端技术,包括 JavaFX、JavaFX Scene Builder 2、Swing 和 2D。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology
ArticleID=601510
ArticleTitle=您不知道的 5 件事……: 增强 Swing
publish-date=10172017